51单片机共有111条指令。可以大致分为以下五类:
(1)数据传送类(29条)
(2)算术操作类(24条)
(3)逻辑运算类(25条)
(4)控制转移类(17条)
(5)位操作类(17条)
可用的指令是固定的、每种指令对应一个物理电路的实际运行过程,如果指令是指令集中不存在的,电路内部无法实现,指令就会出错。
数据传送类指令
一般形式为 MOV <目的操作数> , <源操作数>
操作码包括MOV、MOVC、MOVX、PUSH、POP、XCH、XCHD、SWAP。
(1)首先是MOV。MOV是数据传送中使用的最多的一条操作码。MOV指令用于内部RAM之间数据的传送。具体内容见下表:
指令 | 作用 |
---|---|
MOV A,Rn | 工作寄存器的内容送A,n取0-7 |
MOV A,direct | 将内部RAM中地址为direct的单元的内容送A |
MOV A,@Ri | 利用R0和R1实现间接寻址,i只能取0或1 |
MOV A,#data | 立即数寻址,将立即数data送到累加器A中 |
MOV Rn,A | 将累加器A的内容送给工作寄存器 |
MOV Rn,direct | 直接寻址,内容送工作寄存器 |
MOV Rn,#data | 立即数寻址,将data送给工作寄存器,目的地址为Rn时无间接寻址 |
MOV direct,A | A送地址为direct的RAM单元 |
MOV direct,Rn | Rn送RAM |
MOV direct,dir2 | RAM送RAM |
MOV direct,@Ri | 寄存器间接寻址送RAM |
MOV direct,#data | 立即数送RAM |
MOV @Ri,A | A送间接寻址地址 |
MOV @Ri,direct | RAM内容送工作寄存器间接寻址地址 |
MOV @Ri,data | 立即数送寄存器间接寻址地址 |
MOV DPTR,#data16 | 16位立即数送DPTR |
需要注意的是,间接寻址只针对工作寄存器R0和R1,其他寄存器无法完成该工作。此外,direct所指代的可以是16进制的编码地址,也可以是由助记符所表示的内部RAM地址,比如DPH、DPL、SP、P0、P1等等。在实际使用的时候,我们一般不会使用16进制字符作为direct,因为这样写的程序可读性差,这些地址被称为magic number。
(2)MOVC指令用于程序存储器与累加器A的内容传输,多用于查表。MOVC用法十分固定,只有下面两条指令。MOVC在使用时是访问内部程序存储器还是外部程序存储器由PSEN引脚决定。
指令 | 作用 |
---|---|
MOVC A,@A+DPTR | DPTR基址加变址寻址,将程序存储器中的内容存在累加器A中 |
MOVC A,@A+PC | PC基址加变址寻址,将程序存储器中的内容存在累加器A中,PC保存的是下一条指令的首地址(程序存储器编址)。 |
需要注意的时,MOVC的用法是完全固定的,不可以创造指令,初学容易使用类似MOVC A,@DPTR这样的指令。
(3)MOVX指令用于对外部RAM进行访问。外部RAM编址由0开始,故必须区分MOV、MOVX才能避免冲突。
指令 | 作用 |
---|---|
MOVX A,@Ri | Ri间接寻址,将外部RAM内容写到A中 |
MOVX A,@DPTR | DPTR间接寻址,将外部RAM内容写到A中 |
MOVX @Ri,A | Ri间接寻址,将A内容写到外部RAM中 |
MOVX @DPTR,A | DPTR间接寻址,将A内容写到外部RAM中 |
可以看到,外部RAM只能通过间接寻址的方式进行访问。由于51单片机支持最大64K的外部RAM扩展,所以必须使用16位的DPTR来完成所有地址空间的访问,在使用工作寄存器Ri对外部RAM进行访问时,Ri只能充当低八位地址,高八位地址,也就是P2口的输出需要人为给定。
(4)PUSH和POP指令。压栈和弹栈指令。
指令 | 作用 |
---|---|
PUSH direct | 直接寻址,将内容保存在SP所指向的地址单元,SP+1 |
POP direct | 直接寻址,将SP所指向的地址单元内容弹到新地址,SP-1 |
其实压栈和弹栈指令的本质就是将SP作为间接寻址寄存器,和内部RAM单元进行内容传输。只不过SP会自动增加或减少以在逻辑上维护这个一个平平无奇的内部RAM空间并将其称为栈。
(5)XCH、XCHD、SWAP等交换指令
指令 | 作用 |
---|---|
XCH A,Rn | 寄存器和A内容交换 |
XCH A,direct | A和内部RAM内容交换 |
XCH A,@Ri | 间接寻址,内容交换 |
XCHD A,@Ri | 间接寻址,只交换低4位 |
SWAP A | A的高四位和低四位交换 |
算数操作类指令
操作码有ADD、ADDC、SUBB、INC、DEC、MUL、DIV、DA
具体内容见如下表格
指令 | 作用 |
---|---|
ADD A,Rn | 寄存器内容加到累加器 |
ADD A,direct | 直接寻址内容加到累加器 |
ADD A,@Ri | 寄存器间接寻址内容加到累加器 |
ADD A,#data | 立即数加到累加器 |
ADDC A,Rn | 寄存器内容加到累加器(考虑进位位Cy) |
ADDC A,direct | 直接寻址内容加到累加器(考虑进位位Cy) |
ADDC A,@Ri | 寄存器间接寻址内容加到累加器(考虑进位位Cy) |
ADDC A,#data | 立即数加到累加器(考虑进位位Cy) |
SUBB A,Rn | 累加器内容减寄存器内容(考虑Cy)A-(Rn)-Cy |
SUBB A,direct | 累加器减直接寻址内容(考虑Cy)A-(direct)-Cy |
SUBB A,@Ri | 累加器减寄存器间接寻址内容(考虑Cy) |
SUBB A,#data | 累加器减立即数(考虑Cy) |
INC A | 累加器加1 |
INC Rn | 工作寄存器加1 |
INC direct | 直接寻址地址内容加1 |
INC @Ri | 寄存器间接寻址地址内容加1 |
DEC A | 累加器减1 |
DEC Rn | 工作寄存器减1 |
DEC direct | 直接寻址地址内容减1 |
DEC @Ri | 寄存器间接寻址地址内容减1 |
INC DPTR | DPTR加1,实际就是DPL+1并自动向DPH进位 |
MUL AB | A内容乘寄存器B内容保存到A中 |
DIV AB | A内容除寄存器B内容保存到A中 |
DA A | 累加器十进制调整 |
几点说明:
(1)单片机在进行运算操作时会影响程序状态寄存器PSW中的程序状态标志位。
P标志位只考虑A中1的奇偶个数,所以,只要对A的内容进行修改均会影响P的内容。
Cy和Ac仅在加减操作时会被影响:
当运算发生进位或借位时,Cy置1;否则清0;
当低四位向高四位发生进位或借位时,Ac置1,否则清0;
加减乘除均影响OV位,OV实际保存的是第六位与第七位进位异或的结果。也就是说,只有当第六位和第七位只有一个发生进位或借位时,OV才会置1,否则清0。
(2)十进制调整
这是一个特殊的操作,前提是我们将4位2进制数看作是一个10进制数。这样的话1010-1111是无效的。
举个例子,A=53H,R1=63H。当我们将这两个数用BCD编码表示为十进制数时,其值就是53和63。当我们进行加运算的时候,我们希望A中的结果应该是16H,而Cy保存的是1,这样我们好像就完成了一个十进制的加法运算,但在执行ADD指令时,53H和63H是被当作16进制数进行计算的,运算结果也是以16进制数的格式保存在A中,为了解决直观性的问题,我们可以利用DA指令处理运算后累加器A中的内容从而将其变为16H。
逻辑运算指令
指令 | 作用 |
---|---|
CLR A | 清零A |
CPL A | 按位取反,不影响标志位 |
RL A | 循环左移 |
RLC A | 带进位的循环左移,Cy作为第九位 |
RR A | 循环右移 |
RRC A | 带进位的循环右移 |
ANL A,Rn | 逻辑按位与,保存到A |
ANL A,direct | 逻辑按位与,保存到A |
ANL A,#data | 逻辑按位与,保存到A |
ANL A,@Ri | 逻辑按位与,保存到A |
ANL direct,A | 逻辑按位与,保存到direct |
ANL direct,#data | 逻辑按位与,保存到direct |
ORL A,Rn | 逻辑按位或,保存到A |
ORL A,direct | 逻辑按位或,保存到A |
ORL A,#data | 逻辑按位或,保存到A |
ORL A,@Ri | 逻辑按位或,保存到A |
ORL direct,A | 逻辑按位或,保存到direct |
ORL direct,#data | 逻辑按位或,保存到direct |
XRL A,Rn | 逻辑按位异或,保存到A |
XRL A,direct | 逻辑按位异或,保存到A |
XRL A,#data | 逻辑按位异或,保存到A |
XRL A,@Ri | 逻辑按位异或,保存到A |
XRL direct,A | 逻辑按位异或,保存到direct |
XRL direct,#data | 逻辑按位异或,保存到direct |
这块比较简单,没什么好说的,一般我们可以通过移位操作实现对数据的倍乘。
控制转移类指令
用于控制程序的走向,实际大多是修改PC指针的值来完成的。我们首先列出所有的控制转移类指令,就几个难点讨论一下。
指令 | 作用 |
---|---|
AJMP addr11 | 2k空间内的无条件绝对跳转指令 |
SJMP rel | rel是一个补码,该条指令是一个无条件相对跳转指令 |
LJMP addr16 | 64k空间无条件绝对跳转指令 |
JMP @A+DPTR | 基质加变址绝对跳转指令 |
JZ rel | 判断累加器A的值,为0则执行相对跳转 |
JNZ rel | 判断累加器A的值,非0则执行相对跳转 |
CJNE A,direct,rel | 不相等相对跳转指令,A大于direct空间的值则置位Cy |
CJNE A,#data,rel | 不相等相对跳转指令,A大于#data则置位Cy |
CJNE Rn,#data,rel | 不相等相对跳转指令,Rn的值大于#data则置位Cy |
CJNE @Ri,#data,rel | 不相等相对跳转指令,Ri间接寻址的值大于#data则置位Cy |
DJNZ Rn,rel | 减1不为0相对跳转指令,Rn执行完后会减1 |
DJNZ direct,rel | 减1不为0相对跳转指令,direct单元内容会减1 |
ACALL addr11 | 与AJMP类似,不过会将PC压入堆栈 |
LCALL addr16 | 长调用指令,PC+3后压入堆栈,实际就是将这条指令的下一条指令的地址压入了堆栈,先低字节后高字节,SP+2 |
RET | SP-2,堆栈的内容弹到PC中,先高后低 |
RETI | 与RET差不多,用在中断服务程序中,会清除中断优先触发器 |
NOP | 空操作,PC+1,无其他操作 |
几点说明:
1、AJMP指令。首先它是一个2字节的指令,机器代码格式如下:
高字节的高三位和低字节的8位组成一个11位的绝对地址。在执行该语句时,由于这是一个2字节指令,所以PC+2,随后将这11位地址保存到PC的低11位上实现绝对寻址。从逻辑上可以看出,这条指令实际将64K的程序空间划分为32个2K空间,只要PC+2不向PC的高5位发生进位,程序空间就会被限制在一个2K的子空间内。ACALL同理。
2、SJMP指令。这是一个无条件相对跳转指令。相对体现在他不是至今对PC进行赋值操作,而是对PC进行运算操作。且rel应该被当作一个有符号数来进行计算。执行这条指令后PC的值为PC+2+rel,rel是一个有符号数。但是在实际写代码时,rel常常写作一个标号类似Loop、Main之类的,这个时候我们可以把SJMP看成是一个绝对跳转指令,因为汇编软件帮我们完成了相对地址逆运算的过程。这部分可以套用到所有相对跳转指令中去
3、相对跳转指令PC的值一定要记得加上跳转指令本身的字节长度,这个是随着指令不同而变化的。
4、关于RET和RETI的详细区别:[RET和RETI]((162条消息) 51中ret和reti的区别_astrotycoon的博客-CSDN博客_汇编语言reti)
位操作指令
主要对PSW中的进位标志位进行操作。
指令具体如下:
指令 | 作用 |
---|---|
MOV C,bit | 将bit位的内容送到进位标志位C中 |
MOV bit,C | 将进位标志位C中的内容送到bit位 |
CLR C | 清零C |
CLR bit | 清零一个bit位 |
CPL C | 取反C |
CPL bit | 取反一个bit位 |
SETB C | 置1C |
SETB bit | 置1一个bit位 |
ANL C,bit | 直接寻址位与C与,结果保存在C中 |
ANL C,/bit | 直接寻址位的反与C与,结果保存在C中不影响bit的值 |
ORL C,bit | 直接寻址位与或,结果保存在C中 |
ORL C,bit | 直接寻址位的反与C或,结果保存在C中不影响bit的值 |
JC rel | C为1则跳转 |
JNC rel | C为0则跳转 |
JB bit,rel | bit为1则跳转 |
JNB bit,rel | bit为0则跳转 |
JBC bit,rel | bit为1则跳转,并将bit清零 |
几点说明:
上面提到的bit为支持位寻址的存储空间,主要包括字节地址20H-2FH的RAM单元以及低四位地址为0或8的SFR。
C不能简单的看成是一个简单的位地址,但也可以通过位地址索引到C。
这句话的意思是C本身是一个特殊的关键字,而不可以看成是一个位地址的助记符号,否则MOV C,bit会与MOV direct,dorect2命令冲突。而我们可以通过C的位地址索引到C,比如我们可以执行 JBC 0D7H(C),rel来对C进行跳转判断清零操作。
伪指令一览
伪指令是控制汇编软件的关键字,实际不会写到程序存储器中的一些工具指令。