指令是指挥微型计算机工作的的计算机命令,对于51单片机来说,其主要使用的指令有两种形式:机器语言指令和汇编语言指令。
机器语言指令是指使用二进制代码表示的指令;
汇编语言指令是指使用容易我们记忆的缩写符号表示的机器语言。
下面我使用的是使用汇编语言的机器指令。
目录
🐱指令组成
🐏伪指令
🐎寻址指令
🐕数据传输指令
🐘内部数据传输指令--MOV
🐟数据交换指令
🦀整字节交换--XCH
🦀半字节交换指令--XCHD
🦀高位与低位交换指令--SWAP
🐀栈指令
🐇入栈指令--PUSH
🐇出栈指令--POP
🐂片外数据传输指令--MOVX
🐎ROM访问指令--MOVC
🐅算术运算类指令
🌳加法指令--ADD
🌳带进位加法指令--ADDC
🌳带借位减法指令--SUBB
🌳自增、自减指令--INC和DEC
🐋逻辑运算指令
🐺逻辑与指令--ANL
🐺逻辑或指令--ORL
🐺逻辑异或指令--XRL
🐺累加器A的置位/取反指令--CLR和CPL
⏰移位指令
💡循环左移/右移指令--RL/RR
💡带进位左移/右移指令--RLC/RRC
🦊控制类转移指令
🦏无条件转移指令
🦅无条件长转移指令--LJMP
🦅绝对转移指令--AJMP
🦅短转移指令--SJMP
🦅散转指令--JMP
🦏有条件转移指令
🦅累加器判零转移指令--JZ/JNZ
🦅比较转移指令--CJNE
🦅减一跳转指令--DJNZ
🦏子程序调用、返回指令
🦅长调用指令--LCALL
🦅绝对调用指令--ACALL
🦅子程序返回指令--RET、中断服务程序返回指令--RETI
🦎位操作类指令
🐜位移动指令--MOV
🐜位状态设置指令--CLR / SETB
🐜位逻辑指令--ANL / ORL / CPL
🐜以Cy位为条件的位移动指令-- JC / JNC
🐜以位地址内容为条件的位移动指令-- JB / JNB /JBC
🐱指令组成
汇编语言的指令常见由两部分组成:操作码助记符和操作数;
汇编指令格式:
[标号:]操作码[目的操作数][,源操作数];[注释]
标号指该语句地址的标记符号(别名),是执行该语句的指令的的第一个字节的存放地址。
操作码一个特定的语句操作,常用助记符表示。
操作数是指令执行操作时的数据或者要操作的地址,多操作数之间用逗号隔开。
注释部分是增加阅读便利性的拓展部分。
其中中括号内部为可选内容,相邻的操作块之间要用空格、冒号、逗号或者分号隔开。
指令符号意义:
符号 | 意义 |
Ri、Rn | 当前工作寄存器区的工作寄存器 |
#data | 8位立即数(非地址数就是立即数) |
# data16 | 16位的立即数 |
rel | 相对地址、地址偏移量常用于SJMP, ret的取值位于-127-128之间 |
addr16 | 16位的目的地址 |
addr11 | 11位目的地址 |
direct | 存储区直接地址或者特殊功能寄存器地址 |
bit | 位地址 |
@ | 间接寻址,类似于C语言中的 取地址符号& |
(X) | 地址X中的内容,类似于C语言中的 *指针 |
((X)) | 地址(X)中的内容,类似于C语言 中的*(*地址) |
/ | 操作数取反 |
→ | 内容发送 |
🐏伪指令
在我们汇编程序中,有一些指令并不会有实际的操作意义,并且其也不会有对应的机器码,它的存在作用就是将帮助汇编程序转化为二进制码,那么有以上这些性质的指令就是伪指令。
在我们平常汇编中,常用到的伪指令就是ORG和END,它们分别主要用来指定程序的起始位置和结束位置。当然,还有一些伪指令也可以用来为中间保留运算结果等等。
例如起始程序就是:
ORG 16位地址
它表示的意思是ORG下面的程序的起始地址
END
它表示的意思是一个程序结束,类似于C语言中的return。它表示该指令执行后汇编结束,所以所有下面的指令都不会被执行。
🐎寻址指令
寻址指令是对地址和操作数进行连接的指令,它的功能是建立地址与地址、地址与操作数之间的联系。下面是一些常见的寻址方式
1、寄存器寻址
我们知道,寄存器内部可能会存着许多内容。而指定寄存器中的内容为操作数的寻址方式就称为就寄存器寻址。
2、直接寻址
直接寻址是指我们给出操作数的存储地址的寻址方式。
3、寄存器间接寻址
寄存器间接寻址是需要操作的操作数地址存储在空间B中,而空间B的地址存在于空间C中,我们通过空间C的地址来找到操作数的寻址方式称为寄存器间接寻址。
4、立即数寻址
立即数寻址是指操作单元的地址不是存在寄存器中,而是我们直接指定一个直接数作为操作数的地址的寻址方式。
5、变址寻址
变址寻址是在直接寻址的基础上由原来给定的操作数地址变成需要计算的两部分地址:操作数基地址+偏移量地址。
6、相对寻址
相对寻址是指在当前的PC值(PC上存储着下一条要执行的指令的地址)上加上给出的相对偏移量而作为新的地址。这个偏移量rel是一个有符号的数,其范围为00H - FFH,负数自然表示往前寻址,而正数就是往后寻址。
目的地址 = PC值 + rel ;
因为PC值是存储下一条要执行的指令的地址,所以假如我们现在在在执行转移指令,那么当前的PC值=当前转移指令的地址 + 转移指令的字节数
eg:JZ rel 是双字节转移指令,它的意义是当A为0则跳转。假设其地址为2055H,那么当前PC = 2055H + 2H = 2057H。
7、位寻址
位寻址用于进行单独操作一个位的指令,这个地址的操作数是一个比特位为不是一个字节。它的操作数地址也是一个数,不过它不是一个直接数而是一个没有“#”符号的数字,用以区分直接数寻址。
🐕数据传输指令
MCS-51单片机的指令系统中,数据的传输有许多指令,包括内部的数据间的传说、内部和外部的数据传输,堆栈指令等。数据传输一般并不会改变源操作数对应地址的的内容。除了用POP和MOV指令向PSW传输数据!
🐘内部数据传输指令--MOV
MOV指令用于内部数据在RAM单元之间的传输,一般的汇编语言指令格式为:
MOV [目的地址] , [源地址]
MOV指令的目的操作数可以是一个通用寄存器,也可以是一个寄存器的间接地址,也可也是一个直接的存储地址。
eg:以累加器A为目的操作数的传送指令:
MOV A , Rn ; 将工作寄存器的内容传输给累加器A
MOV A , direct; 将直接地址处存储的内容传输给累加器A
MOV A ,@Ri; 将寄存器Ri处存储的地址的内容传给累加器A
MOV A , #data; 将直接数据传输给累加器A
除了上面的累加器,我们还可以选择以直接地址、工作寄存器和寄存间接地址为目的操作数。
我们知道,DPTR寄存器设计的初衷是帮助我们和外接的ROM或者RAM进行寻址的,而我们都知道外接的ROM或者RAM都是16位的,所以我们需要一个能够传输16位数字方法。
而刚好我们也可以利用MOV来传输16位的数据,并且指令系统中也仅有这一条可以传输16位的指令。它的功能是将数据的高8位放到寄存器DPH中,而低8位放到寄存器DPL中。
例如执行指令:MOV DPTR, #1619H 后,DPL中存放的内容是:19H、DPH中存放的内容是:16H。
🐟数据交换指令
汇编中还提供了一个数据交换的指令,它可以允许我们直接将两个存储位置间的内容进行互换,而这种互换方式有整字节互换和半字节互换。而这些指令的源默认操作数都可以是累加器A、工作寄存器、直接地址和相对地址等。但是目标操作数是累加器A
🦀整字节交换--XCH
XCH [目的操作数] ,[源操作数]
例如下面是将直接数30H给累加器A,然后将直接数10H赋值给地址0x38H,最后再用XCH指令将它们存储数据进行交换,那么累加器A中就是10H,而0X38H中的地址就是30H
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV A ,#30H
MOV 38H, #10H
XCH A, 38H
END
执行前的内容:
执行后的结果:
🦀半字节交换指令--XCHD
XCHD [目的操作数],[源操作数]
与前面整字节交换指令不同,半字节交换指令只会交换地址内部存储的低4位内容,而不会交换高四位内容,即执行完后源操作数地址内的数据0xmn与累加器A内的数据0xqw会交换变成源操作数的数据是0xmw而累加器A的数据是0xqn。
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV A ,#37H
MOV 12H, #0F1H
MOV @R1, #12H
XCHD A, @R1
END
这个操作指令的源操作数只能是工作寄存器,并且操作方式只能是寄存器间接寻址。
例如上面的程序中,程序完成之后累加器A中的值是32H,而@R1中的值是17H。
🦀高位与低位交换指令--SWAP
SWAP [目的操作数]
SWAP1指令是将目的操作数中的8位数值的前4位与后四位进行交换,例如下面的指令中一开始A是#32H,交换完之后A就变成了#23H
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV A, #32H
SWAP A
END
🐀栈指令
栈是一种存储的数据结构,它遵从数据先进后出的存储方式,该数据结构广泛用于各种计算机存储中。
🐇入栈指令--PUSH
PUSH [目的操作数]
入栈也称压栈,也就是我们将数据放入该数据结构的过程,这个过程我们用的是push指令,该指令也只有一个操作数,那就是一个直接地址。执行该指令后表示的意义有两个:
①栈顶指针往后移动一格;
②目的操作数内存储的数据放入栈指针当前指向的位置。
🐇出栈指令--POP
POP [目的操作数]
POP是出栈指令,也就是将数据从栈中取出来的指令。执行该指令的时候表示的意义也有两个:
①将出栈的数据放入到目的操作数的地址中;
②将SP栈顶指针往后移动一个单位。
下面所示是一个例子,表示将地址为40H处的数字22H入栈(默认栈顶为07H),然后再将数据出栈并且放入到41H这个地方,可以看出,下面的效果就等同于MOV 41H, 40H。
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV 40H, #22H
PUSH 40H
POP 41H
END
🐂片外数据传输指令--MOVX
当我们需要将数据和外部的数据存储器RAM或者与外部的IO进行传输时,我们就需要用到这个指令。这个指令和我们之前遇到的MOV指令类似。
这里要注意一点,MCS-51单片机片内与片外之间的数据传输必须要用寄存器间接寻址的寻址方式和使用累加器A作为操作数。而外部的存储器是16位的,所以我们需要用到一个能存放16位地址的寄存器,毫无疑问就是DPTR。与外部的RAM进行数据传送,如果地址小于256B则可以用工作寄存器间接访问,而大于256B则用DPTR间接访问,我们可以用@DPTR作为操作数;但是我们还可以利用工作寄存器@Rn作为操作数,只不过工作寄存器一次只能访问低8位的空间。一般数据和外部进行传输我们用P0和P2口,地址低八位由P0口输出,地址高8位由P2口输出,最后再分时将数据通过P0口传输。
🐎ROM访问指令--MOVC
MOVC A, @A+DPTR
MOVC A, @A+PC
ROM访问指令,也被称为是查表指令,程序存储器ROM向累加器传送时数据操作数采用变址的寻址方式,输出数据时就把程序存储器中的数据放入累加器A中,总之该指令就是一个需要用到累加器A的指令。
而将数据读入到A中我们有两种方式,分别是使用DPTR和使用PC,对比一下两者的优劣势:
首先是DPTR的读取方式,可以看出该指令的寻址范围与该指令当前的存储位置无关,只与DPTR和累加器A的值有关,所以我们可以在64KB范围内任意寻址。
其次是PC的读取方式,该读取方式会与当前的地址有关,因为累加器A是8位的,所以最多只能在该地址后面的256个单位内寻址(2^8)即限定了寻址范围。但是它又有优势,因为它不会改变特殊功能寄存器和PC的状态。
🐅算术运算类指令
算数运算是最基本的数据变换,而MCS-51中算术运算类指令共有24条,它们不仅包括了我们常见的加减乘除等四则运算,还有一些常见的逻辑运算。
🌳加法指令--ADD
ADD [目的操作数],[源操作数]
在MCS-51中的加法指令是ADD,也同时是英文中的加法。它的目的操作数是累加器A,而源操作数的寻址方式可以是直接数寻址、直接地址寻址、寄存器间接寻址和寄存器寻址。
ADD计算后得到的结果会被放入累加器A中,计算的结果状态也会被存储到累加器A中,而进行加法运算的可以说计算机中任意一个单元的无符号数或者有符号数。计算的状态在PSW寄存器里面,其中PSW中的常见几个标志位就是存储了这些数据。
PSW中的几个常见的标志位就是Cy、AC、OV和D7。
--Cy:当两个数字相加后的最高位要向前进位,那么此位置会被置1;
--AC:当D3位也就是前4个比特位的数字相加向前进位,那么此位将会被置1,此位也被称为半进位;
--OV:当D7进位但是D6没有进位(负数溢出),或者D7没有进位但是D6进位了(正数溢出),也置1,此标志位用来判断有符号数是否溢出。判断方式就是看符号位是否与操作数相同,因为有符号数溢出两操作数肯定是同号。
--P:奇偶校验标志,如果有奇数个1则为1,反之则为0。
当无符号数溢出,对应着最高位向前面进位,所以Cy会被置以1;如果是有符号数溢出,因为有符号数最大是7位,最高为符号位,所以其就是对应着D7进位但是D6不进位或者D7不进位D6进位。而关于溢出的处理方式,无符号直接把Cy位也算上就可以,而有符号数则需要分类讨论,具体不细讲。
🌳带进位加法指令--ADDC
ADDC [目的操作数], [源操作数]
ADDC是带有进位的加法指令,它和之前的加法指令不同的是它自带一个进位标志,也就是说如果我们想要进行超8比特的多字节运算,我们就需要使用这个指令。而它的操作数和之前ADD一样。源操作数的寻址方式可以是寄存器直接、寄存器间接、直接数和直接地址等寻址方式。
它的执行方式是: ( A ) <-- ( A ) + val + Cy
也就是它会在原来ADD的执行方式基础上后面加上一个Cy位,也就是表示后面低8位的进位标志。
但是我们要怎么用呢?下面来看一个例子:
我们需要将一个16比特的数字和另外一个16比特的数字相加,并且将16位的结果存储起来。我们假设两个16位数字分别是加数1:#1234和加数2:#ABCD。首先我们设置几个地址分别存储这些内容,因为一个地址只能存储8位数字,所以我们需要6个地址,分别存储加数1、加数2的低八位和高八位以及结果的低八位和高八位。我们就设在数据存储器的#50H~#55H吧。程序和最后的执行结果如下:
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV 50H, #34H
MOV 51H, #12H
MOV 52H, #0CDH
MOV 53H, #0ABH
MOV A, 50H
ADD A, 52H
MOV 54H, A
MOV A, 51H
ADDC A, 53H
MOV 55H, A
END
结果:
🌳带借位减法指令--SUBB
SUBB [目的操作数], [源操作数]
对于减法来说的话,常用的就是这个减法指令了。它和ADDC有相似之处。首先它们的源操作数寻址都可而言是常用的寻址方式,而目的操作数是累加器A。
并且使用SUBB的时候需要同时用到PSW状态寄存器,当D7有借位则需要将Cy位置1,当D3有借位时,需要将AC位置1,当D6、D7不同时借位则OV=1,同时还有奇偶校验位P这些都不多说了,和前面几乎一样的用法。
但是需要注意减法是只有SUBB指令的,并且其使用到了Cy位,所以如果我们进行无进位的减法(类似于ADDC转换为ADD,因为残留的Cy值可以能会有影响)时,我们需要先将Cy位清零也就是CLR C。
🌳自增、自减指令--INC和DEC
自增自减作为我们常用的加法,对此汇编中也有相对应的指令INC和DEC。
INC/DEC [目的操作数]
我们要想操作某个地址的内容自增/自减,我们只需要在指令后面加上存放改内容的地址即可,寻址方式可以是几乎是我们熟悉的寻址方式。它的功能是将地址内的内容加一后放回去。它们的操作如果溢出不会影响Cy位,也就是说说它们如果溢出循环回去。
但是如果作用于16位的地址,那么其自增的只会是低八位地址的内容。
🐋逻辑运算指令
逻辑运算和移位指令有24条,包括逻辑与、逻辑或、逻辑非、逻辑异或等等。
🐺逻辑与指令--ANL
ANL [目的操作数], [源操作数]
与前面的运算类型指令不同,逻辑与指令的操作目的操作数可以是直接地址或者累加器A等,而源操作数的寻址方式则不定。该指令的最常用方法就是清除某个字节的内容,或者选择性地保留一个字节的某些位(保留位与1相与,非保留位与0相与即可)又或者是将一个字节内的某些位修改置0。
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV A, #0FEH
MOV 40H, #00H
ANL 40H, A
🐺逻辑或指令--ORL
ORL [目的操作数], [源操作数]
逻辑或指令则是ORL,注意不是ORG。它的操作数和操作数寻址方式和前面ANL一样,所以不多赘述。它的功能是用于修改某个字节内特定位的内容而不去改变该字节的其它位的内容(一般变化内容是置1,不变位与0相或)。
🐺逻辑异或指令--XRL
XRL [目的操作数], [源操作数]
逻辑异或的功能是当两个位相同,则置为0,相异则置为1。它的操作数和操作数寻址功能不多说。然后它的用处很灵活,需要程序员自身开发。例如一个很巧妙的清零方法就是自身与自身异或。
🐺累加器A的置位/取反指令--CLR和CPL
因为我们经常要用到累加器A,所以自然而然汇编中也会有针对于累加器A的一些常用指令,例如对累加器A的所有位清零则用CLR,而对累加器A内容进行取反则用CPL,它们均为单字节指令。
CLR/CPL A
只不过后者是双字节指令,需要花费更多的存储空间和执行时间。
⏰移位指令
我们之前介绍的内容运算指令几乎都是以字节为操作对象,接下来我们来看一下以一个位为操作对象的指令。
💡循环左移/右移指令--RL/RR
RL/RR [目的操作数]
首先该指令的操作目的操作数一般都是累加器A,然后我们需要知道一个字节的单元里面有8个比特位数据,它们均匀排布,现在我们需要进行一个操作,那就是将该字节里面的所有比特位的内容往右移一位:
首先将所有内容往右移动一位,那么第八位的0就会多余出来,而第一位就会空缺,那么循环移动指令就会将第8位多出来的数字放到第1位空缺的地方,这就是循环移动指令,它可以用作输出引脚的一轮性往复变化。
💡带进位左移/右移指令--RLC/RRC
RLC/RRC [目的操作数]
虽然RLC和RRC的目的操作数和之前的指令一样,但是其移动端本质不同。首先是我们需要用到PSW状态寄存器里面的Cy位。每次当我们移动一个字节里的比特位时,多余出来的内容会被放入到Cy位里面,而那个空缺的内容会被Cy位原来的内容填充!也就是多引入了一个位置。
⭐算术左移:所谓的算术左移就是将数字整体往左移动一位,但是这样势必会造成最左边的首位溢出,而循环的方法是将其放入最后面的空缺位;带进位的方法是将其放入Cy位;但是算术左移却会将其舍弃而在空缺位引入一个0!
🦊控制类转移指令
控制类转移指令有点类似于C语言中的goto,而汇编里面的控制类转移指令有17条,它们又分为有条件和无条件转移指令,有了这些指令我们就可以随意跳转到子程序、循环、分支里面。又或者是跳转到前面、后面执行过的或者未执行的程序。
🦏无条件转移指令
🦅无条件长转移指令--LJMP
LJMP [直接地址]
无条件转移指令是指到了该位置不会进行任何判断而直接跳转到目标位置,其中LJMP又被称为长转移指令,其是一个三字节指令,它的转移范围是16位直接地址。例如如果我们想要前往地址#3000H,那么我们只需要写LJMP 3000H。当程序执行到该位置时,就会自动跳转到3000H的地址处。
🦅绝对转移指令--AJMP
AJMP [目的地址]
AJMP是双字节指令,所以它的机器代码是16位代码,其中它有11位用来存储直接地址,有5位用来存储指令特有的操作码:
而它的跳转地址计算方式如下:
当前PC值 = 当执行存储AJMP指令的地址内容时的PC值 + 2(指令字节数)
目的地址 = 当前PC值高五位 + 指令中目的地址中的11位地址;
由于PC当前值高五位是固定的,所以目标地址的范围是2^11 +2 ^8 = 2^11 * 1B = 2KB。所以目标地址的范围只能是PC值2KB大小内。
举个例子:
例如现在AJMP 0050H的指令存放在 0035H 处,那么当我们执行AJMP这个绝对转移指令时,当前PC是0035H + 2 = 0037H,化为16进制为0000 0000 0011 0111;而指令中的目的的地址化成16进制为 0000 0000 0011 0101。所以目的地址为 00000(PC高五位)+ 000 0011 0101(指令后11位)最后等于0000 0000 0011 0101。
🦅短转移指令--SJMP
SJMP [偏移量]
SJMP后面跟偏移量,所以它的寻址方式为相对寻址,该偏移量rel是一个有符号数,它的范围是-128-+127。也就是说SJMP这是另一个版本但是略微不同的AJMP。当rel<0时,为向上寻址,当rel>0时为向下寻址。
目的地址计算公式为:
目的地址 = PC + 2 + rel
看到这条公式,不知道你们会不会想到如果偏移量rel=-2会怎样?其实当rel为-2时,新的地址就是PC,那么就会实现一个无限的死循环!下一条指令一直是自己!此时的rel = FEH(-2的补码),因为我们可能有时候会直接用一个死循环,所以为了简化引入了SJMP $ 来替代 SJMP FEH!
🦅散转指令--JMP
JMP [@A+DPTR]
该指令执行时,将累加器A中断数和DPTR寄存器中的16位数相加,将结果放入PC中跳转到目的地址,并且不会修改A和DPTR的数值。
🦏有条件转移指令
有条件转移是当满足某些条件时才会发生跳转动作,这个判断条件可以是某个值或者某条指令的执行结果。
🦅累加器判零转移指令--JZ/JNZ
JZ / JNZ rel
JZ和JNZ都是通过判断累加器内的值来执行跳转的,类似于SJMP,JZ和JNZ都是操作偏移量进行地址跳转,只不过JZ和JNZ的判断结果方式不同:
①JZ rel :当累加器A的数值为0,则PC = PC + 2 + rel;
②JNZ rel :当累加器A的数值不为0,则PC = PC + 2 +rel;
举个书上很有作用的例子:
目的:将外部RAM的数据包传送到内部RAM,外部RAM的数据起始地址为data1,内部RAM的数据起始地址为data2。
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV R0,#DATA1 ;将外部地址存放于R0寄存器
MOV R1,#DATA2 ;将内部存储地址存放于R1寄存器
LOOP:
MOVX A, @DATA1 ;循环中的一次读取并放入累加器A
CHECK:
JZ CHECK ;判断是否取到0,如果是则PC = PC + 2 跳出,否则继续往下执行
MOV @R1,A ;将A的内容放到内部寄存器中的地址里面
INC R1 ;地址自增
INC R0 ;地址自增
SJMP LOOP ;继续一次循环
🦅比较转移指令--CJNE
CJNE [目的操作数], [源操作数], rel
CJNE是先将两个操作数进行对比,根据结果来决定是否跳转,不相等则跳转!,并且根据目的操作数是否大于源操作数还会改变Cy位(目的操作数>源操作数:Cy=0;目的操作数<源操作数:Cy=1)它也是进行增量相加,它的常见目的操作数和源操作数如下:
CJNE A, #data, rel:累加器和立即数比较;
CJNE A, direct, rel:累加器和直接地址内容比较;
CJNE @Ri, #data, rel:间址内容与立即数比较;
CJNE Rn, #data, rel:工作寄存器内容与立即数比较;
下面是一个例子:它表示当P1口的电位为0000 0001的时候才执行下面那四个INC,否则将一直在WAIT里面循环!
ORG 00H
AJMP MAIN
ORG 30H
MAIN:
MOV A, P1
WAIT:
CJNE A, #01H, WAIT
INC A
INC A
INC A
INC A
END
🦅减一跳转指令--DJNZ
DJNZ [操作数], rel
DJNZ指令第一个参数是操作数,也就是我们要进行自减的内容的地址,然后它的跳转量也是一个偏移量,也就是当前PC是原来PC的基础上加二 / 三再加偏移量,而二还是三则根据不同的DJNZ指令:
①DJNZ direct, rel;它表示先将direct直接地址的内容自减,然后再根据direct里面的内容是否为0来赋值当前PC。如果为0,则PC = PC + 3;反之PC = PC +3 +rel
②DJNZ Rn, rel;这条指令和之前的判断内容大致相似,只不过是跳转的时候PC的偏移量是2而不是3,也就表示这是一个双字节指令,前面是三字节指令。
该指令是吧自减功能和条件转移结合,因为每次执行的时候操作数的内容都会减1。当操作数的内容自减为0,那么就会跳转,这里的rel偏移量范围是以该指令当前值为中心的-128~+127范围内。
🦏子程序调用、返回指令
主程序中如果我们执行某个语句后,接下来我们选择不按顺序执行下面的语句,而是进入到另外一个程序去执行,那么我们就需要将该程序设计为一个子程序。进入该子程序需要调用指令,而出去该子程序则需要返回指令。
🦅长调用指令--LCALL
LCALL [addr16]
LCALL是一个三字节长调用指令,调用它的时候,它将会首先将当前的PC值放入堆栈区中,然后再去将目的跳转子程序的16位地址传给PC,这样做的目的是为了保护现场,还能够回来后继续按顺序执行。因为它跳转的地址范围是16位,所以自然而然它能够调用2^8 * 2^16 = 2^24 = 64KB大小范围的子程序。
🦅绝对调用指令--ACALL
ACALL [addr11]
ACALL是一个两字节绝对跳转指令,它也会做将当前PC值压栈保护现场的动作,不同的是它的目的跳转子程序地址是一个11位,这也就代表着它只能够跳转到大小为2KB范围内的子程序。
举个应用例子:
假如当前的堆栈指针是SP = 40H,子程序首地址为3000H,名称为FUNC;当前PC为3200H,并且3200H的内容是 LCALL FUNC。
那么执行的过程如下:先会将当前PC的下一条指令的地址(PC = PC + 3 = 3203H)入栈,入站的过程分为两步:首先是SP指针递增指向地址31H,入栈PC的低八位03H;然后是SP指针再递增指向32H,入栈PC的高八位32H。这样一来PC就入栈成功了。接下来就是将FUNC的地址3000H传给PC,PC = 3000H后就跳转到FUNC子程序中执行。
🦅子程序返回指令--RET、中断服务程序返回指令--RETI
返回指令是能够返回到我们调用的地方,将压入栈的PC重新出栈。返回指令必须放到整个子程序的末尾,因为一旦执行就会返回到之前的断点处。返回指令一般就是两条:子程序用的返回指令--RET和我们设置的中断服务程序执行完后恢复子程序时调用的指令RETI。
🦎位操作类指令
位操作类指令的操作目标是一个比特位的数值,它的常用领域有PSW寄存器、设置中断类寄存器、定时器、IO口等地方的单个比特位的状态。
而比特位的表示方法有很多:
①点的表示形式,例如PSW.0、PSW.2就分别代表该寄存器下的0位和2位
②位的名称,如CLR RS1就代表清空PSW寄存器里面RS1位置的数值
③位地址,这就只能用于有位地址这个寻址方式的RAM中了
🐜位移动指令--MOV
MOV C, [bit]
与前面我们利用MOV来进行整个8位的移动类似,如果我们想将一个比特赋予到某一个位中,我们也可以利用MOV指令。但是这里要值得注意的是使用MOV一定需要有一个操作数是标志位Cy,并且这里的bit的意义是RAM中20H-2FH这128个可进行位寻址的地址以及特殊功能寄存器中可以进行位寻址的寄存器的位。
🐜位状态设置指令--CLR / SETB
CLR / SETB [bit]
我们可以针对一个位进行位操作,又因为一个位中放的不是0就是1,所以对应着的位状态设置的指令就有两个CLR和SETB。其中CLR表示将改为设置为0,而SETB表示将该位设置为1。他们的操作数可以是Cy位或者是其它的位地址。
🐜位逻辑指令--ANL / ORL / CPL
ANL / ORL / CPL [bit] ( [bit] )
前面我们使用这几个指令的时候的操作数是全部整8个位,而现在我们的操作数变成了一个位,但是这些指令本身包含的意义都没有变,ANL依旧表示与、ORL表示或而CPL表示非。
🐜以Cy位为条件的位移动指令-- JC / JNC
JC / JNC [rel]
类似于我们条件跳转指令,这个是位条件跳转的双字节指令,它是通过判断Cy位的状态来实现是否跳转的,其中JC表示当CY位为1的时候则跳转,否则顺序执行;而JNC表示当Cy位为0的时候则跳转,否则顺序执行。
🐜以位地址内容为条件的位移动指令-- JB / JNB /JBC
JB/ JNB/ JBC [bit], [rel]
JB、JNB和JBC这三个指令都是三字节指令,我们首先来看看各个指令的意义:
JB表示当bit位地址里面是1的时候则跳转,否则顺序执行;
JNB表示当bit位地址里面是0的时候则跳转,否则顺序执行;
JBC表示当bit位地址里面是1的时候则跳转,并且将bit位里面的内容修改为0,否则顺序执行。
上文为总结我参考、总结老师上课ppt后自己的学习笔记,部分图片用的是老师的ppt上的图