成就更好的自己
目录
3.4.4 控制转移类指令
1.长转移指令
LJMP addr16
指令执行时,把转移的目的地址,即指令的第二和第三字节分别装入PC的高位和低位字节中,无条件地转向addr16指定的目的地址:64KB程序存储器地址空间的任何位置。
2.相对转移指令
SJMP rel
无条件转移,rel为相对偏移量,是一单字节的带符号8位二进制补码数,因此程序转移是双向的。rel如为正,向地址增大的方向转移;rel如为负,向地址减小的方向转移。
执行时,在PC加2(本指令为2B)之后,把指令的有符号的偏移量rel加到PC上,并计算出目的地址。
编程时,只需写上目的地址标号, 相对偏移量由汇编程序自动计算。例如:
LOOP: MOV A,R6
……
SJMP LOOP
……
汇编时,跳到LOOP处的偏移量由汇编程序自动计算和填入。
3.绝对转移指令
AJMP addr11
指令双字节,格式如下:
指令提供11位地址A10~A0(即addr11),其中A10~A8则位于第1字节的高3位,A7~A0在第2字节。操作码只占第1字节的低5位。
指令构造转移目的地址:执行本指令,PC加2,然后把指令中的11位无符号整数地址addr11(A10~A0)送入PC.10~PC.0,PC.15~PC.11保持不变,形成新的16位转移目的地址。
需注意,目标地址必须与AJMP指令的下一条指令首地址的高5位地址码A15~A11相同,否则将混乱。所以,是2KB范围内的无条件跳转指令。
4.间接跳转指令
JMP @A+DPTR
单字节转移指令,目的地址由A中8位无符号数与DPTR的16位无符号数内容之和来确定。以DPTR内容为基址,A的内容作为变址。给A赋予不同值,即可实现多分支转移。
5.条件转移指令
执行指令时,如条件满足,则转移;不满足,则顺序执行下一指令。转移目的地址在以下一条指令首地址为中心的256B范围内(-128~+127)。
JZ rel ;如果累加器内容为0,则执行转移
JNZ rel ;如果累加器内容非0,则执行转移
6.比较不相等转移指令
CJNE A,direct,rel
CJNE A,#data,rel
CJNE Rn,#data,rel
CJNE @Ri,#data,rel
比较前两个操作数大小,如果值不相等则转移,并转向目的地址。
如果第一操作数(无符号整数)小于第二操作数(无符号整数),则进位标志位Cy置1,否则Cy清0。该指令的执行不影响任何一个操作数的内容。
7.减1不为0转移指令
把减1与条件转移两种功能合在一起。两条:
DJNZ Rn,rel ;n =0~7
DJNZ direct,rel
用于控制程序循环。预先装入循环次数,以减1后是否为“0”作为转移条件,即实现按次数控制循环。
8.调用子程序指令
(1)长调用指令
LCALL addr16
可调用64KB范围内程序存储器中的任何一个子程序。执行时,先把PC加3获得下一条指令的地址(断点地址),并压入堆栈(先低位字节,后高位字节),堆栈指针加2。
接着把指令的第二和第三字节(A15~A8,A7~A0)分别装入PC的高位和低位字节中,然后从PC指定的地址开始执行程序。执行后不影响任何标志位。
(2)绝对调用指令
ACALL addr11
与AJMP指令类似,为兼容MCS–48的CALL指令而设,不影响标志位。格式如下:
2KB范围内的调用子程序的指令。子程序地址必须与ACALL指令下一条指令的16位首地址中的高5位地址相同,否则将混乱。
9.子程序的返回指令
RET
执行本指令时
(SP)→PCH,然后(SP)-1→SP
(SP)→PCL,然后(SP)-1→SP
功能: 从堆栈中退出PC的高8位和低8位字节,把栈指针减2,从PC值处开始继续执行程序。不影响任何标志位。
10.中断返回指令
RETI
与RET指令相似,不同处:该指令清除了中断响应时被置1的内部中断优先级寄存器的中断优先级状态,其他相同。
11.空操作指令
NOP
不进行任何操作,耗一个机器周期时间,执行(PC)+1→PC操作
3.4.5 位操作类指令
内部有一个位处理机,对应位操作指令。
1.数据位传送指令
MOV C,bit
MOV bit,C
把源操作数指定的位变量送到目的操作数指定处。一个操作数必须为进位标志,另一个可以是任何直接寻址位。不影响其他寄存器或标志位。
例如: MOV C,06H ;(20H).6→Cy
06H是位地址,20H是内部RAM字节地址。06H是内部RAM 20H字节位6的位地址。
MOV P1.0,C ;Cy→P1.0
2.位变量修改指令
CLR C ;Cy位清0
CLR bit ;bit位清0
CPL C ;Cy位求反
CPL bit ;bit位求反
SETB C ;Cy位置1
SETB bit ;bit位置1
这组指令将操作数指定的位清0、求反、置1,不影响其他标志位。例如:
CLR C ;Cy位清0
CLR 27H ;0→(24H).7位
CPL 08H ; →(21H).0位
SETB P1.7 ; P1.7位置1
3.位变量逻辑与指令
ANL C,bit ;bit∧Cy→Cy
ANL C,/bit ; ∧Cy→Cy
第2条指令先对直接寻址位求反,然后与进位标志位C进行“逻辑与”运算,结果送回到位累加器中。
4.位变量逻辑或指令
ORL C,bit
ORL C,/bit
第1条指令是直接寻址位与进位标志位Cy(位累加器)进行“逻辑或”运算,结果送回到进位标志位中。
第2条指令先对直接寻址位求反,然后与位累加器(进位标志位)进行“逻辑或”运算,结果送回到进位标志位中。
5.条件转移类指令
JC rel ;如进位标志位Cy=1,则转移
JNC rel ;如进位标志位Cy=0,则转移
JB bit,rel ;如直接寻址位=1,则转移
JNB bit,rel ;如直接寻址位=0,则转移
JBC bit,rel ;如直接寻址位=1,转移,并把寻址位
;清0
3.5 AT89S51指令汇总
表3-2列出了按功能排列的全部的指令助记符及功能简要说明,以及指令长度、执行时间和指令代码(机器代码)。
由于指令条数多,读者不宜死记硬背,应在程序的编写中多加练习,在实践中不断掌握和巩固常用的指令。读者应熟练地查阅下表,正确地理解指令的功能及特性并正确地使用指令。
3.6 某些指令的说明
111条指令介绍完毕。一些细节问题,还需要说明。
1. 关于并行I/O口的“读引脚”和“读锁存器”指令的区别
例如,当P1口的P1.0引脚外接一个发光二极管LED的阳极,LED的阴极接地。
若想查看一下单片机刚才向P1.0脚输出的信息是0还是1,不能直接从P1.0脚读取,因为单片机刚才向P1.0输出的信息如果是1的话,则LED导通点亮,此时P1.0引脚就为0电平,如果直接读引脚,结果显然错误。
正确的做法是读D锁存器的Q端状态,那里储存的才是前一时刻送给P1.0的真实值。就是说,凡遇“读取P1口前一状态以便修改后再送出”的情形,都应当“读锁存器”的Q端信息,而不是读取引脚的信息。
当P1口外接输入设备时,要想P1口引脚上反映的是真实的输入信号,必须要设法先让该引脚内部的场效应管截止才行,否则当场效应管导通时,P1口引脚上将永远为低电平,无法正确反映外设的输入信号。让场效应管截止,就是用指令给P1口的相应位送一个“1”电平,这就是为什么读引脚之前,一定要先送出1的原因。
指令“MOV C,P1.0”读的是P1.0脚,同样,指令“MOV A,P1”也是读引脚指令,读引脚指令之前一定要有向P1.0写“1”的指令。
而指令“CPL P1.0”则是“读锁存器”,也即“读—修改—写”指令,它会先读P1.0的锁存器的Q端状态,接着取反,然后再送到P1.0引脚上。而指令“ANL P1,A” 也是“读锁存器”命令。类似的“读—修改—写”指令举例如下:
INC P1
XRL P3,A
ORL P2,A
ANL P1,A
CPL P3.0
2. 关于操作数的字节地址和位地址的区分问题
如何区别指令中出现的字节变量和位变量?
例如指令“MOV C,40H”和指令“MOV A,40H”两条指令中源操作数“40H”都是以直接地址形式给出的,“40H” 是字节地址还是位地址?对于助记符相同指令,观察操作数就可看出。
显然前条指令中的“40H”肯定是位地址,因为目的操作数C是位变量。
后条指令的“40H” 是字节地址,因为目的操作数A是字节变量。
3. 关于累加器A与Acc的书写问题
累加器可写成A或Acc,区别是什么?
Acc汇编后的机器码必有一个字节的操作数是累加器的字节地址E0H,A汇编后则隐含在指令操作码中。
例如: “INC A”的机器码,查表3-2是04H。
如写成“INC Acc”后,则成了“INC direct”的格式,再查表3-2,对应机器码为“05H E0H”。在对累加器A直接寻址和累加器A的某一位寻址要用Acc,不能写成A。
例如,指令“POP Acc”不能写成“POP A”;
指令“SETB Acc.0”,不能写成“SETB A.0”。
4.书写2位十六进制数据前要加“0”
经常遇到必须在某些数据或地址的前面多填一个“前导”0。否则汇编就通不过?这是汇编语言的严格性和规范性的体现。
由于部分十六进制数是用字母来表示的,而程序内的标号也常用字母表示,为了将标号和数据区分开,几乎所有的汇编语言都规定,凡是以字母开头(对十六进制数而言,就是A~F开头)的数字量,应当在前面添加一个数字0。
至于地址量,它也是数据量的一种,前面也应该添加“0”。
例如:
MOV A,#0F0H ;“F0”以字母开头的数据量
MOV A,0F0H ;“F0”以字母开头的地址量
如不加“前导”0,就会把字母开头的数据量当作标号来处理,从而出错以及不能通过汇编。
3.7 8051汇编语言程序设计概述
程序是指令的有序集合。
单片机运行就是执行指令序列的过程。
编写这一指令序列的过程称为程序设计。
3.7.1 编程语言概述
常用的编程语言是汇编语言和高级语言。
1.汇编语言
用英文字符来代替机器语言,这些英文字符被称为助记符
汇编语言:用助记符表示的指令。
汇编语言源程序:用汇编语言编写的程序。
“汇编”:汇编语言源程序需转换(翻译)成为二进制代码表示的机器语言程序,才能识别和执行。
完成“翻译”的程序称为汇编程序。经汇编程序“汇编”得到的以“0”、“1”代码形式表示的机器语言程序称为目标程序。
优点:用汇编语言编写程序效率高,占用存储空间小,运行速度快,能编写出最优化的程序,
缺点:可读性差,离不开具体的硬件,是面向“硬件”的语言通用性差。
2.高级语言
不受具体“硬件”的限制,优点:通用性强,直观、易懂、易学,可读性好。
目前多数的51单片机用户使用C语言(C51)来进行程序设计,已公认为高级语言中高效简洁而又贴近51单片机硬件的编程语言。
将C语言向单片机上移植,始于20世纪80年代的中后期。
经过十几年努力,C51已成为单片机的实用高级编程语言。
尽管目前已有不少设计人员使用C51来进行程序开发,但在对程序的空间和时间要求较高的场合,汇编语言仍必不可少。
在这种场合下,可使用C语言和汇编语言混合编程。在很多需要直接控制硬件且对实时性要求较高的场合,则更是非用汇编语言不可。
掌握汇编语言并能进行程序设计,是学习和掌握单片机程序设计的基本功之一。
3.7.2 汇编语言语句和格式
两种基本语句:指令语句和伪指令语句。
(1)指令语句
已在第3章介绍。每一指令语句在汇编时都产生一个指令代码(机器代码),执行该指令代码对应着机器的一种操作。
(2)伪指令语句
是控制汇编(翻译)过程的一些控制命令。在汇编时没有机器代码与之对应。
汇编语言语句是符合典型的汇编语言的四分段格式:
标号字段和操作码字段之间要有冒号“:”分隔;
操作码字段和操作数字段间的分界符是空格;
双操作数之间用逗号相隔;
操作数字段和注释字段之间的分界符用分号“;”。
任何语句都必须有操作码字段,其余各段为任选项。
【例4-1】下面是一段程序的四分段书写格式。
标号字段 操作码字段 操作数字段 注释字段
START:MOV A,#00H ;0→A
MOV R1,#10 ;10→R1
MOV R2,#00000011B ;03H→R2
LOOP: ADD A,R2 ;(A)+(R2)→A
DJNZ R1,LOOP ;R1减1不为零,则跳LOOP处
NOP
HERE: SJMP HERE
上述4个字段应该遵守的基本语法规则如下。
1.标号字段
语句所在地址的标志符号,才能被访问。如标号“START”和“LOOP”等。有关标号规定如下:
(1)标号后必须跟冒号“:”。
(2)标号由1~8个ASCII码字符组成,第一个字符必须是字母。
(3)同一标号在一个程序中只能定义一次,不能重复定义。
(4)不能使用汇编语言已经定义的符号作为标号,如指令助记符、伪指令以及寄存器的符号名称等。
(5)标号的有无,取决于本程序中的其他语句是否访问该条语句。如无其他语句访问,则该语句前不需标号。
2.操作码字段
操作码字段规定了语句执行的操作,操作码是汇编语言指令中唯一不能空缺的部分。
3.操作数字段
指令的操作数或操作数地址。
在本字段中,操作数的个数因指令的不同而不同。通常有单操作数、双操作数和无操作数三种情况。
如果是多操作数,则操作数之间要以逗号隔开。
操作数表示时,几种情况需注意:
(1)十六进制、二进制和十进制形式的操作数表示
多数情况,操作数或操作数地址是采用十六进制形式来表示的。则需加后缀“H”。
在某些特殊场合用二进制表示,需加后缀“B”
若操作数采用十进制形式,则需加后缀“D”,也可省略。
若十六进制操作数以字符A~F开头,需在它前面加一个 “0”,以便汇编时把它和字符A~F区别开。
(2)工作寄存器和特殊功能寄存器的表示
当操作数为工作寄存器或特殊功能寄存器时,允许用工作寄存器和特殊功能寄存器的代号表示。
例如,工作寄存器用R7~R0,累加器用A(或Acc)表示。另外,工作寄存器和特殊功能寄存器也可用其地址来表示,如累加器A可用其地址E0H来表示。
4.注释字段
用于解释指令或程序的含义,对可读性非常有用。
使用时须以分号开头,长度不限,一行写不下可换行书写,但注意也要以分号开头。
汇编时,遇到“;” 就停止“翻译”。因此,注释字段不会产生机器代码。
3.7.3 伪指令
在汇编语言源程序中应有向汇编程序发出的指示信息,告诉它如何完成汇编工作,这是通过伪指令来实现。
伪指令不属于指令系统中的汇编语言指令,它是程序员发给汇编程序的命令,也称为汇编程序控制命令。
只有在汇编前的源程序中才有伪指令。 “伪”体现在汇编后,伪指令没有相应的机器代码产生。
伪指令具有控制汇编程序的输入/输出、定义数据和符号、条件汇编、分配存储空间等功能。
不同汇编语言的伪指令有所不同,但基本内容相同。
介绍常用的伪指令。
1.ORG(ORiGin)汇编起始地址命令
源程序的开始,用一条ORG伪指令规定程序的起始地址。如果不用ORG,则汇编得到的目标程序将从0000H地址开始。例如:
ORG 2000H
START: MOV A,#00H
……
即规定标号START代表地址为2000H开始。
在一源程序中,可多次用ORG指令,规定不同的程序段的起始地址。但是,地址必须由小到大排列,且不能交叉、
重叠。例如:
ORG 2000H
……
ORG 2500H
……
ORG 3000H
……
这种顺序是正确的。若按下面顺序的排列则是错误的,因为地址出现了交叉。
ORG 2500H
……
ORG 2000H
……
ORG 3000H
……
2. END(END of Assembly)汇编终止命令
源程序结束标志,终止源程序的汇编工作。整个源程序中只能有一条END命令,且位于程序的最后。如果END出现在程序中间,其后的源程序,将不进行汇编处理。
3.EQU(EQUate)标号赋值命令
用于给标号赋值。赋值后,标号值在整个程序有效。
例如:TEST: EQU 2000H
表示TEST=2000H,汇编时,凡是遇到TEST时,均以2000H来代替。
4.DB(Define Byte)定义数据字节命令
用于从指定的地址开始,在程序存储器连续单元中定义字节数据。例如:
ORG 2000H
DB 30H,40H,24,"C","B"
汇编后
(2000H)=30H
(2001H)=40H
(2002H)=18H(十进制数24)
(2003H)=43H(字符“C”的ASCII码)
(2004H)=42H(字符“B”的ASCII码)
显然,DB功能是从指定单元开始定义(存储)若干字节,十进制数自然转换成十六进制数,字母按ASCII码存储。
5.DW(Define Word)定义数据字命令
该命令用于从指定的地址开始,在程序存储器的连续单元中定义16位的数据字。例如:
ORG 2000H
DW 1246H,7BH,10
汇编后
(2000H)=12H ;第1个字
(2001H)=46H
(2002H)=00H ;第2个字
(2003H)=7BH
(2004H)=00H ;第3个字
(2005H)=0AH
6.DS(Define Storage)定义存储区命令
从指定地址开始,保留指定数目的字节单元作为存储区,供程序运行使用。例如:
TABEL:DS 10
表示从TABEL代表的地址开始,保留10个连续的地址单元。又例如:
ORG 2000H
DS 10 H
表示从2000H地址开始,保留16个连续地址单元。
注意:DB、DW和DS命令只能对程序存储器有效,不能对数据存储器使用。
7.BIT 位定义命令
用于给字符名称赋以位地址,位地址可以是绝对位地址,也可是符号地址。例如:
QA BIT P1.6
功能是把P1.6的位地址赋给变量QA。
3.7.4 汇编语言源程序的汇编
“汇编”----可分为手工汇编和机器汇编两类。
1.手工汇编
通过查指令的机器代码表(表3-2),逐个把助记符指令“翻译”成机器代码,再进行调试和运行。
手工汇编遇到相对转移偏移量的计算时,较麻烦,易出错,只有小程序或受条件限制时才使用。实际中,多采用“汇编程序”来自动完成汇编。
2. 机器汇编
用微型计算机上的软件(汇编程序)来代替手工汇编。 在微机上用编辑软件进行源程序编辑,然后生成一个ASCII码文件,扩展名为 “.ASM”。在微机上运行汇编程序,译成机器码。
机器码通过微机的串口(或并口)传送到用户样机(或在线仿真器),进行程序的调试和运行。
有时,在分析某些产品的程序的机器代码时,需将机器代码翻译成汇编语言源程序,称为“反汇编”。
【例3-13】 表3-3是一段源程序的汇编结果,可手工汇编,来验证下面的汇编结果是否正确。机器码从1000H单元开始存放。