8051寄存器、指令集、伪指令和关键字详细介绍
作者 | 将狼才鲸 |
---|---|
创建日期 | 2022-04-09 |
修改日期 | 2024-0723 |
- Gitee文档源码地址:01_8051寄存器、指令集、伪指令和关键字介绍.md
- CSDN文章阅读地址:8051寄存器、指令集、伪指令和关键字介绍
一、概述
- C51是8位CPU。顾名思义,它的某个总线是8位,或者一些总线是8位的。实际上它的数据总线是8位的,每条CPU指令只能处理一个8位的数据,而它的外部地址总线是16位的,可以执行最大64KB的程序,也可以简单的理解为编译出来的可执行程序不能超过64KB(可以类比理解为在电脑上你只能下载安装64K以内的软件),这是C51的限制。
-
不想使用盗版Keil的话,可以尝试使用SDCC开源编译器,只是没有IDE,还需要自己编写Makefile进行编译。
C51 开源编译器SDCC学习笔记-安装
开源SDCC编译器(一)–基本介绍
还在用Keil做51单片机开发吗?快来试试开源的SDCC吧
SDCC下载地址
SDCC使用说明 -
如果你只是自己学习,也可以控制将程序编译在2k的范围内(c51总共也才只支持64k的代码容量),去keil官方下载软件试用。
Keil C51官方下载地址(评估版只能编译2k的代码) -
启动流程:
8051 MCU学习之分析单片机的启动过程 -
C51资源:
51单片机知识重点汇总一,干货分享,学习单片机必懂知识
51单片机CPU的基本构成及作用
mcs-51单片机CPU的内部结构及工作原理
51单片机CPU结构各部件的原理详细分析
第一章C51系列单片机的硬件结构
51单片机的内部硬件结构(CPU工作原理,储存器结构,51,52和89C51,89S51型号对比) -
典型芯片:AT89C51
二、寄存器和地址
-
地址分为ROM地址和RAM地址,最大16位64K。
-
RAM:
地址 | 大小 | 描述 | 备注 |
---|---|---|---|
0x00~0x2F | 32 | 工作寄存器地址 | 4组R0~R7寄存器 |
0x20~0x2F | 16 | 位寻址 | 为了让寄存器可直接位寻址,用SBIT设置 |
0x30~0x7F | 48 | 内部RAM | IRAM,可设置为堆栈 |
0x80~0xFF | 128 | 内部RAM和特殊寄存器共用 | IRAM、SFR |
0x0100~0xFFFF | 65280B | 外部RAM | RAM |
- ROM:
地址 | 大小 | 描述 | 备注 |
---|---|---|---|
0x0000~0x0FFF | 4KB | 内部ROM | IROM |
0x1000~0xFFFF | 60KB | 外部ROM | ROM |
- 寄存器介绍:
21个特殊功能寄存器(52系列是26个)不连续地分布在128个字节的SFR存储空间中,地址空间为80H-FFH,在这片SFR空间中,包含有128个位地址空间,地址也是80H-FFH,但只有83个有效位地址,可对11个特殊功能寄存器的某些位作位寻址操作(这里介绍一个技巧:其地址能被8整除的都可以位寻址)。
SFR被称为特殊功能寄存器,芯片厂商自定义外设的寄存器地址也都在这组地址里面。
C51单片机的寄存器 | ||
---|---|---|
符号 | 地址 | 功能介绍 |
R0~R7 | 0x00~0x2F | 工作寄存器地址 |
位寻址 | 0x20~0x2F | 为了让寄存器可直接位寻址 |
堆栈或内部RAM | 0x30~0x7F | |
P0 | 80H | P0口锁存器 |
SP | 81H | 堆栈指针 |
DPL | 82H | 数据地址指针(低8位) |
DPH | 83H | 数据地址指针(高8位) |
PCON | 87H | 电源控制寄存器 |
TCON | 88H | T0、T1定时器/计数器控制寄存器 |
TMOD | 89H | T0、T1定时器/计数器方式控制寄存器 |
TL0 | 8AH | 定时器/计数器0(低8位) |
TL1 | 8BH | 定时器/计数器0(高8位) |
TH0 | 8CH | 定时器/计数器1(低8位) |
TH1 | 8DH | 定时器/计数器1(高8位) |
P1 | 90H | P1口锁存器 |
SCON | 98H | 串行口控制寄存器 |
SBUF | 99H | 串行口锁存器 |
P2 | A0H | P2口锁存器 |
IE | A8H | 中断允许控制寄存器 |
P3 | B0H | P3口锁存器 |
IP | B8H | 中断优先级控制寄存器 |
PSW | D0H | 程序状态字 |
ACC | E0H | 累加器 |
B | F0H | B寄存器 |
51单片机寄存器功能一览表
C51/C52 特殊功能寄存器表
三、指令集
- 指令:
8位总共256个,每条指令的耗时占一个到多个指令周期,也就是CPU主频每跳动一次所消耗的时间 - 参考文档:
指令代码 | 指令长度 | 指令周期 | 助记符 | 操作对象 | 描述 | 举例 |
---|---|---|---|---|---|---|
00 | 1 | 1 | NOP | 无 | 延时1个指令周期的时间 | NOP ; 延时 |
01 | 2 | 2 | AJMP | addr11 | 短跳转,绝对转移,地址范围0xXX±0x000~07FF,转移范围为当前指令地址高5位相同的2K范围 | AJMP ADDR1 |
02 | 3 | 2 | LJMP | addr16 | 全域跳转,可以跳转到64K范围所有绝对地址,后面常用自定义的标号跳转 | LJMP ADDR2 |
03 | 1 | 1 | RR | A | 累加器循环右移,实际上也是除以2 | |
04 | 1 | 1 | INC | A | 累加器自增1,所有INC指令都不会产生借位 | 可用作循环处理 |
05 | 2 | 1 | INC | direct | 将给出地址当中的数据自增1(自增完后放回原处) | 同上 |
06 | 1 | 1 | INC | @R0 | 将R0中的数据当作地址,将这个地址中的数据自增1 | 同上 |
07 | 1 | 1 | INC | @R1 | 同上 | 同上 |
08 | 1 | 1 | INC | R0 | 将R0中的数据自增1 | 同上 |
09 | 1 | 1 | INC | R1 | 同上 | 同上 |
0A | 1 | 1 | INC | R2 | 同上 | 同上 |
0B | 1 | 1 | INC | R3 | 同上 | 同上 |
0C | 1 | 1 | INC | R4 | 同上 | 同上 |
0D | 1 | 1 | INC | R5 | 同上 | 同上 |
0E | 1 | 1 | INC | R6 | 同上 | 同上 |
0F | 1 | 1 | INC | R7 | 同上 | 同上 |
10 | 3 | 2 | JBC | bit, offset | 如果某个可位寻址的位地址值为1,则清零后跳转到offset这个地址继续执行,否则顺序执行下一条指令 | JBC BIT C |
11 | 2 | 2 | ACALL | addr11 | 短函数调用,函数中最后一条要是RET。绝对跳转,地址范围0xXX±0x000~07FF,跳转范围为当前指令地址高5位相同的2K范围,有参数的话子函数中还需要压栈和弹栈。调用完成后接着顺序执行本指令的下一条 | ACALL FUNC1 |
12 | 3 | 2 | LCALL | addr16 | 全域函数调用,函数中最后一条要是RET。地址范围0x0000~FFFF,有参数的话子函数中还需要压栈和弹栈。调用完成后接着顺序执行本指令的下一条 | LCALL FUNC2 |
13 | 1 | 1 | RRC | A | 带进位累加器循环右移,也就是除以2 | |
14 | 1 | 1 | DEC | A | 累加器自减1,所有DEC指令都不会产生借位 | 可用作循环处理 |
15 | 2 | 1 | DEC | direct | 将给出地址当中的数据自减1(自增完后放回原处) | 同上 |
16 | 1 | 1 | DEC | @R0 | 将R0中的数据当作地址,将这个地址中的数据自减1 | 同上 |
17 | 1 | 1 | DEC | @R1 | 同上 | 同上 |
18 | 1 | 1 | DEC | R0 | 将R0中的数据自减1 | 同上 |
19 | 1 | 1 | DEC | R1 | 同上 | 同上 |
1A | 1 | 1 | DEC | R2 | 同上 | 同上 |
1B | 1 | 1 | DEC | R3 | 同上 | 同上 |
1C | 1 | 1 | DEC | R4 | 同上 | 同上 |
1D | 1 | 1 | DEC | R5 | 同上 | 同上 |
1E | 1 | 1 | DEC | R6 | 同上 | 同上 |
1F | 1 | 1 | DEC | R7 | 同上 | 同上 |
20 | 3 | 2 | JB | bit, offset | 如果某个可位寻址的位地址值为1,则跳转到offset这个地址继续执行,否则顺序执行下一条指令,该bit不清零 | 实现if else switch |
21 | 2 | 2 | AJMP | addr11 | 重复1 | |
22 | 1 | 2 | RET | 从子函数中返回 | ||
23 | 1 | 1 | RL | A | 累加器循环左移,也相当于乘以2 | |
24 | 2 | 1 | ADD | A, #immed | 累加器和一个立即数(常数)相加,结果放回到A | ADD A, #080H |
25 | 2 | 1 | ADD | A, direct | 累加器和内存地址里的值相加,结果放回到A | |
26 | 1 | 1 | ADD | A, @R0 | 累加器和R0里面存的内存地址里面指向的值相加,结果放回到A | |
27 | 1 | 1 | ADD | A, @R1 | 同上 | |
28 | 1 | 1 | ADD | A, R0 | 累加器和R0里面的值相加,结果放回到A | ADD A, R0 |
29 | 1 | 1 | ADD | A, R1 | 同上 | |
2A | 1 | 1 | ADD | A, R2 | 同上 | |
2B | 1 | 1 | ADD | A, R3 | 同上 | |
2C | 1 | 1 | ADD | A, R4 | 同上 | |
2D | 1 | 1 | ADD | A, R5 | 同上 | |
2E | 1 | 1 | ADD | A, R6 | 同上 | |
2F | 1 | 1 | ADD | A, R7 | 同上 | |
30 | 3 | 2 | JNB | bit, offset | 如果直接寻址位为0则转移 | 实现if else switch |
31 | 2 | 2 | ACALL | addr11 | 重复2 | |
32 | 1 | 2 | RETI | 中断程序返回 | ||
33 | 1 | 1 | RLC | A | 带进位累加器循环左移,也就是乘以2 | |
34 | 2 | 1 | ADDC | A, #immed | 带进位求和 | |
35 | 2 | 1 | ADDC | A, direct | ||
36 | 1 | 1 | ADDC | A, @R0 | ||
37 | 1 | 1 | ADDC | A, @R1 | ||
38 | 1 | 1 | ADDC | A, R0 | ||
39 | 1 | 1 | ADDC | A, R1 | ||
3A | 1 | 1 | ADDC | A, R2 | ||
3B | 1 | 1 | ADDC | A, R3 | ||
3C | 1 | 1 | ADDC | A, R4 | ||
3D | 1 | 1 | ADDC | A, R5 | ||
3E | 1 | 1 | ADDC | A, R6 | ||
3F | 1 | 1 | ADDC | A, R7 | ||
40 | 2 | 2 | JC | offset | 如果CY进位为1 则跳转,进位不清零 | |
41 | 2 | 2 | AJMP | addr11 | 重复3 | |
42 | 2 | 1 | ORL | direct, A | 后面的与前面的相或,结果放到前面,*direct &= A | |
43 | 3 | 2 | ORL | direct, #immed | ||
44 | 2 | 1 | ORL | A, #immed | ||
45 | 2 | 1 | ORL | A, direct | ||
46 | 1 | 1 | ORL | A, @R0 | ||
47 | 1 | 1 | ORL | A, @R1 | ||
48 | 1 | 1 | ORL | A, R0 | ||
49 | 1 | 1 | ORL | A, R1 | ||
4A | 1 | 1 | ORL | A, R2 | ||
4B | 1 | 1 | ORL | A, R3 | ||
4C | 1 | 1 | ORL | A, R4 | ||
4D | 1 | 1 | ORL | A, R5 | ||
4E | 1 | 1 | ORL | A, R6 | ||
4F | 1 | 1 | ORL | A, R7 | ||
50 | 2 | 2 | JNC | offset | 如果CY进位为0 则转移 | |
51 | 2 | 2 | ACALL | addr11 | 重复4 | |
52 | 2 | 1 | ANL | direct, A | 累加器“与”到直接地址 | |
53 | 3 | 2 | ANL | direct, #immed | ||
54 | 2 | 1 | ANL | A, #immed | ||
55 | 2 | 1 | ANL | A, direct | ||
56 | 1 | 1 | ANL | A, @R0 | ||
57 | 1 | 1 | ANL | A, @R1 | ||
58 | 1 | 1 | ANL | A, R0 | ||
59 | 1 | 1 | ANL | A, R1 | ||
5A | 1 | 1 | ANL | A, R2 | ||
5B | 1 | 1 | ANL | A, R3 | ||
5C | 1 | 1 | ANL | A, R4 | ||
5D | 1 | 1 | ANL | A, R5 | ||
5E | 1 | 1 | ANL | A, R6 | ||
5F | 1 | 1 | ANL | A, R7 | ||
60 | 2 | 2 | JZ | offset | 累加器为0 则转移 | |
61 | 2 | 2 | AJMP | addr11 | 重复5 | |
62 | 2 | 1 | XRL | direct, A | 累加器“异或”到直接地址 | |
63 | 3 | 2 | XRL | direct, #immed | ||
64 | 2 | 1 | XRL | A, #immed | ||
65 | 2 | 1 | XRL | A, direct | ||
66 | 1 | 1 | XRL | A, @R0 | ||
67 | 1 | 1 | XRL | A, @R1 | ||
68 | 1 | 1 | XRL | A, R0 | ||
69 | 1 | 1 | XRL | A, R1 | ||
6A | 1 | 1 | XRL | A, R2 | ||
6B | 1 | 1 | XRL | A, R3 | ||
6C | 1 | 1 | XRL | A, R4 | ||
6D | 1 | 1 | XRL | A, R5 | ||
6E | 1 | 1 | XRL | A, R6 | ||
6F | 1 | 1 | XRL | A, R7 | ||
70 | 2 | 2 | JNZ | offset | 累加器为1 则转移 | |
71 | 2 | 2 | ACALL | addr11 | 重复6 | |
72 | 2 | 2 | ORL | C, bit | 直接寻址位“或”到进位位 | |
73 | 1 | 2 | JMP | @A+DPTR | 相对DPTR 的无条件间接转移 | |
74 | 2 | 1 | MOV | A, #immed | ||
75 | 3 | 2 | MOV | direct, #immed | ||
76 | 2 | 1 | MOV | @R0, #immed | ||
77 | 2 | 1 | MOV | @R1, #immed | ||
78 | 2 | 1 | MOV | R0, #immed | ||
79 | 2 | 1 | MOV | R1, #immed | ||
7A | 2 | 1 | MOV | R2, #immed | ||
7B | 2 | 1 | MOV | R3, #immed | ||
7C | 2 | 1 | MOV | R4, #immed | ||
7D | 2 | 1 | MOV | R5, #immed | ||
7E | 2 | 1 | MOV | R6, #immed | ||
7F | 2 | 1 | MOV | R7, #immed | ||
80 | 2 | 2 | SJMP | offset | 无条件相对转移 | |
81 | 2 | 2 | AJMP | addr11 | 重复7 | |
82 | 2 | 2 | ANL | C, bit | 直接寻址位“与”到进位位 | |
83 | 1 | 2 | MOVC | A, @A+PC | 代码字节传送到累加器 | |
84 | 1 | 4 | DIV | AB | 累加器除以B 寄存器 | |
85 | 3 | 2 | MOV | direct, direct | ||
86 | 2 | 2 | MOV | direct, @R0 | ||
87 | 2 | 2 | MOV | direct, @R1 | ||
88 | 2 | 2 | MOV | direct, R0 | ||
89 | 2 | 2 | MOV | direct, R1 | ||
8A | 2 | 2 | MOV | direct, R2 | ||
8B | 2 | 2 | MOV | direct, R3 | ||
8C | 2 | 2 | MOV | direct, R4 | ||
8D | 2 | 2 | MOV | direct, R5 | ||
8E | 2 | 2 | MOV | direct, R6 | ||
8F | 2 | 2 | MOV | direct, R7 | ||
90 | 3 | 2 | MOV | DPTR, #immed | 16 位常数加载到数据指针 | |
91 | 2 | 2 | ACALL | addr11 | 重复8 | |
92 | 2 | 2 | MOV | bit, C | 进位位位传送到直接寻址 | |
93 | 1 | 2 | MOVC | A, @A+DPTR | 代码字节传送到累加器 | |
94 | 2 | 1 | SUBB | A, #immed | 累加器减去立即数(带借位) | |
95 | 2 | 1 | SUBB | A, direct | ||
96 | 1 | 1 | SUBB | A, @R0 | ||
97 | 1 | 1 | SUBB | A, @R1 | ||
98 | 1 | 1 | SUBB | A, R0 | ||
99 | 1 | 1 | SUBB | A, R1 | ||
9A | 1 | 1 | SUBB | A, R2 | ||
9B | 1 | 1 | SUBB | A, R3 | ||
9C | 1 | 1 | SUBB | A, R4 | ||
9D | 1 | 1 | SUBB | A, R5 | ||
9E | 1 | 1 | SUBB | A, R6 | ||
9F | 1 | 1 | SUBB | A, R7 | ||
A0 | 2 | 2 | ORL | C, /bit | 直接寻址位的反码“或”到进位位 | |
A1 | 2 | 2 | AJMP | addr11 | 重复9 | |
A2 | 2 | 1 | MOV | C, bit | 直接寻址位传送到进位位 | |
A3 | 1 | 2 | INC | DPTR | 数据指针加1 | |
A4 | 1 | 4 | MUL | AB | 累加器和B 寄存器相乘 | |
A5 | reserved | |||||
A6 | 2 | 2 | MOV | @R0, direct | ||
A7 | 2 | 2 | MOV | @R1, direct | ||
A8 | 2 | 2 | MOV | R0, direct | ||
A9 | 2 | 2 | MOV | R1, direct | ||
AA | 2 | 2 | MOV | R2, direct | ||
AB | 2 | 2 | MOV | R3, direct | ||
AC | 2 | 2 | MOV | R4, direct | ||
AD | 2 | 2 | MOV | R5, direct | ||
AE | 2 | 2 | MOV | R6, direct | ||
AF | 2 | 2 | MOV | R7, direct | ||
B0 | 2 | 2 | ANL | C, /bit | 直接寻址位的反码“与”到进位位 | |
B1 | 2 | 2 | ACALL | addr11 | 重复10 | |
B2 | 2 | 1 | CPL | bit | 取反直接寻址位 | |
B3 | 1 | 1 | CPL | C | 取反进位位 | |
B4 | 3 | 2 | CJNE | A, #immed, offset | 比较直接地址和累加器,不相等转移 | |
B5 | 3 | 2 | CJNE | A, direct, offset | ||
B6 | 3 | 2 | CJNE | @R0, #immed, offset | ||
B7 | 3 | 2 | CJNE | @R1, #immed, offset | ||
B8 | 3 | 2 | CJNE | R0, #immed, offset | ||
B9 | 3 | 2 | CJNE | R1, #immed, offset | ||
BA | 3 | 2 | CJNE | R2, #immed, offset | ||
BB | 3 | 2 | CJNE | R3, #immed, offset | ||
BC | 3 | 2 | CJNE | R4, #immed, offset | ||
BD | 3 | 2 | CJNE | R5, #immed, offset | ||
BE | 3 | 2 | CJNE | R6, #immed, offset | ||
BF | 3 | 2 | CJNE | R7, #immed, offset | ||
C0 | 2 | 2 | PUSH | direct | 直接地址压入堆栈 | |
C1 | 2 | 2 | AJMP | addr11 | 重复11 | |
C2 | 2 | 1 | CLR | bit | 清直接寻址位 | |
C3 | 1 | 1 | CLR | C | 清进位位,和CLR CY是一样的效果 | |
C4 | 1 | 1 | SWAP | A | 累加器高、低4 位交换 | |
C5 | 2 | 1 | XCH | A, direct | 直接地址和累加器交换 | |
C6 | 1 | 1 | XCH | A, @R0 | ||
C7 | 1 | 1 | XCH | A, @R1 | ||
C8 | 1 | 1 | XCH | A, R0 | ||
C9 | 1 | 1 | XCH | A, R1 | ||
CA | 1 | 1 | XCH | A, R2 | ||
CB | 1 | 1 | XCH | A, R3 | ||
CC | 1 | 1 | XCH | A, R4 | ||
CD | 1 | 1 | XCH | A, R5 | ||
CE | 1 | 1 | XCH | A, R6 | ||
CF | 1 | 1 | XCH | A, R7 | ||
D0 | 2 | 2 | POP | direct | 直接地址弹出堆栈 | |
D1 | 2 | 2 | ACALL | addr11 | 重复11 | |
D2 | 2 | 1 | SETB | bit | 置位直接寻址位 | |
D3 | 1 | 1 | SETB | C | 置位进位位 | |
D4 | 1 | 1 | DA | A | 累加器十进制调整 | |
D5 | 3 | 2 | DJNZ | direct, offset | 直接地址减1,不为0 则转移 | |
D6 | 1 | 1 | XCHD | A, @R0 | 间接RAM 和累加器交换低4 位字节 | |
D7 | 1 | 1 | XCHD | A, @R1 | ||
D8 | 2 | 2 | DJNZ | R0, offset | 寄存器减1,不为0 则转移 | |
D9 | 2 | 2 | DJNZ | R1, offset | ||
DA | 2 | 2 | DJNZ | R2, offset | ||
DB | 2 | 2 | DJNZ | R3, offset | ||
DC | 2 | 2 | DJNZ | R4, offset | ||
DD | 2 | 2 | DJNZ | R5, offset | ||
DE | 2 | 2 | DJNZ | R6, offset | ||
DF | 2 | 2 | DJNZ | R7, offset | ||
E0 | 1 | 2 | MOVX | A, @DPTR | 外部RAM(16 地址)传送到累加器 | |
E1 | 2 | 2 | AJMP | addr11 | 重复12 | |
E2 | 1 | 2 | MOVX | A, @R0 | 外部RAM(8 地址)传送到累加器 | |
E3 | 1 | 2 | MOVX | A, @R1 | ||
E4 | 1 | 1 | CLR | A | 累加器清零 | |
E5 | 2 | 1 | MOV | A, direct | ||
E6 | 1 | 1 | MOV | A, @R0 | ||
E7 | 1 | 1 | MOV | A, @R1 | ||
E8 | 1 | 1 | MOV | A, R0 | ||
E9 | 1 | 1 | MOV | A, R1 | ||
EA | 1 | 1 | MOV | A, R2 | ||
EB | 1 | 1 | MOV | A, R3 | ||
EC | 1 | 1 | MOV | A, R4 | ||
ED | 1 | 1 | MOV | A, R5 | ||
EE | 1 | 1 | MOV | A, R6 | ||
EF | 1 | 1 | MOV | A, R7 | ||
F0 | 1 | 2 | MOVX | @DPTR, A | 累加器传送到外部RAM(16 地址) | |
F1 | 2 | 2 | ACALL | addr11 | 重复13 | |
F2 | 1 | 2 | MOVX | @R0, A | 累加器传送到外部RAM(8 地址) | |
F3 | 1 | 2 | MOVX | @R1, A | ||
F4 | 1 | 1 | CPL | A | 累加器求反 | |
F5 | 2 | 1 | MOV | direct, A | 累加器传送到直接地址 | |
F6 | 1 | 1 | MOV | @R0, A | 直接地址传送到间接RAM | |
F7 | 1 | 1 | MOV | @R1, A | ||
F8 | 1 | 1 | MOV | R0, A | ||
F9 | 1 | 1 | MOV | R1, A | ||
FA | 1 | 1 | MOV | R2, A | ||
FB | 1 | 1 | MOV | R3, A | ||
FC | 1 | 1 | MOV | R4, A | ||
FD | 1 | 1 | MOV | R5, A | ||
FE | 1 | 1 | MOV | R6, A | ||
FF | 1 | 1 | MOV | R7, A | ||
伪指令 | ORG | 汇编起始伪指令 | ||||
伪指令 | END | 结束伪指令 | ||||
伪指令 | DB | 字节数据定义伪指令 | ||||
伪指令 | DW | 字数据定义伪指令 | ||||
伪指令 | DS | 空间定义伪指令 | ||||
伪指令 | EQU | 赋值伪指令 | ||||
伪指令 | MACRO ENDM | 宏定义函数 | NOP2 MACRO \ NOP \ NOP \ ENDM 该举例为无参数宏定义函数 | 也可以带参数 | ||
伪指令 | BIT | 位地址符号定义伪指令 | ||||
伪指令 | DATA | 片内RAM直接字节地址定义伪指令 | ||||
伪指令 | XDATA | 片外RAM直接字节地址定义伪指令 | ||||
伪指令 | HIGH | 16位数的高字节 | #HIGH(65536) | |||
伪指令 | LOW | 16位数的低字节 | #LOW(65536) |
还有一些编译器自定义的符号,如Keil的伪指令:
$NOMOD51
$NOPRINT
NAME ; 给当前模块命令,同时也是一段代码的入口
SEGMENT ;类似于typedef
PUBLIC
?C_START ; main函数入口
IF
ELSE
ENDIF
标号:
CODE
IDATA
EXTRN
RSEG ; 段选择指令
CSEG
AT
$INCLUDE(USER.ASM)
C51 各个存储区说明
keil C51中各个地址的区别
51单片机片内RAM的128B(00H~FFH) 分为哪几部分各部分地址范围及功能?
51单片机特殊功能寄存器中的字节寻址和位寻址
C51 特殊功能寄存器SFR的名称和地址
C51最全111条汇编指令合集,以及使用时的注意事项,超详细
51单片机指令表
正确区分LJMP、AJMP、SJMP、JMP单片机跳转指令
51单片机的汇编指令中AJMP 和SJMP都是两个字节,都是两个机器周期,它们有什么区别呢?
谁能帮我解释一下 INC A ; INC direct INC Rn INC @Ri INC DPTR
MCS51单片机的伪指令有哪些?
keil_C51伪指令
四、扩展
-
Keil官网上显示支持98家公司的9500款芯片(截止到2022-09-29),其中一半基于ARM核,一半基于8051核,少量基于其它核。
-
点击Legacy Device List查看所有器件
-
参考网址
厂商列表 MDK5 Device List:https://www.keil.com/dd2/,里面有几十家公司
器件列表 Legacy Device List:https://www.keil.com/dd/ -
除了Keil,其它的8051模拟器还有:
emu8051:
https://github.com/jarikomppa/emu8051
https://www.cnblogs.com/jikexianfeng/p/6357529.html
EdSim51:
http://edsim51.com/
https://zhuanlan.zhihu.com/p/371060362
- Keil伪指令
-
Keil A51汇编代码中支持很多常用的伪指令,需要掌握,写汇编时经常会用到,这些伪指令可以在A51相关的英文文档里看到所有的描述;国内网站上没找到有人完整的翻译所有的伪指令,但是能在Keil官网上找到英文原版的,我没有仔细去翻找,但是应该在C51用户手册里面的一系列文档中的某些章节里面;Keil安装好的文件里的帮助文档的a51文档中能搜到所有的伪指令含义。
-
Keil汇编伪指令介绍详见同级目录下的文档[《Keil A51汇编伪指令》](./02_doc/Keil A51汇编伪指令.md)
- Keil 8051伪指令相关参考网址
Keil文档:https://developer.arm.com/documentation#
C51用户手册:https://www.keil.com/support/man_c51.htm
ARM用户手册:https://www.keil.com/support/man_arm.htm
A51 伪指令
keil+A51
Keil伪指令
keilA51汇编语言伪指令
RSEG用法和汇编问号的涵义
Keil伪指令
- Keil创建工程以及使用的参考网址如下:
51单片机实训(一)————Keil 基本操作
C8051单片机BootLoader心得
Keil C51软件的使用
第13章 Keil c51 和Proteus 虚拟仿真平台的使用 - 后面的用例中,我都会提供创建好的Keil工程和源码,可以直接使用;但是如果你想自己从头到尾创建一个工程的话,可以参考以下步骤:
- 已经用过Keil的可以自行下载Keil破解版并进行破解。
- 从官网下载Keil C51程序,这是一个IDE,集成了编辑器、编译器、链接器、模拟器。
- 下载地址 https://www.keil.com/demo/eval/c51.htm 需要注册并填写个人信息,评估版只支持编译2k容量的代码。
- 安装过程中,安装路径不要有空格,不要有中文目录。
- 安装完成后打开“Keil uVision5”软件,
点击“Project”–>“new uVision Project”–>选中你愿意放工程的目录–>
在选择设备弹窗中,我选择Cadence公司的R8051XC2(8DPTR),选择哪款8051芯片都无所谓,前期只操作基础的8051寄存器,不同公司的基础寄存器是一致的,我这里以这款芯片为例–>
点击下一步后,弹出“Copy ‘STARTUP.A51’ to Project Folder and Add File to Project?”,点是,这样编译器会自动给你填充一份Boot汇编代码模板,这个Boot代码能让你跳转到main函数。 - 点击左上角两个箭头的按钮,编译程序,下方“Build Output”区域提示
“.\Objects\cadence_first_project” - 0 Error(s), 2 Warning(s).
错误为零则代表编译通过,生成的可执行文件是没有后缀的Objects\cadence_first_project - 点击右上角红色的‘d’图标,能直接使用软件自带的模拟器仿真运行8051程序,
如果提示“EVALUATION MODE…2K”,表示未注册的评估版只支持2K代码,我前期的代码没有超过2K,直接点确定下一步–>
使用Keil默认提供的boot汇编程序,测试时断点会停在“?C_STARTUP”这一行–>
点击行号前面可以创建和取消断点–>
点击左上角几个带箭头或者叉叉的图标,可以单步执行、跳转到函数内部执行、持续运行、立即停止运行,快捷键是F10、F11、F5–>
进入调试模式后,Keil里有各种窗口看串口输出的信息、RAM里的数据值、当前各个变量和寄存器的值、所有寄存器的值,修改当前变量的值,让程序中的变量按自己手动输入的值生效并运行,如果程序跑飞了可以查看异常寄存器和地址寄存器存储的上一个语句的地址。
- 如果熟悉了这款芯片后,你不使用Keil自带的Boot程序,而是自己写整个寄存器宏定义,则在Keil工程配置里面,魔法棒图标–>Device–>勾选使用LX51和使用AX51,使用自己定义的SFR特殊功能寄存器。
- 如果你需要将程序下到板子里面去,甚至还需要将hex可执行文件转成容量更小的bin文件,则在Output页面中勾选"Create HEX File"。
- 在Keil使用过程中的一些技巧:
-
不使用Keil自带的GB2312中文编码,而是使用UTF-8中文编码,这样在用Git进行版本管理时能正常显示中文,在Linux和Windows之间来回切换工程后也不容易产生乱码,导致中文信息丢失无法恢复,步骤如下:
打开工程–>点击左上角“Edit”–>点击弹出菜单最下方Configuration–>
在弹出页面最左侧Editor页面中点击"Encoding"–>
从ANSI改为UTF-8,点击“OK”,这样可以输入中文。 -
Keil将Tab设置为固定4个空格(为了和Linux内核还有Git Tab以8个字节显示进行兼容,空格能让显示格式固定)
Configuration–>Editor–>C/C++ Files–>Tab size: 4
4.1、8051资源描述
- 为了直观,这里直接列出了8051的所有寄存器,而8051所有的汇编指令表格在这个寄存器表格后面以文章的链接给出来。
- 8051单片机数据存储器可划分为两大区域:00H~7FH为片内低128字节RAM区;80H~FFH为特殊功能寄存器区(SFR)。
- 我举例用的8051 IP核使用的是Cadence的r8051xc,相关的通用寄存器需要查看r8051xc相关的文档。
- 8051寄存器中地址以0和8结尾的都是可以位寻址的,如0x80 P0、0x88 TCON,而且8051中对每一个可位寻址的位都有一个对应的名字,直接操作这个名字就能操作这个位,具体的含义请在程序源码中看注释,或者需要时直接在网上搜索。
- 地址为00H~7FH的低128字节片内RAM区又可划分为三个区域:通用寄存器区地址(00H~1FH)、可位寻址区(20H~2FH)、用户RAM区(30H~7FH,堆栈也可以设置在这里)。
- 8051通用寄存器介绍,共128个:
地址 | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
---|---|---|---|---|---|---|---|---|
通用寄存器 | 0组R0 | 0组R1 | 0组R2 | 0组R3 | 0组R4 | 0组R5 | 0组R6 | 0组R7 |
地址 | 0x08 | 0x09 | 0x0A | 0x0B | 0x0C | 0x0D | 0x0E | 0x0F |
1组R0 | 1组R1 | 1组R2 | 1组R3 | 1组R4 | 1组R5 | 1组R6 | 1组R7 | |
地址 | 0x10 | 0x11 | 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 |
2组R0 | 2组R1 | 2组R2 | 2组R3 | 2组R4 | 2组R5 | 2组R6 | 2组R7 | |
地址 | 0x18 | 0x19 | 0x1A | 0x1B | 0x1C | 0x1D | 0x1E | 0x1F |
3组R0 | 3组R1 | 3组R2 | 3组R3 | 3组R4 | 3组R5 | 3组R6 | 3组R7 | |
地址 | 0x20 | 0x21 | 0x22 | 0x23 | 0x24 | 0x25 | 0x26 | 0x27 |
位地址 | 00H~06H | 07H~0FH | 10H~16H | 17H~1FH | 20H~26H | 27H~2FH | 30H~36H | 37H~3FH |
地址 | 0x28 | 0x29 | 0x2A | 0x2B | 0x2C | 0x2D | 0x2E | 0x2F |
40H~46H | 47H~4FH | 50H~56H | 57H~5FH | 60H~66H | 67H~6FH | 70H~76H | 77H~7FH | |
地址 | 0x30 | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 |
剩下都是 | 用户RAM | 一般开辟 | 成堆栈 | …… | ||||
地址 | 0x38 | 0x39 | 0x3A | 0x3B | 0x3C | 0x3D | 0x3E | 0x3F |
地址 | …… | …… | …… | …… | …… | …… | …… | …… |
地址 | 0x78 | 0x79 | 0x7A | 0x7B | 0x7C | 0x7D | 0x7E | 0x7F |
- R8051XC2特殊功能寄存器区(SFR)介绍,最大128个,8051通用的寄存器会加粗,未加粗的是R8051XC2特有的:
地址 | 0x80 | 0x81 | 0x82 | 0x83 | 0x84 | 0x85 | 0x86 | 0x87 |
---|---|---|---|---|---|---|---|---|
描述 | P0 IO口锁存器 | SP 堆栈指针 | DPL 数据地址低8位 | DPH 数据地址高8位 | WDTREL | PCON | ||
地址 | 0x88 | 0x89 | 0x8A | 0x8B | 0x8C | 0x8D | 0x8E | 0x8F |
TCON Timer控制 | TMOD Timer方式 | TL0 Timer0低8位 | TL1 Timer1低8位 | TH0 Timer0高8位 | TH1 Timer1高8位 | CKCON | ||
地址 | 0x90 | 0x91 | 0x92 | 0x93 | 0x94 | 0x95 | 0x96 | 0x97 |
P1 IO口锁存器 | DPS | DPC | PAGESEL | D_PAGESEL | ||||
地址 | 0x98 | 0x99 | 0x9A | 0x9B | 0x9C | 0x9D | 0x9E | 0x9F |
S0CON串口控制 | S0BUF串口锁存 | IEN2 | S1CON | S1BUF | S1RELL | |||
地址 | 0xA0 | 0xA1 | 0xA2 | 0xA3 | 0xA4 | 0xA5 | 0xA6 | 0xA7 |
P2 IO口锁存器 | DMAS0 | DMAS1 | DMAS2 | DMAT0 | DMAT1 | DMAT2 | ||
地址 | 0xA8 | 0xA9 | 0xAA | 0xAB | 0xAC | 0xAD | 0xAE | 0xAF |
IE0 中断允许 | IP0 | S0RELL | ||||||
地址 | 0xB0 | 0xB1 | 0xB2 | 0xB3 | 0xB4 | 0xB5 | 0xB6 | 0xB7 |
P3 IO口锁存器 | DMAC0 | DMAC1 | DMAC2 | DMASEL | DMAM0 | DMAM1 | ||
地址 | 0xB8 | 0xB9 | 0xBA | 0xBB | 0xBC | 0xBD | 0xBE | 0xBF |
IP 中断优先级 IEN1 | IP1 | S0RELH | S1RELH | IRCON2 | ||||
地址 | 0xC0 | 0xC1 | 0xC2 | 0xC3 | 0xC4 | 0xC5 | 0xC6 | 0xC7 |
IRCON | CCEN | CCL1 | CCH1 | CCL2 | CCH2 | CCL3 | CCH3 | |
地址 | 0xC8 | 0xC9 | 0xCA | 0xCB | 0xCC | 0xCD | 0xCE | 0xCF |
T2CON | CRCL | CRCH | TL2 | TH2 | RTCSEL | |||
地址 | 0xD0 | 0xD1 | 0xD2 | 0xD3 | 0xD4 | 0xD5 | 0xD6 | 0xD7 |
PSW 程序状态字 | IEN4 | I2C2DAT | I2C2ADR | I2C2CON | I2C2STA | SMB2_SEL | SMB2_DST | |
地址 | 0xD8 | 0xD9 | 0xDA | 0xDB | 0xDC | 0xDD | 0xDE | 0xDF |
ADCCON | P5 IO口 | I2CDAT | I2CADR | I2CCON | I2CSTA | SMB_SEL | SMB_DST | |
地址 | 0xE0 | 0xE1 | 0xE2 | 0xE3 | 0xE4 | 0xE5 | 0xE6 | 0xE7 |
ACC 累加器 | SPSTA | SPCON | SPDAT | SPSSN | P6 IO口 | |||
地址 | 0xE8 | 0xE9 | 0xEA | 0xEB | 0xEC | 0xED | 0xEE | 0xEF |
P4 IO口 | MD0 | MD1 | MD2 | MD3 | MD4 | MD5 | ARCON | |
地址 | 0xF0 | 0xF1 | 0xF2 | 0xF3 | 0xF4 | 0xF5 | 0xF6 | 0xF7 |
B 寄存器 | ||||||||
地址 | 0xF8 | 0xF9 | 0xFA | 0xFB | 0xFC | 0xFD | 0xFE | 0xFF |
-
8051的指令集和boot原理介绍,含指令集中完整256条指令的表格:
embedded-knowledge-wiki/ documents / 2.3.1.1_C51汇编介绍、boot程序编写.md - https://gitee.com/langcai1943/embedded-knowledge-wiki/blob/develop/documents/2.3.1.1_C51汇编介绍、boot程序编写.md -
其它8051资源描述的网页链接:
8051单片机内部RAM低128单元划分为哪三个部分?各有什么特点?
8051基础之三:数据存储类型
8051内部RAM位寻址区
8051系列单片机-
R8051XC2的寄存器描述在网上没搜到,但是创建完R8051XC2工程后,在Keil中进入debug模式后,View–>Symbols Windows里面能看到所有寄存器地址。
-
Keil调试时“Register”窗口有最简单的寄存器:r0~r7、a、b、sp、sp_max、PC $、dpspl、dptr07、dpc07、states、sec、psw(p、f1、ov、rs、f0、ac、cy)。
-
Keil调试时“Symbols”窗口中有Virtual Registers寄存器:PPAGE、XPAGE、XTAL、CLOCK、INTPINS、INT0PIN、INT1PIN、PORT03O、PORT03I、SO1IN、S01OUT、SO1TIME、T01PIN、SWD、I2S、SPI、RTC。
-
Keil调试时“Symbols”窗口中有Special Function Registers:SP、PSW、ACC、A、B、DPL、DPH、DPS、DPC、CKCON、PCON、IEN04、IP01、IRCON、IRCON2、P0P3、S01CON、SO1BUF、SO1RELL、SO1RELH、ADCON、IEN2、TCON、TMOD、TL01、TH01、T2CON、TL2、TH2、CRCL、CRCH、CCEN、CCL13、CCH13、WDTREL、MD05、ARCON、I2CDAT、I2CADR、I2CCON、I2CSTA、I2C2ADR、I2C2CON、I2C2STA、SPSTA、SPCON、SPDAT、SPSSN、DMAS02、DMAT02、DMAC0~2、DMASEL、DMAM0、DMAM2、SRST、RTCSEL、RTCDAT
-
-
参考网址:
r8051中文资料,r8051xc所有寄存器描述
8051单片机21个特殊功能寄存器和指令汇总
R8051XC 数据表(PDF)
ATT7035A_7037A_7037B用户手册 - 钜泉
Toolchain Extensions for R8051XC/R8051XC2 Core
特殊功能寄存器(SFR)详解 ——以8051单片机为例
实验二 8051单片机IO口输出操作实验
五、boot编写
- 如何写纯汇编程序
; $NOMOD51 ; 使A51不使用8051所有预定义的符号,使用自定义符号
; 不同的芯片厂商可以将SFR寄存器进行全新的定义
;==== SFR寄存器定义====
P0 DATA 80H ; P0 IO口
SP DATA 81H ; 堆栈指针
DPL DATA 82H ; 数据指针低字节
DPH DATA 83H ; 数据指针高字节
PCON DATA 87H ; 电源控制
TCON DATA 88H ; 定时器控制
TF1 BIT TCON.7
TR1 BIT TCON.6
TF0 BIT TCON.5
TR0 BIT TCON.4
IE1 BIT TCON.3
IT1 BIT TCON.2
IE0 BIT TCON.1
IT0 BIT TCON.0
TMOD DATA 89H ; 定时器方式
TL0 DATA 8AH ; 定时器0低字节
TH0 DATA 8CH ; 定时器0高字节
TL1 DATA 8BH ; 定时器1低字节
TH1 DATA 8DH ; 定时器1高字节
P1 DATA 90H ; P1 IO口
SCON0 DATA 98H ; UART0
TI0 BIT SCON0.1
RI0 BIT SCON0.0
SBUF0 DATA 99H ; 串口0数据
SCON1 DATA 9BH ; UART1 ; 芯片厂商自行添加的
SBUF1 DATA 9CH ; 串口1数据
P2 DATA 0A0H ; P2 IO口 ; 对于最高位大于等于10(ABCDEF)的数前面必须带0
IEN0 DATA 0A8H ; 中断使能
EA BIT IEN0.7
WDT BIT IEN0.6 ; 芯片厂商自行添加
EX1 BIT IEN0.2
EX0 BIT IEN0.0
P3 DATA 0B0H ; P3 IO口
T2CON DATA 0C8H ; 定时器2控制
TL2 DATA 0CCH ; 定时器2低字节
TH2 DATA 0CDH ; 定时器2高字节
PSW DATA 0D0H ; 程序状态寄存器
CY BIT PSW.7
AC BIT PSW.6
F0 BIT PSW.5
RS1 BIT PSW.4
RS0 BIT PSW.3
OV BIT PSW.2
F1 BIT PSW.1
P BIT PSW.0
ACC DATA 0E0H ; 累加器
B DATA 0F0H ; 寄存器B
EXADR DATA 0FEH ; SFR扩展接口 ; 支持更多的寄存器
EXDATA DATA 0FFH
; 赋值
APP_MODE EQU 0F8H ; 类似于宏定义
; 中断入口,程序入口(程序从0地址开始执行)
ORG 0000H
LJMP RESET
ORG 000BH ; 中断入口的地址都是固定的
LJMP T0INT
ORG 001BH
LJMP T1INT
ORG 002BH
LJMP T2INT
ORG 0100H ; 程序起始地址
$INCLUDE(USER.ASM)
RESET:
; 你的汇编代码,初始化各个模块,执行函数,响应中断,执行程序
END
- 如何写汇编boot程序,并引导到main()函数执行
;;;;
; 其它未写出的准备操作:
; 用DATA申明所有的SFR寄存器名字,P0(80H) ~ B(0F0H)
; 自定义的宏定义,如DEBUG_LEVEL EQU 01H,用于配置软件的不同功能
CSEG AT 0000H ; 板子复位后执行的第一条指令
LJMP STARTUP ; 执行初始化函数
CSEG AT 0003H ; 外部中断0
LJMP interupt_0 ; 依次注册好所有中断处理函数
;;;; 省略其它中断处理函数
; SEGMENT申明本模块在CODE代码段,CODE代码段起始地址是0x100,这也是程序默认运行的起始地址,前面的地址是一些固定的中断处理的函数地址
STARTUP_FUNC SEGMENT CODE AT 0100H ; 等同于ORG 0100H
RSEG STARTUP_FUNC ; 定义函数再定义段
PUBLIC STARTUP ; 申明函数,并向别的.asm暴露出函数接口
STARTUP: ; 标号,同时也是函数名,和C语言中标号类似,C语言的标号可以goto跳转
NOP ; 延时一个时钟周期
CLR EAL ; SBIT(EAL, IE, 7) ; 关闭中断7
CLR RS0 ; RS0 BIT PSW.3 ;
CLR RS1 ; RS0 BIT PSW.3 ; 和上条命令一起选择第一组R0~R7寄存器
MOV IE, #0H ; 关闭所有中断
NOP
MOV SP, #ORIGIN_SP ; ORIGIN_SP EQU 40H ; 初始化堆栈起始地址
NOP
LCALL _hardware_init ; 调用你写的函数写驱动模块寄存器初始化你需要的硬件,如引脚、PLL时钟倍频分频、JTAG设置、看门狗复位、IO输出、引脚复用、软件配置判断、内存初始化、串口、SPI、I2C等初始化
NOP
LCALL _crt0Startup ; 调用crt0.c里面的C语言函数,其实这时候已经可以直接调用main函数了,但是有些main函数之前的准备工作是用C写的,所以要提前调用一下
;;;;
; extern int main(int, char * const []);
; extern int sysExit(int exit_code);
; #define sysMain main
; int crt0Startup(int argc, char * const argv[])
; {
; // 关闭所有中断、DMA缓存刷新、CPU工作模式选择、硬件频率进一步设置、串口的完整初始化(设置波特率)、中断初始化、时钟初始化(更新当前实时时间)、有操作系统的话初始化task、内核、使能中断、调用main函数、main结束后进行资源销毁,便于软复位后系统能再次正常运行
; // sysMain(argc, argv); // 跳转到main函数执行
; // sysExit();
; }
NOP
LJMP _cpuStop ; 关闭PLL时钟分频倍频,将时钟设置为晶振的原始频率
NOP
RET
NOP
END
CSEG ; 绝对地址指示的代码段,可以当成一个函数的入口
RSEG ; 再定位段选择指令,它用来选择一个在前面已经定义过的再定位段作为当前段,例如先申明一个函数段,后面写这个函数段。PS: 程序代码放到代码段,数据对象放到数据段,段分两种,一是种绝对段,一种是再定位段。
SEGMENT ; 申明是哪种段,类似C语言的{}花括号,和END配和使用
AT ; 该段的起始地址
PUBLIC ; 给别的.asm文件暴露出函数接口,类似于C语言 int api_func(void);放在头文件中
$SAVE ; 存储最近的LIST和GEN的设置
$NOLIST ; 不使用最近的LIST配置
$RESTORE ; 恢复最近的LIST和GEN的设置
EXTRN CODE (YOUR_FUNCTION_NAME) ; EXTRN 是与PUBLIC 配套使用的,要调用其它模块的函数,就必须先在模块前声明
- 函数参数:
可用作函数参数的,及时压栈和弹栈的寄存器有ACC累加器、B寄存器(为乘法和除法指令而设置)、PSW程序状态字(处理进位、非零、正负、溢出等)、DPH/DPL(数据地址指针,读外部RAM数据)、R0~7(工作寄存器);函数调用时,让这些寄存器放弃它们本来的用法,当作函数参数使用。压栈和弹栈时顺序要刚好相反