嵌入式系统 - 指令

除非执行控制转移指令,否则程序流程将按顺序进行,从一条指令到下一条指令。汇编语言中的各种控制转移指令包括条件或无条件跳转和调用指令。

循环和跳转指令

8051 中的循环

重复执行一系列指令一定次数称为循环。指令DJNZ reg, label用于执行循环操作。在此指令中,寄存器减 1;如果不是零,则 8051 跳转到标签所指的目标地址。

在循环开始之前,寄存器中加载了重复次数的计数器。在此指令中,寄存器减量和跳转决定都合并为一条指令。寄存器可以是 R0–R7 中的任意一个。计数器也可以是 RAM 位置。

示例

使用重复加法技术将 25 乘以 10。

解决方案 − 乘法可以通过重复添加被乘数(与乘数一样多)来实现。例如,

25 * 10 = 250(FAH)

25 + 25 + 25 + 25 + 25 + 25 + 25 + 25 + 25 + 25 = 250

   MOV A,#0             ;A = 0,clean ACC 
   MOV R2,#10           ; the multiplier is replaced in R2 
   Add A,#25            ;add the multiplicand to the ACC 
	
AGAIN:DJNZ R2, 
AGAIN:repeat  until R2 = 0 (10 times) 

   MOV R5 , A           ;save A in R5 ;R5 (FAH)

8051 中的缺点 − 使用指令 DJNZ Reg label 的循环操作仅限于 256 次迭代。如果不进行条件跳转,则执行跳转后的指令。

循环内的循环

当我们在另一个循环内使用循环时,这被称为 嵌套循环。当最大计数限制为 256 时,使用两个寄存器来保存计数。因此,我们使用此方法重复操作超过 256 次。

示例

编写一个程序来 −

  • 将值 55H 加载到累加器。
  • 对 ACC 进行 700 次补码。

解决方案 −由于 700 大于 255(任何寄存器的最大容量),因此使用两个寄存器来保存计数。以下代码显示如何使用两个寄存器 R2 和 R3 进行计数。

   MOV A,#55H            ;A = 55H 
	
NEXT: MOV R3,#10         ;R3 the outer loop counter 
AGAIN:MOV R2,#70         ;R2 the inner loop counter 

   CPL A                 ;complement

其他条件跳转

下表列出了 8051 − 中使用的条件跳转

指令 操作
JZ 如果 A = 0 则跳转
JNZ 如果 A ≠ 0 则跳转
DJNZ 如果寄存器 ≠ 0 则递减并跳转
CJNE A, 数据 如果 A ≠ 则跳转数据
CJNE reg, #data 如果字节 ≠ 数据则跳转
JC 如果 CY = 1 则跳转
JNC 如果 CY ≠ 1 则跳转
JB 如果位 = 1 则跳转
JNB 如果位 = 0 则跳转
JBC 如果位 = 1 并清除位则跳转
  • JZ(如果 A = 0 则跳转) − 在此指令中,检查累加器的内容。如果为零,则 8051 跳转到目标地址。JZ 指令只能用于累加器,不适用于任何其他寄存器。

  • JNZ(如果 A 不等于 0 则跳转) − 在此指令中,检查累加器的内容是否为非零。如果不为零,则 8051 跳转到目标地址。

  • JNC(如果没有进位则跳转,如果 CY = 0 则跳转) − 标志(或 PSW)寄存器中的进位标志位用于决定是否跳转"JNC 标签"。CPU 查看进位标志以查看其是否被提升(CY = 1)。如果不升高,则CPU开始从标号的地址取指令并执行,如果CY = 1,则不跳转,而是执行JNC下面的下一条指令。

  • JC(进位跳转,CY = 1跳转) − 如果CY = 1,则跳转到目标地址。

  • JB(位高跳转)

  • JNB(位低跳转)

注意 −必须注意的是,所有条件跳转都是短跳转,即目标地址必须在程序计数器内容的 -128 到 +127 字节范围内。

无条件跳转指令

8051 中有两种无条件跳转 −

  • LJMP(长跳转) − LJMP 是 3 字节指令,其中第一个字节表示操作码,第二和第三个字节表示目标位置的 16 位地址。2 字节目标地址允许跳转到从 0000 到 FFFFH 的任意内存位置。

  • SJMP(短跳转) − 它是一个 2 字节指令,其中第一个字节是操作码,第二个字节是目标位置的相对地址。相对地址范围从 00H 到 FFH,分为正向和反向跳转;即在相对于当前 PC(程序计数器)地址的 -128 到 +127 字节内存范围内。在正向跳转的情况下,目标地址可以在距离当前 PC 127 字节的空间内。如果是向后跳转,目标地址可以在当前 PC 的 –128 字节范围内。

计算短跳转地址

所有条件跳转(JNC、JZ 和 DJNZ)都是短跳转,因为它们是 2 字节指令。在这些指令中,第一个字节表示操作码,第二个字节表示相对地址。目标地址始终相对于程序计数器的值。要计算目标地址,将第二个字节添加到跳转正下方指令的 PC。查看下面给出的程序 −

Line   PC    Op-code   Mnemonic   Operand 
1      0000               ORG       0000 
2      0000  7800         MOV       R0,#003  
3      0002  7455         MOV       A,#55H0 
4      0004  6003         JZ        NEXT 
5      0006  08           INC       R0 
6      0007  04   AGAIN:  INC       A 
7      0008  04           INC       A 
8      0009  2477 NEXT:   ADD       A, #77h 
9      000B  5005         JNC       OVER 
10     000D  E4           CLR       A
11     000E  F8           MOV       R0, A 
12     000F  F9           MOV       R1, A 
13     0010  FA          MOV       R2, A 
14     0011  FB           MOV       R3, A 
15     0012  2B   OVER:   ADD       A, R3 
16     0013  50F2         JNC       AGAIN 
17     0015  80FE HERE:   SJMP      HERE 
18     0017             END

反向跳转目标地址计算

在向前跳转的情况下,位移值为 0 到 127(十六进制为 00 到 7F)之间的正数。但是,对于向后跳转,位移为 0 到 -128 之间的负值。

CALL 指令

CALL 用于调用子程序或方法。子程序用于执行需要经常执行的操作或任务。这使程序更加结构化并节省内存空间。有两个指令 − LCALL 和 ACALL。

LCALL(长调用)

LCALL 是一个 3 字节指令,其中第一个字节表示操作码,第二个和第三个字节用于提供目标子程序的地址。 LCALL 可用于调用 8051 64K 字节地址空间内的子程序。

为了成功返回到执行被调用子程序后的点,CPU 会将 LCALL 正下方的指令地址保存在堆栈中。因此,当调用子程序时,控制权将转移到该子程序,处理器将 PC(程序计数器)保存在堆栈上并开始从新位置获取指令。指令 RET(返回)在完成子程序执行后将控制权转移回调用者。每个子程序都使用 RET 作为最后一条指令。

ACALL(绝对调用)

ACALL 是 2 字节指令,而 LCALL 是 3 字节指令。子程序的目标地址必须在 2K 字节以内,因为 2 个字节中只有 11 位用于地址。 ACALL 与 LCALL 的区别在于:LCALL 的目标地址可以是 8051 的 64K 字节地址空间内的任何位置,而 CALL 的目标地址则在 2K 字节范围内。