以 MCS-51 汇编为例。
循环(数组处理为例)
多字节二进制数减法
设被减数首地址存于 R0,减数首地址存于 R1,两个数的字节长度存于 R2,相减结果存于被减数所在单元,
多字节二进制数按低字节在前的顺序存放。
MSUB: CLR C ;清进位位
MSUB1: MOV A, @R0 ;被减数最低字节
SUBB A, @R1 ;减
MOV @R0, A ;存
INC R0 ;指向高一个字节
INC R1
DJNZ R2, MSUB1 ;循环
CY位保存减法结果的借位状态
注意数组处理时常用 Ri(i=0,1)
作数组索引,如果同时处理两个数组就把 R0 和 R1 都用上。
通过间接寻址读写数组元素(MOV @R0, A
),通过自增调整数组索引(INC R0
)。
DJNZ 指令功能是先将操作数内容减1,然后判断,如果操作数不为零,则转移到指定单元,否则顺序执行。 他有两种用法:
DJNZ Rn, rel
DJNZ direct, rel
Rn 表示 R0 到 R7 共 8 个工作寄存器;direct 表示直接地址(如 0F7H、67H、2AH); rel 表示一个字节的有符号数补码,可索引当前指令地址前后共 256 字节的区间,一般由汇编器 从标号汇编而来而非直接写出。
第一条指令是两字节指令,第二条是三字节指令,都不影响 PSW。
DJNZ 的用法类似于 do-while ,那么可以用等效于 while 的 CJNE 代替:
从内部 RAM 的 22H 单元开始存放了一组 8 位无符号数,数据块长度在 20H 单元,编写求和程序,
和存入 21H 单元,设累加结果不超过 8 位二进制数。
ORG 1000H ;向汇编器要求代码起始于 ROM 的 1000H 地址处
START: CLR A
MOV R1, #22H ;数据块首址
MOV R2, 20H ;字节数
LOOP: CJNE R2, #00H,NEXT ;取数据
MOV 21H, A ;存累加和
HERE: SJMP HERE
NEXT: ADD A,@R1 ;累加
INC R1 ;修改地址指针
DEC R2 ;计数值减一
SJMP LOOP
HERE: SJMP HERE
表示程序结束,在此死循环。可以用 SJMP $
代替,美元符表示当前指令地址。
CJNE 功能是把两个操作数作比较,若二者不相等则转移,否则顺序执行。它共有四种用法:
CJNE A, #data, rel
CJNE A, direct, rel
CJNE Rn, #data, rel
CJNE @Ri, #data, rel
#data
表示立即数,即数字字面量。
它们都是三字节指令。与 DJNZ 不同的是,CJNE 会影响 CY 标志位:
- 若目的操作数=源操作数,则 CY=0
- 若目的操作数>源操作数,则 CY=0
- 若目的操作数<源操作数,则 CY=1
即相当于用第一个操作数减去第二个操作数,CY 表示结果是否为负。 利用本组指令可以判别两数的大小:
将累加器内容与内部 RAM 30H 单元的数相比。若 (A)=(30H) 则程序转到 BR0;若 (A)<(30H) 则转向 BR1;
若 (A)>(30H) 则转到 BR2。BR0、BR1、BR2 的地址在本程序的同一 2KB 页面内。
CJNE A, 30H, NEXT ;比较
AJMP BR0 ;=
NEXT: JC LAG ;<
AJMP BR2 ;>
LAG: AJMP BR1
JC rel
根据 CY 位是否为 1 决定是否跳转。
查表程序
设内部 RAM 的 30H 有一个数,根据该值的不同转移到不同的程序段进行处理,设数值的范围是 0~10 的无符号数
MOV A, 30H ;取数
RL A ;循环左移,相当于 ×2
MOV DPTR, #JMPTBL ;跳转表首地址
JMP @A+DPTR ;一次跳转
JMPTBL: AJMP BRCH0 ;转至分支 0
AJMP BRCH1 ;转至分支 1
......
AJMP BRCH10 ;转至分支 10
BRCH0: ...... ;分支 0 程序
...... ;其他分支的程序
跳转表的每一条 AJMP (11 bits addr)
都是两字节的,所以变量索引要乘二;如果用的是三字节指令 LJMP (16 bits addr)
,
则要乘三。
第一次跳转核心在于 JMP @A+DPTR
。这是常用模板,以 DPTR 作跳转表首地址,以 A 作运行时变量索引,通过间接寻址跳转。
子程序调用
汇编语言子程序结构中,参数的传递要靠程序设计者自己安排数据的存放和工作单元的选择。汇编语言子程序参数的传递一般可采用下面的方法:
- 传递数据:将数据通过工作寄存器 R0~R7 或者累加器 A 来传送。
- 传递地址:数据存放在数据存储器中,参数传递时只通过 R0、R1、DPTR 传递数据所存放的地址。调用结束时,结果也可存放在数据存储器中,传送回的也是存在寄存器中的地址。
- 通过堆栈传递参数:在调用之前,先把要传送的参数压入堆栈,进入子程序后,再将堆栈中的参数弹出到工作寄存器或其他内部 RAM 单元。
进入子程序后,除了要处理的参数数据和要传递回主程序的参数之外,需要现场保护
- 有关的内部 RAM 单元的内容
- 工作寄存器的内容
- 各标志的状态
现场保护方法:
- 进入子程序时,将使用的或被改变的单元内容压堆栈,在返回前,把堆栈中数据弹出到原对应的工作单元
- 对所使用的工作寄存器的保护可用改变工作寄存器组的方法
如果你喜欢我的文章,请我吃根冰棒吧 (o゜▽゜)o ☆
最后附上 GitHub:https://github.com/gonearewe