当前位置: 首页 > news >正文

51单片机的五类指令(四)——控制转移类指令

目录

一、无条件转移指令

(一)LJMP(长转移指令)

 (二)AJMP(绝对转移指令)

(三)SJMP(短转移指令)

(四)JMP @A+DPTR(间接转移指令)

二、条件转移指令

(一)累加器 A 判零转移指令

1、JZ(累加器 A 为零转移)

2、JNZ(累加器 A 不为零转移)

(二)比较不相等转移指令

1、CJNE A, direct, rel

2、CJNE A, #data, rel

3、CJNE Rn, #data, rel

4、CJNE @Ri, #data, rel

5、示例与代码分析

6、偏移量 rel 详解

(三)减 1 不为零转移指令

1、DJNZ Rn, rel

2、DJNZ direct, rel

三、子程序调用与返回指令

(一)子程序调用指令

1、LCALL(长调用指令)

2、ACALL(绝对调用指令)

(二)子程序返回指令

1、RET(子程序返回指令)

2、RETI(中断返回指令)

四、空操作指令


        单片机中寻址是为了找到操作数,而各类指令则是为了对操作数进行操作。本篇文章将介绍51单片机中的第四类指令——控制转移类指令。

        51 单片机的控制转移类指令是汇编语言编程里极为重要的一类指令,其主要功能是改变程序的执行顺序,这在流程控制方面十分关键。以下是对这些指令的详细介绍。

一、无条件转移指令

(一)LJMP(长转移指令)

1、格式:LJMP addr16

2、功能

        把 16 位目标地址 addr16 载入程序计数器PC,让程序无条件跳转到该地址继续执行。此指令的转移范围是 64KB 程序存储器空间(0000H - FFFFH)。

3、示例

LJMP 2000H ; 程序跳转到地址2000H处继续执行

4、代码分析

(1)LJMP 是长转移指令,其操作数 2000H 为一个 16 位的目标地址。

(2)当执行这条指令时,程序计数器 PC 会被直接赋值为 2000H。也就是说,不管当前程序执行到哪里,都会立即跳转到地址 2000H 处接着执行该地址上存储的指令。由于 LJMP 可以在 64KB(0000H - FFFFH)的程序存储器空间内进行跳转,所以可以用于跳转到程序的任意位置。 

 (二)AJMP(绝对转移指令)

1、格式:AJMP addr11

2、功能

        把当前PC值的高 5 位和指令中给出的 11 位地址addr11组合成新的 16 位地址,再载入PC,从而使程序跳转到该地址执行。它的转移范围是当前PC值所在的 2KB 页面内

3、示例

AJMP 030H ; 假设当前PC值高5位为00001,组合后跳转到0030H地址处执行

4、代码分析

(1)AJMP 是绝对转移指令,操作数 030H 是一个 11 位的地址。将其转换为二进制数可得:030H = 0000 0011 0000B。从二进制表示能够看出,它总共 12 位,不过最高位是 0,所以有效的部分其实是 11 位(000 0011 0000)

(2)该指令会将当前 PC 值的高 5 位与 030H 组合成新的 16 位地址。例如,若当前 PC 值的高 5 位是 00001,那么新的地址就是 00001 0000011000B,即 0030H。程序会跳转到这个新地址处继续执行。不过,AJMP 的转移范围被限制在当前 PC 值所在的 2KB 页面内,也就是 2048 字节的地址空间。

(三)SJMP(短转移指令)

1、格式:SJMP rel

2、功能

        rel是一个 8 位带符号偏移量,范围为 -128 到 +127。新的PC值等于当前PC值加上偏移量rel,程序就会跳转到新地址执行。它的转移范围是以当前PC值为中心的 -128 到 +127 字节。

3、示例

SJMP NEXT ; 跳转到标号NEXT处执行
NEXT: MOV A, #0FFH ; 标号NEXT处的指令

4、代码分析

(1)SJMP 是短转移指令,NEXT 是一个标号,代表目标地址汇编器会计算出从当前 PC 值到标号 NEXT 处的 8 位带符号偏移量 rel

(2)当执行 SJMP NEXT 时,会把当前 PC 值加上这个偏移量 rel,得到新的 PC 值,程序就会跳转到该地址。rel 的范围是 -128 到 +127,所以 SJMP 的转移范围是以当前 PC 值为中心的 -128 到 +127 字节。

(四)JMP @A+DPTR(间接转移指令)

1、格式:JMP @A+DPTR

2、功能

        把累加器A中的 8 位无符号数与数据指针DPTR中的 16 位地址相加,结果作为新的PC值,使程序跳转到该地址执行。这一指令可实现多分支转移。

3、示例

MOV DPTR, #TABLE ; 数据指针指向跳转表首地址
MOV A, R0 ; 累加器A中存放偏移量
JMP @A+DPTR ; 根据A的值跳转到相应分支
TABLE: AJMP ROUTINE0 ; 跳转表
       AJMP ROUTINE1
       AJMP ROUTINE2

4、代码分析

(1)MOV DPTR, #TABLE:将数据指针 DPTR 赋值为跳转表 TABLE 的首地址,这样 DPTR 就指向了跳转表的起始位置。

(2)MOV A, R0:把寄存器 R0 中的值传送到累加器 A 中,A 中的值将作为偏移量。

(3)JMP @A+DPTR:将累加器 A 中的 8 位无符号数与 DPTR 中的 16 位地址相加,得到的结果作为新的 PC 值。程序会根据 A 的值跳转到跳转表中的相应分支

(4)跳转表 TABLE 由多个 AJMP 指令构成,每个 AJMP 指令对应一个分支程序入口(如 ROUTINE0、ROUTINE1、ROUTINE2)。通过改变 A 的值,就可以实现程序的多分支转移

二、条件转移指令

(一)累加器 A 判零转移指令
1、JZ(累加器 A 为零转移)

(1)格式:JZ rel

(2)功能

        若累加器A的值为 0就把当前PC值加上偏移量rel,得到新的PC值,程序跳转到该地址执行若A的值不为 0,程序则顺序执行下一条指令。偏移量rel是 8 位带符号数,范围是 -128 到 +127。

(3)示例

MOV A, #00H ; 将立即数00H送入累加器A
JZ NEXT ; 因为A的值为0,跳转到标号NEXT处执行
NOP ; 若不跳转,执行该空操作指令
NEXT: MOV B, #01H ; 标号NEXT处的指令

(4)代码分析

① MOV A, #00H:把立即数 00H 传送到累加器 A 中,此时 A 的值为 0。

② JZ NEXT:JZ 指令会判断累加器 A 的值是否为 0。由于 A 为 0,满足跳转条件,程序会跳转到标号 NEXT 处执行。

③ NOP:这是一条空操作指令,若 A 不为 0,程序会顺序执行这条指令。

④ MOV B, #01H:程序跳转到此处后,将立即数 01H 传送到寄存器 B 中。

        注意:偏移量 rel 是在汇编阶段由汇编器先计算出目标地址(标号 NEXT 对应的地址)与当前 PC 值的距离然后将这个距离转换为一个 8 位带符号数作为偏移量加入到指令中。

2、JNZ(累加器 A 不为零转移)

(1)格式:JNZ rel

(2)功能

        若累加器A的值不为 0,就把当前PC值加上偏移量rel,得到新的PC值,程序跳转到该地址执行;若A的值为0,程序则顺序执行下一条指令。

(3)示例

MOV A, #01H ; 将立即数01H送入累加器A
JNZ NEXT ; 因为A的值不为0,跳转到标号NEXT处执行
NOP ; 若不跳转,执行该空操作指令
NEXT: MOV B, #02H ; 标号NEXT处的指令

(4)代码分析

① MOV A, #01H:将立即数 01H 传送到累加器 A 中,此时 A 的值不为 0。

② JNZ NEXT:JNZ 指令判断累加器 A 的值是否不为 0。由于 A 不为 0,满足跳转条件,程序跳转到标号 NEXT 处执行。

③ NOP:若 A 为 0,程序会顺序执行这条空操作指令。

④ MOV B, #02H:程序跳转到此处后,将立即数 02H 传送到寄存器 B 中。

(二)比较不相等转移指令
1、CJNE A, direct, rel

(1)格式:CJNE A, direct, rel

(2)功能

        把累加器A的值和直接地址direct单元中的值进行比较。若二者不相等,就把当前PC值加上偏移量rel,程序跳转到该地址执行;若相等,程序则顺序执行下一条指令。

        同时,该指令还会影响进位标志CY:若A < direct,则CY = 1;若A > direct,则CY = 0。

2、CJNE A, #data, rel

(1)格式:CJNE A, #data, rel

(2)功能

        把累加器A的值和立即数data进行比较。若二者不相等,就把当前PC值加上偏移量rel,程序跳转到该地址执行;若相等,程序则顺序执行下一条指令。同样会影响进位标志CY。

3、CJNE Rn, #data, rel

(1)格式:CJNE Rn, #data, rel

(2)功能

        把寄存器Rn(n = 0 - 7)的值和立即数data进行比较。若二者不相等,就把当前PC值加上偏移量rel,程序跳转到该地址执行;若相等,程序则顺序执行下一条指令。也会影响进位标志CY。

4、CJNE @Ri, #data, rel

(1)格式:CJNE @Ri, #data, rel

(2)功能

        把以寄存器Ri(i = 0, 1)内容为地址的内部 RAM 单元的值和立即数data进行比较。若二者不相等,就把当前PC值加上偏移量rel,程序跳转到该地址执行;若相等,程序则顺序执行下一条指令。同样会影响进位标志CY。

5、示例与代码分析
MOV A, #05H ; 将立即数05H送入累加器A
MOV 30H, #06H ; 将立即数06H送入内部RAM的30H单元
CJNE A, 30H, NEXT ; 比较A和30H单元的值,不相等则跳转到NEXT
NOP ; 若相等,执行该空操作指令
NEXT: JC LESS  ; 若CY = 1,说明A < 30H单元的值,跳转到LESS
               ; 若CY = 0,说明A > 30H单元的值,继续执行下面的指令
LESS: MOV B, #0FFH ; 标号LESS处的指令

① MOV A, #05H:将立即数 05H 传送到累加器 A 中。

② MOV 30H, #06H:将立即数 06H 传送到内部 RAM 的 30H 单元。

③ CJNE A, 30H, NEXT:比较累加器 A 的值和内部 RAM 30H 单元的值。由于05H不等于06H,满足不相等条件,程序跳转到标号 NEXT 处执行,同时该指令会影响进位标志 CY。因为 A < 30H 单元的值,所以 CY = 1。

④ NOP:若 A 和 30H 单元的值相等,程序会顺序执行这条空操作指令。

⑤ JC LESS:JC 指令判断进位标志 CY 是否为 1。由于 CY = 1,满足跳转条件,程序跳转到标号 LESS 处执行。

⑥ MOV B, #0FFH:程序跳转到此处后,将立即数 0FFH 传送到寄存器 B 中。

6、偏移量 rel 详解

(1)编写代码时用标号指定跳转目标

         在 51 单片机汇编语言里,当使用条件转移指令(像 JZ、JNZ、CJNE 等)或者相对短转移指令(如 SJMP)时,通常会在偏移量的位置填写标号。例如:

MOV A, #0
JZ LOOP  ; 这里 LOOP 是一个标号
NOP
LOOP: MOV B, #1

        在这个代码片段中,JZ LOOP 里的 LOOP 就是一个标号,用来代表程序跳转的目标位置。在编写代码时,程序员借助标号来表达程序逻辑的跳转,而无需关心具体的地址计算

(2)汇编过程中计算偏移量 rel

汇编器在处理代码时,会经历以下步骤:

        ① 确定标号对应的地址:汇编器会对整个代码进行扫描,确定每个标号在程序存储器中对应的实际地址。例如在上述代码中,它会记录下 LOOP 标号对应的地址

        ② 计算偏移量 rel:当遇到转移指令时,汇编器会计算当前 PC 值(即执行完当前转移指令后的 PC 值)和标号所对应地址之间的差值。具体计算公式是:rel = 目标地址 - (当前PC值 + 转移指令字节数)。以 JZ 指令为例,它是双字节指令,所以 rel = 目标地址 - (当前PC值 + 2)。

        ③ 生成机器码:汇编器将计算得到的偏移量 rel 嵌入到转移指令中,生成最终的机器码。在生成的机器码里,转移指令中偏移量位置存放的就是这个计算好的 rel 值。

(3)偏移量 rel 的特性

        ① 范围限制:由于 rel 是 8 位带符号数,其取值范围是 -128 到 +127。这意味着转移指令的跳转范围是以当前 PC 值为中心的 -128 到 +127 字节。如果目标地址超出这个范围,就需要使用其他类型的转移指令(如长转移指令 LJMP)。

        ② 相对跳转:rel 是相对偏移量,这使得程序具有一定的灵活性。当代码在不同的存储区域加载时,只要相对位置关系不变,转移指令依然能够正确跳转。

        总之,在编写汇编代码时用标号指定跳转目标而在汇编过程中,汇编器会把标号转换为实际的偏移量 rel,从而实现程序的条件跳转。

(三)减 1 不为零转移指令
1、DJNZ Rn, rel

(1)格式:DJNZ Rn, rel

(2)功能:先将寄存器Rn(n = 0 - 7)的值减 1,再判断其结果是否为 0。若不为 0,就把当前PC值加上偏移量rel,程序跳转到该地址执行;若为 0,程序则顺序执行下一条指令。此指令常用于循环程序中控制循环次数。

(3)示例

MOV R2, #03H ; 将立即数 03H 送入寄存器 R2
LOOP: DJNZ R2, LOOP ; R2 的值减 1,不为 0 则跳转到 LOOP 处继续循环
NOP ; 循环结束后执行该空操作指令

(4)代码分析

① MOV R2, #03H:将立即数 03H 传送到寄存器 R2 中,此时 R2 的值为 3。

② DJNZ R2, LOOP:DJNZ 指令先将寄存器 R2 的值减 1,然后判断结果是否为 0。第一次执行时,R2 变为 2,不为 0,程序跳转到标号 LOOP 处继续执行该指令。第二次执行,R2 变为 1,不为 0,继续跳转。第三次执行,R2 变为 0,不满足跳转条件,程序顺序执行下一条指令

③ NOP:循环结束后,程序执行这条空操作指令。

2、DJNZ direct, rel

(1)格式:DJNZ direct, rel

(2)功能:先将直接地址direct单元中的值减 1,再判断其结果是否为 0。若不为 0,就把当前PC值加上偏移量rel,程序跳转到该地址执行;若为 0,程序则顺序执行下一条指令。

三、子程序调用与返回指令

        如果要从主程序中跳到子程序中执行程序,需要通过子程序调用指令;而从子程序中返回来,则需要使用子程序返回指令

(一)子程序调用指令
1、LCALL(长调用指令)

(1)格式:LCALL addr16

(2)功能

        先把当前 PC 值(调用指令的下一条指令地址)压入堆栈以保存返回地址,再将 16 位目标地址 addr16 载入 PC,让程序跳转到该地址执行子程序。其调用范围是 64KB 程序存储器空间(0000H - FFFFH)。

(3)示例

MAIN:
    LCALL SUB_PROC ; 调用子程序 SUB_PROC
    ; 子程序返回后继续执行后续指令
    SJMP $

SUB_PROC:
    ; 子程序代码
    RET ; 子程序返回

(4)代码分析

        执行 LCALL SUB_PROC 时,先将下一条指令的地址压入堆栈,然后把 SUB_PROC 对应的 16 位地址赋给 PC,程序跳转到 SUB_PROC 处执行子程序。

2、ACALL(绝对调用指令)

(1)格式:ACALL addr11

(2)功能

        先将当前 PC 值(即调用指令的下一条指令地址)压入堆栈,以此保存返回地址,接着把当前 PC 值的高 5 位和指令中给出的 11 位地址 addr11 组合成新的 16 位地址,再载入 PC,使程序跳转到该地址去执行子程序。其调用范围是当前 PC 值所在的 2KB 页面内。

(3)示例

MAIN:
    ACALL SUB_PROC ; 调用子程序 SUB_PROC
    ; 子程序返回后继续执行后续指令
    SJMP $

SUB_PROC:
    ; 子程序代码
    RET ; 子程序返回

(4)代码分析

        当执行 ACALL SUB_PROC 时,先把下一条指令的地址压入堆栈,然后将当前 PC 值的高 5 位和 SUB_PROC 对应的 11 位地址组合,程序跳转到 SUB_PROC 处执行子程序

(二)子程序返回指令
1、RET(子程序返回指令)

(1)格式:RET

(2)功能

        从堆栈中弹出两个字节的数据,将其赋值给 PC,使程序返回到调用子程序时保存的返回地址处继续执行。

(3)示例:见上面 ACALL 和 LCALL 示例中的 SUB_PROC 子程序。

(4)代码分析

        当子程序执行到 RET 指令时,会从堆栈中取出之前压入的返回地址,将其赋给 PC,程序就返回到调用子程序的下一条指令处继续执行。

2、RETI(中断返回指令)

(1)格式:RETI

(2)功能

        它主要用于中断服务子程序的返回。和 RET 类似,RETI 也会从堆栈中弹出两个字节的数据赋值给 PC,使程序返回到被中断的程序处继续执行。此外,它还会清除中断响应时设置的中断优先级状态触发器,以允许响应其他同级或低级别的中断。

(3)示例

ORG 0000H
LJMP MAIN

ORG 0003H ; 外部中断 0 中断服务程序入口地址
LJMP INT0_ISR

MAIN:
    ; 主程序代码
    SETB EA ; 全局中断使能
    SETB EX0 ; 外部中断 0 使能
    SJMP $

INT0_ISR:
    ; 中断服务子程序代码
    RETI ; 中断返回

(4)代码分析

        当发生外部中断 0 时,程序跳转到 0003H 处,然后跳转到INT0_ISR执行“中断服务子程序。执行到 RETI 指令时,从堆栈中取出返回地址赋给 PC,同时清除中断优先级状态触发器,程序返回到被中断的地方继续执行,即继续执行SJMP $。

四、空操作指令

1、格式:NOP

2、功能

        该指令不进行任何操作,仅使 PC 值加 1,然后继续执行下一条指令,通常用于产生一个机器周期的延时。

3、示例

NOP ; 空操作,延时一个机器周期

相关文章:

  • ai画图esrgan放大算法。
  • HTB-Code
  • AWS中S3的使用
  • Python Cookbook-4.18 搜集命名的子项
  • SAPIEN 仿真环境下的 pose
  • 告别失联!北斗三号多模对讲机TD70破除无网通信难题
  • go中锁的入门到进阶使用
  • 【算法day27】有效的数独——请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
  • 电子电气架构 --- 自动驾驶汽车整体架构
  • Python入门(3):语句
  • 各类神经网络学习:(七)GRU 门控循环单元(上集),详细结构说明
  • Hadoop集群常用命令
  • Kotlin 接口详解
  • 阿里云服务器安装docker以及mysql数据库
  • 集和诚携手Intel重磅发布BRAV-7820边缘计算新品,为车路云一体化场景提供强大算力支撑
  • 鸿蒙如何通过日程管理提高直播上线率
  • 【NLP】16. NLP推理方法重点回顾 -- 52道多选题
  • C#:尝试解析方法TryParse
  • JSON.toJSONString(xxx) @JsonFormat失效
  • 米勒电容-Mos管驱动台阶
  • 做网站私活多少钱/怎么做游戏推广员
  • 做页面设计的网站/百度云链接
  • 1网站建设公司/福州短视频seo公司
  • 反向代理/大兵seo博客
  • 阿三做网站/营销网络是啥意思
  • 公寓注册公司需要什么条件/重庆百度推广排名优化