简单汇编程序设计

《嵌入式系统设计与应用》课程要点

Posted by John Mactavish on November 21, 2020

以 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, #00HNEXT  ;取数据 
       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 标志位:

  1. 若目的操作数=源操作数,则 CY=0
  2. 若目的操作数>源操作数,则 CY=0
  3. 若目的操作数<源操作数,则 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 作运行时变量索引,通过间接寻址跳转。

子程序调用

汇编语言子程序结构中,参数的传递要靠程序设计者自己安排数据的存放和工作单元的选择。汇编语言子程序参数的传递一般可采用下面的方法:

  1. 传递数据:将数据通过工作寄存器 R0~R7 或者累加器 A 来传送。
  2. 传递地址:数据存放在数据存储器中,参数传递时只通过 R0、R1、DPTR 传递数据所存放的地址。调用结束时,结果也可存放在数据存储器中,传送回的也是存在寄存器中的地址。
  3. 通过堆栈传递参数:在调用之前,先把要传送的参数压入堆栈,进入子程序后,再将堆栈中的参数弹出到工作寄存器或其他内部 RAM 单元。

进入子程序后,除了要处理的参数数据和要传递回主程序的参数之外,需要现场保护

  • 有关的内部 RAM 单元的内容
  • 工作寄存器的内容
  • 各标志的状态

现场保护方法:

  • 进入子程序时,将使用的或被改变的单元内容压堆栈,在返回前,把堆栈中数据弹出到原对应的工作单元
  • 对所使用的工作寄存器的保护可用改变工作寄存器组的方法

如果你喜欢我的文章,请我吃根冰棒吧 (o゜▽゜)o ☆

contribution

最后附上 GitHub:https://github.com/gonearewe