嵌入式系统 - 说明
程序流程以顺序方式进行,从一条指令到下一条指令,除非执行控制转移指令。汇编语言中的各种类型的控制转移指令包括条件或无条件跳转和调用指令。
循环和跳转指令
在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 标签指令的循环操作仅限于 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 中使用的条件跳转 -
操作说明 | 行动 |
---|---|
锦泽 | 如果 A = 0 则跳转 |
济南 | 如果 A ≠ 0 则跳转 |
DJNZ | 如果寄存器 ≠ 0,则递减并跳转 |
CJNE A,数据 | 如果 A ≠ 数据则跳转 |
CJNE reg,#data | 如果字节≠数据则跳转 |
杰成 | 如果 CY = 1 则跳转 |
江南公司 | 如果 CY ≠ 1 则跳转 |
柔佛州 | 如果位 = 1 则跳转 |
江南银行 | 如果位 = 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 用于调用子例程或方法。子例程用于执行需要频繁执行的操作或任务。这使得程序更加结构化并节省内存空间。有两个指令 - 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 字节范围内。