第 4章 汇编语言程序设计
4.1 汇编语言程序设计方法
4.2 汇编语言源程序的基本结构顺序、循环、分支、子程序
4.3 常用程序设计举例
4.1.1 MCS-51汇编语言伪指令伪指令不属于指令系统,汇编后不会产生机器码,只用来对汇编过程进行控制或提供某些汇编信息 。
如:规定汇编生成的目标代码在内存种的存放区域,
给源程序中的符号和标号赋值以及指示汇编的结束等 。
但它不会令计算机做任何操作,没有对应的机器语言代码,由于它有指令的形式而无指令的实质,所以称
,伪,指令 。
4.1 汇编语言程序设计方法
4.1.3 MCS-51汇编语言伪指令
1,ORG( Origin) 汇编起始地址命令功能:用于规定目标程序的起始地址 。
格式:
[标号,] ORG 地址地址项,16位绝对地址,也可以用标号或表达式表示 。
如果不用 ORG规定,则汇编得到的目标程序将从 0000H开始 。
一个源程序中,可多次使用 ORG指令以规定不同程序段的起始位置。地址应从小到大顺序排列,不允许重叠。
例如,ORG 0000H
LJMP MAIN
ORG 4200H
MAIN,MOV A,30H
2,DB( Define Byte) 字节定义伪指令功能:用于从指定的地址单元开始,在程序存储器中定义字节数据 。
格式,[标号,] DB 8位数表例如,DB,how are you?”
常使用本命令存放数据表格,例如存放数码管显示的十六进制数的字形码,可使用多条 DB命令定义:
DB C0H,F9H,A4H,B0H
DB 99H,92H,82H,F8H
DB 80H,90H,88H,83H
DB C6H,A1H,86H,84H
3,DW( Define Word) 字定义伪指令功能:用于从指定地址开始,在程序存储器单元中定义 16位的数据字 。
格式,[标号,] DW 16位数表存放规则:高 8位在前 ( 低地址 ),低 8位在后 ( 高地址 ) 。
例:
DW 100H,1ACH,-814 ;按顺序存 01H,00H,
01H,ACH,D2H,FCH
DW,AA” ;存入 41H,41H
DW,A” ;存入 00H,41H
DW,ABC” ;不合法,因是两个字节查表时,为确定数据区的起始位置,可采用两种方法:
( 1) 根据 DB命令前一条指令的地址确定 。
例,8100H MOV A,#49H ( 1字节 )
TAB,DB C0H,F9H,A4H,B0H
( 2) 使用 ORG指令专门规定例,ORG 8100H
TAB,DB C0H,F9H,A4H,B0H
定义的数码管字形码从 8100地址开始存放 。
注意,DB和 DW定义的数表,数的个数不得超过 80个。
如果数据的数目较多时,可使用多个定义命令。
在 MCS-51程序设计应用中,常以 DB定义数据,
以 DW定义地址。
4,DS( Define Storage) 定义存储区伪指令功能:用于从指定地址开始,保留指定数目的字节单元为存储区,供程序运行使用 。 汇编时对这些单元不赋值 。
格式,[标号,] DS 16位数表例,ADDRTABL,DS 20
例,ORG 8100H
DS 08H
注意:对 MSC-51单片机来说,DB,DW,DS命令只能对程序存储器使用,不能对数据存储器使用 。
5,EQU( Equate) 赋值伪指令功能:用于给字符名称赋予一个特定值 。 赋值以后,其值在整个过程中有效 。
格式:
字符名称 EQU 赋值项
,赋值项,,可以是常数,地址,标号或表达式 。
8位或 16位二进制数 。 赋值以后的字符名称即可作地址使用,也可作立即数使用 。
PA8155 EQU 8001H
给标号 PA8155赋值 8001H。
6,BIT 定义位地址符号伪指令功能,用于给字符名称赋以位地址 。
格式,字符名称 BIT 位地址例如:
AAA BIT 40H
BBB BIT 50H
其中,位地址,可以是绝对地址,也可以是符号地址 。
7,END( END of assembly) 汇编终止命令功能:用于终止源程序的汇编工作。
END之后的指令,汇编程序不予处理。
格式:
[标号,] END [表达式 ]
只有主程序模块才有,表达式,项,且,表达式,的值等于该程序模块的入口地址 。 而其他程序模块就没有,表达式,项
。,标号:,也是选择项 。
一个源程序只能有一个 END命令 。
4.1.2 程序的设计步骤与方法汇编和高级语言程序设计的过程:
首先对问题进行分析,然后确定算法,再根据算法流程编写程序,最后是调试程序 。
汇编语言程序设计的 独特点 在于:
( 1) 用汇编语言进行程序设计时,对 数据的存放,寄存器和工作单元的使用等要由设计者安排 。 而高级语言程序设计时,这些工作都由计算机软件安排,程序设计者不必考虑 。
( 2) 汇编语言程序设计要求设计人员必须对所使用的计算机的硬件结构有较为详细的了解 。特别是对 各类寄存器、端口、定时器 /计数器、中断等 内容要熟悉,
以便在程序设计时能熟练使用。
汇编语言程序的设计步骤:
( 1)分析问题,抽象出描述问题的数学模型
( 2)确定解决问题的算法
( 3)根据算法绘制流程图
( 4)分配存储空间及工作单元
( 5)编写源程序
( 6)上机调试程序质量的判断标准:
程序的执行时间短;占用的内存单元少;程序的语句行数少。
4.1.3 汇编语言源程序的编辑和汇编
1,汇编语言源程序的编辑
编辑好的源程序应以,,ASM”扩展名存盘
2,汇编语言源程序的汇编
源程序经机器汇编之后,若无语法错误,一般将生成两个新文件,它们的扩展名分别是,OBJ和,LST。
例 4-1 将内部 RAM从 30H开始的 10个单元的内容相加,其和保存于 A(假设其和仍为 8位数)。
列表文件(,LST)信息如下:
地址 机器码 汇编语言源程序 注释
ORG 2000H ;定义程序起始地址
2000 7830 START,MOV R0,#30H ;初始地址送 R0
2002 7A0A MOV R2,#10 ;循环初始值送 R2
2004 7400 MOV A,#00H ;累加和初始值送 A
2006 26 LOOP,ADDC A,@R0 ;完成累加
2007 08 INC R0 ;修改地址
2008 DAFC DJNZ R2,LOOP ;循环判断
200A 80FE SJMP $ ;暂停
END ;程序结束
– INCLUDE IN HASHTAB
– START,2000
– LOOP,2006
目标文件(,OBJ)信息如下:
2000200C78307A0A74002608DAFC80FE
方法一:用逻辑指令将高,低半字节分开,对数字 0~9加上 30H即可得到所对应的 ASCII码 30H~39H。
ORG 2000H
MOV A,40H ;取数
ANL A,#0FH ;分离出低半字节
ADD A,#30H ;变为 ASCII码
MOV 42H,A ;保存
MOV A,40H ;重新取数
SWAP A ;高、低半字节交换
ANL A,#0FH ;分离出高半字节
ADD A,#30H ;变为 ASCII码
MOV 41H,A ;保存
END
例 4-2 将 40H单元的高、低半字节的两个 BCD码拆开并变成其 ASCII码,并分别存入 41H和 42H单元汇编语言程序共有四种结构形式:
顺序结构、循环结构、分支结构和子程序结构。
方法二:采用 BCD数除以 10H的方法,可把处于高、低半字节的两个 BCD
码分别移到 A和 B的低 4位,然后再各自与 30H相“或”即变为 ASCII码。参考程序如下:
ORG 2000H
MOV A,40H ;取数
MOV B,#10H ;送被除数
DIV AB ;分离高、低半字节
ORL B,#30H ;低 4位调整为 ASCII码
MOV 42H,B
ORL A,#30H ;高 4位调整为 ASCII码
MOV 41H,A
END
例 4-2 将 40H单元的高、低半字节的两个 BCD码拆开并变成其 ASCII码,并分别存入 41H和 42H单元例 求两个 8位无符号数的和 。
设两个 8位无符号数分别存放在内部 RAM 20H和 21H单元,所求和 ( 不超过 255) 存放在 22H单元 。
ORG 2000H
START,MOV R0,#20H
MOV A,@R0
INC R0
ADD A,@R0
INC R0
MOV @R0,A
SJMP $
END
4.2.5 循环程序设计循环初态循环程序一般由四个主要部分组成,
(1) 初始化部分,为循环程序做准备,如规定循环次数,给各变量和地址指针预置初值 。
(2) 处理部分,为反复执行的程序段,是循环程序的实体,也是循环程序的主体 。
(3) 循环控制部分,这部分的作用是修改循环变量和控制变量,并判断循环是否结束,直到符合结束条件时,跳出循环为止 。
(4) 结束部分,这部分主要是对循环程序的结果进行分析、
处理和存放。
循环:单重循环多重循环 ( 二重以上 ) --循环嵌套 。
在多重循环程序中,只允许外重循环嵌套内重循环程序,而不允许循环体互相交叉,另外,也不允许从循环程序的外部跳入循环程序的内部 。
例 4-3 把外部 RAM 5000H~50FFH单元的内容清零 。
ORG 2000H
START1,MOV DPTR,#5000H ;循环初始化
MOV R0,#00H
MOV A,#00H
LOOP1,MOVX @DPTR,A ;循环体
INC DPTR
INC R0
CJNE R0,#00H,LOOP1;循环控制
END
例 4-4 将内部 RAM从 40H开始连续单元的数据传送到外部 RAM从 2000H
开始的连续单元中,当所传送的数据为 0FFH时,传送停止。
解:
ORG 2000H
MOV R0,#40H ;循环初始化
MOV DPTR,#2000H
LOOP,MOV A,@R0
CJNE A,#0FFH,LOOP1 ;循环控制
SJMP NEXT
LOOP1:MOVX @DPTR,A ;循环体
INC R0
INC DPTR
SJMP LOOP
NEXT,SJMP $
END
例 4-5 从 P1口重复 256次读数并取平均值,平均值的整数和小数部分分别保存于内部 RAM的 30H和 31H单元中。
解:假设将 R2 R3作为 16位寄存器以保存连续 256次读数的累加和
ORG 4000H
INTEGER EQU 30H
DECIMAL EQU 31H
AVR,MOV R2,#0 ;循环初始化
MOV R3,#0
MOV R4,#0 ;累加次数为 256
AVR1,MOV A,P1 ;循环体
ADD A,R3 ;累加至低 8位
JNC AVR2 ;无进位则暂存结果
INC R2 ;有进位则高 8位加增 1
AVR2,MOV R3,A ;暂存低 8位
DJNZ R4,AVR1 ;循环控制
MOV INTEGER,R2
MOV DECIMAL,R3
END
例 设单片机外部数据存储器起始地址为 3000H的数据块中有
100个补码,要求逐一检查,若为负数则求补后放回,正数则不变 。 ORG 1000H
START1,MOV R7,#64H ;设置循环初始值
MOV DPTR,#3000H
LOOP1,MOVX A,@DPTR
JNB ACC.7,LOOP2
CPL A
INC A
MOVX @DPTR,A
LOOP2,INC DPTR ;外部 RAM单元加 1
DJNZ R7,LOOP1
END
例 有 10个无符号数依次存放在内部 RAM 30H开始的单元中,求其和,并将结果放在 R2和 R3中 。
ORG 2000H
MOV R0,#30H
MOV R2,#00H
MOV R3,#00H
MOV R7,#0AH
NEXT,MOV A,@R0 ;设置循环初始值
ADD A,R3
MOV R3,A
MOV A,R2
ADDC A,#00H
MOV R2,A
INC R0
DJNZ R7,NEXT
SJMP $
END
例 4.6 设 MCS-51单片机的时钟频率为 fosc = 12 MHz,试设计延时 50ms的延时程序 。
延时程序所花费的时间是该程序指令的总机器周期数与机器周期的乘积 。
通常,延时程序采用 MOV和 DJNZ指令来实现 。
单循环延时程序,最大的循环次数位 256,则程序段为:
MOV R0,#00H ;机器周期数为 1
DJNZ R0,$ ;机器周期数为 2
若单片机晶振为 12MHz,则一个机器周期为 1us。
延时时间:( 1+256× 2) × 1μ s= 513μ s。
需采用多重循环。
多重循环,在一个循环体中又包含了其它的循环程序 。
这种方式是实现延时程序的常用方法 。 使用多重循环时,必须 注意,
(1) 循环嵌套,必须层次分明,不允许产生内外层循环交叉 。
(2) 外循环可以一层层向内循环进入,结束时由里往外一层层退出 。
(3) 内循环可以直接转入外循环,实现一个循环由多个条件控制的循环结构方式 。
MOV R1,#M
LOOP,MOV R2,#N
NOP
DJNZ R2,$
DJNZ R1,LOOP
内层循环的机器周期数为 Tn=1+1+2*N,总机器周期数为 Tm=
( Tn+2)*M+1.设 N=123,M=200,则延时时间为 50.001ms。
修改后程序清单:
ORG 2000H
MOV R1,#0C8H(200)
LOOP,MOV R2,#7BH(123)
NOP
DJNZ R2,$
DJNZ R1,LOOP
SJMP $
END
补充例 设三字节无符号数相加被加数:内部 RAM 22H~ 20H单元 ( 低位在低字节 ),
加数,内部 RAM 32H~ 30H单元 ( 低位在低字节 ),
结果:存于内部 RAM 22H~ 20H单元,进位位存于 23H单元 。
利用 ADDC指令进行多字节加法运算 。
ORG 4200H
MOV R0,#20H ;被加数的低字节地址
MOV R1,#30H ;加数的低字节地址
MOV R2,#03H ;循环次数
CLR C
LOOP,MOV A,@R0
ADDC A,@R1 ;低字节相加
MOV @R0,A ;存放字节相加结果
INC R0
INC R1
DJNZ R2,LOOP ;循环控制
MOV A,#00H
ADDC A,#00H
MOV @R0,A ;进位位送 23H
END
4.2.3 分支程序分支程序的基本结构:单分支和多分支 。
其特点是:各处理模块是相互排斥的 。
13条条件转移指令,分别为,
JZ,JNZ,累加器判零转移指令;
CJNE,比较条件转移指令;
DJNZ,减 1条件转移指令 ;
JC,JNC,JB,JNB,JBC,位控制条件转移指令等四类。
(1)单重分支结构例 4-7 片内 RAM DATA1和 DATA2两个单元中各存有一个无符号数字,将两个数中的小者存入 MIN单元 。
ORG 4000H
MIN EQU 30H
DATA1 EQU 31H
DATA2 EQU 32H
MOV A,DATA1 ;第一数送 A
CLR C
CJNE A,DATA2,UNEQU ;两数比较
SJMP STORE ;相等,DATA1作为小的数
UNEQU,JC STORE ;有借位,DATA1为小
MOV A,DATA2 ;无借位,DATA2为小
STORE,MOV MIN,A ;小者送 RAM
END
例 4-8 片内 RAM两个单元中存有不相等的有符号数字 X和 Y,
比较两数的大小,并将大数存入 MAX单元。
若 X-Y> 0,则当( OV)= 0时,X> Y;
当( OV)= 1时,X< Y;
若 X-Y< 0,则当( OV)= 0时,X< Y;
当( OV)= 1时,X> Y。
图 4-6 例 4-8的流程图
X 送 MAX
X = Y?
Y
分支 0
N
( X - Y ) > 0?
Y
OV = 1?
N
OV = 1?
Y 送 MAX
N
N
Y
Y
例 4-8 片内 RAM两个单元中存有不相等的有符号数字 X和 Y,
比较两数的大小,并将大数存入 MAX单元。
ORG 1000H
XD EQU 31H
YD EQU 32H
MAX EQU 30H
CLR C
MOV A,XD
SUBB A,YD ; X-Y,形成 OV标志
JZ XMAX ;若 X=Y,则转向 FINISH
JB ACC.7,NEG ;若 (X-Y)<0,则转向 NEG
JB OV,YMAX ;若 (OV)=1,则转向 YMAX
SJMP XMAX ;若 (OV)=0,则转向 XMAX
NEG,JB OV,XMAX ;若 (OV)=1,则转向 XMAX
YMAX,MOV A,YD ; Y>X,存 Y
SJMP FINISH
XMAX,MOV A,XD ; X>Y,存 X
FINISH,MOV MAX,A ;大数送 MAX单元
END
( 2)多重分支结构例 设 x,y分别存放在内部 RAM 30H和 40H中,根据 x的值,
给 y赋值为 01H,00H,0FFH(-1).

0
1
1
y
0
0
0
x
x
x
ORG 2000H
MOV A,30H
JZ DONE
JB ACC.7,LOOP
MOV A,#01H
SJMP DONE
LOOP,MOV A,#0FFH
DONE,MOV 40H,A
SJMP $
END
(3)散转结构例 设 R7的内容为 0~ n,对应的处理程序入口地址分别为
PROG0~PROGn,编写散转程序 。
跳转方法:逐个比较,类似 CASE。
使用散转指令 JMP @A+DPTR 。
设( R7)= 0~ n,对应的处理程序入口地址分别为
PROG0~PROGn,且按照一定的规律排列 。
ORG 2000H
MOV DPTR,#TAB ;设置处理程序入口首地址
MOV A,R7
CLR C
RLC A ;
JNC NEXT
INC DPH
NEXT,JMP @A+DPTR ;转向形成的散转地址入口
TAB,AJMP PROG0 ;直接转移地址表
AJMP PROG1
……
AJMP PROGn
例 4-9 在内部 RAM 20H和 21H单元中有两个无符号的数,由 22H
中的值决定对该数完成加,减,乘或除运算 ( 20H单元的数为被减数或被除数 ),运算规则及结果保存处见表 4-1。
表 4-1 例 4-9说明
( 22H) 操作 结果保存处
0 加 30H(低字节),31H(高字节 )
1 减 40H
2 乘 50H(低字节),51H(高字节 )
3 除 60H(余数),61H(商 )
ORG 1000H
MOV A,22H
MOV B,21H
RL A
MOV DPTR,#TAB
JMP @A+DPTR
NOP
TAB,AJMP ADDM ;散转表
AJMP SUBM
AJMP MULM
AJMP DIVM
ADDM,MOV A,20H ;加法运算
ADD A,B
MOV 30H,A
MOV A,#0
ADDC A,#0
MOV 31H,A
SJMP FINISH
SUBM,MOV A,20H ;减法运算
SUBB A,B
MOV 40H,A
SJMP FINISH
MULM,MOV A,20H ;乘法运算
MUL AB
MOV 51H,B
MOV 50H,A
SJMP FINISH
DIVM,MOV A,20H ;除法运算
DIV AB
MOV 61H,A
MOV 60H,B
FINISH,END
4.2.4 子程序设计子程序与一般程序的主要区别是在子程序的末尾有一条子程序返回指令 ( RET),其功能是执行完子程序后通过将堆栈内的断点地址弹出到
PC而返回到主程序中 。
在编写子程序时应注意以下几点:
( 1) 要给每个子程序赋一个名字 。
实际上是一个入口地址的代号 。
( 2) 在子程序的末尾必须有子程序返回指令 RET。
(3)要能正确地传递参数 。
首先要有入口条件,说明进入子程序时它所要处理的数据如何得到,另外,要有出口条件,即处理的结果是如何存放的 。
( 4) 注意保护现场和恢复现场。
注意保存主程序和子程序共同涉及的,但值不同的累加器、寄存器和单元的内容。
保护现场,PUSH
恢复现场,POP
( 5) 注意子程序的通用性。
主程序调用子程序的指令:,LCALL”,,ACALL”。
子程序返回指令,RET。
子程序可以嵌套,嵌套次数从理论上说是无限的,但实际上由于受堆栈深度的影响,嵌套次数是有限的。
例 4-10 在图 3-11 的 P1口与 LED的连接示意图中,若使得
LED0~LED7依次点亮,其延时时间分别从 1s至 8s,LED7点亮之后又从 LED0开始循环。编写汇编语言源程序实现该功能 。
– ORG 1000H
MOV A,#1 ;主程序
MOV R4,#0
NEXT,INC R4
– MOV P1,A
– ACALL DELAY
RL A
SJMP NEXT
DELAY,MOV R7,#10 ; 1s延时子程序
DELAY3,MOV R6,#200
DELAY2,MOV R5,#125
DELAY1,DJNZ R5,DELAY1 ; 125× 4= 500μs= 0.5ms
DJNZ R6,DELAY2 ; 0.5 ms× 200= 0.1s
DJNZ R7,DELAY3 ; 0.1s× 200= 1s
DJNZ R4,DELAY
RET
END
例 4-11 两个无符号数据块的首地址分别为 30H和 40H,每个数据块的第一个字节都存放着数据块的长度(小于 15),求各数据块中最大值的乘积,并将结果存入 50H(乘积低字节)和 51H(乘积高字节)。
解:可将求最大值的过程编写成一个子程序,子程序的入口参数是数据块的首地址,存放在 R1中,返回参数即为最大值,存放在 A中,参考程序如下,ORG 1000H ;主程序
MOV R1,#30H ;置入口条件参数
ACALL FMAX ;调用求最大值子程序
MOV B,A ;第一个最大值存放于 B
MOV R1,#40H ;置入口条件参数
ACALL FMAX ;调用求最大值子程序
MUL AB ;求乘积
MOV 50H,A ;存乘积低字节
MOV 51H,B ;存乘积低高字节
SJMP $
ORG 1200H ;子程序
FMAX,MOV A,@R1 ;取数据块长度
MOV R2,A ; R2中存放数据块的长度
INC R1 ;改变地址指针
MOV A,@R1 ;将第一个数放入 A
DEC R2 ;数据个数减 1
LOOP1,INC R1 ;修改地址指针
CLR C
SUBB A,@R1 ;相减比较大小
JNC LOOP2 ; A中的数为大,跳向
MOV A,@R1 ;否则,更换大数到 A
SJMP LOOP3
LOOP2,ADD A,@R1 ;恢复原最大值 LOOP3:
LOOP3:DJNZ R2,LOOP1 ;若未比较完,则循环
RET
END
例,将 R0和 R1所指的内部 RAM中两个多字节无符号数相加,结果存入 R0所指的内部 RAM中 。
NADD:CLR C
NADD1:MOV A,@R0
ADDC A,@R1
MOV @R0,A
INC R0
INC R1
DJNZ R7,NADD1
JNC NADD2
MOV @R0,#01H
INC R0
NADD2:DEC R0
RET
4.3 常用程序设计举例
4.3.1 代码转换类程序计算机内部的运算一般都是用二进制,而在计算机与外设的数据传送中常采用 BCD码,ASCII码和其它代码,因此
,就存在代码转换的问题 。 在程序设计中常采用 算法处理和查表方式 来实现代码转换 。
1,十六进制数与 ASCII码之间的转换例 4-12 将从 30H单元开始的连续 8个单元中存放的十六进制数转换成其所对应的
ASCII码,并分别存放在从 40H开始的 16个单元中。
解,ORG 2000H
MOV R0,#30H ;设定地址指针
MOV R1,#40H
MOV R7,#8 ;循环次数
NEXT,MOV A,@R0 ;高字节转换
SWAP A
ANL A,#0FH
ACALL HEXAS
MOV @R1,A
INC R1
MOV A,@R0 ;低字节转换
ANL A,#0FH
ACALL HEXAS
MOV @R1,A
INC R1 ;修改地址指针
INC R0
DJNZ R7,NEXT
NOP
SJMP $
HEXAS,CLR C ;十六进制转换成 ASCII码子程序
SUBB A,#10
JC LOOP
ADD A,#7
LOOP,ADD A,#10 ;补偿减掉的 10
ADD A,#30H
RET
END
例 把外部 RAM 30H~3FH单元中的 ASCII码依次转换为十六进制数,并存入内部 RAM 60H~67H单元之中 。
假设,被转换的 ASCII为 十六进制数 ( 0~F)的 ASCII,则,
因为,0~9-- ASCII 30~39H
A~F-- ASCII 41~46H
若 ( 30H) = 41H-- A= 10
41H- 30H= 11H= 17
因为一个字节可装两个转换后得到的十六进制数,即两次转换才能拼装为一个字节。为了避免在程序中重复出现转换程序段,因此通常采用子程序结构,把转换操作编写为子程序。
ORG 5000H
MAIN,MOV R0,# 30H ;设置 ASCII码地址指针
MOV R1,# 60H ;设置十六进制数地址指针
MOV R7,# 08H ;字节个数
AB,ACALL TRAN ;调用转换子程序
SWAP A ; A高低字节交换
MOVX @R1,A
INC R0
ACALL TRAN ; 调用转换子程序
XCHD A,@R1 ;十六进制数拼装
INC R0
INC R1
DJNZ R7,AB
子程序 ( TRAN),
TRAN,CLR C
MOVX A,@R0 ;取 ASCII码
SUBB A,#30H
CJNE A,#0AH,BB
AJMP BC
BB,JC DONE
BC,SUBB A,#07H ;大于等于 0AH,再减 07H
DONE,RET
END
例 4-13 将两字节十六进制整数转换成三字节的 BCD码 。 若待转换的双字节十六进制整数在 R6,R7中 ( R6中为高位 ),转换后的三字节 BCD码整数存于 R3,R4和 R5
中 ( R3中为高位 ) 。
解:二进制数 b7b6b5b4b3b2b1b0B所对应的十进制数 X可按照下式计算因此,只要按照十进制运算法则,将 bi( i=7,6,…,1,0)按权相加,就可以得到相应的十进制数 X。参考程序如下:
ORG 2000H
HEBCD,MOV A,#0 ; BCD码初始化
MOV R3,A
MOV R4,A
MOV R5,A
MOV R2,#16 ;循环次数
NEXT,CLR C
MOV A,R7 ; R7右移一位并送回
RLC A
MOV R7,A
MOV A,R6 ; R6右移一位并送回
RLC A
MOV R6,A
MOV A,R5 ;( R5) × 2并调整为 BCD码
ADDC A,R5
DA A
MOV R5,A
MOV A,R4 ;( R4) × 2并调整为 BCD码
ADDC A,R4
DA A
MOV R4,A
MOV A,R3
ADDC A,R3
MOV R3,A ;若万位数不超过 6,则不用调整
DJNZ R2,NEXT ;处理完 16位了吗?
NOP
END
4.3.2 查表程序常用于非线性修正,非线性函数转换以及代码转换等 。
专用的查表指令:
MOVC A,@A+DPTR ;远程查表,64KB
通过 以下三步操作实现查表 。
·将所查表格的 首地址送入 DPTR;
·将要查找的 数据序号,即数据在表中的位置送入 累加器 A中 ;
·执行查表指令 MOVC A,@A+DPTR 进行读数并存结果存于累加器 A。
MOVC A,@A+PC ;近程查表,0~+256B
其实现查表也可通过以下三步操作来完成。
· 将要查找的 数据序号,即数据在表中的位置 送入累加器 A中;
· 把从 查表指令到表的首地址间的偏移量与 A值相加 ;
· 执行查表指令 MOVC A,@A+PC 进行读数,查表结果送累加器 A。
例 4-14 用查表指令编程实现将从 30H单元开始的连续 8个单元中存放的十六进制数转换成其所对应的 ASCII码,并分别存放在从 40H开始的 16个单元中 。
ORG 2000H
ASCTAB,DB 30H,31H,32H,33H,34H,35H,36H,37H; ASCII码表
DB 38H,39H,41H,42H,43H,44H,45H,46H
MOV R0,#30H ;设定地址指针
MOV R1,#40H
MOV R7,#8 ;循环次数
MOV DPTR,#ASCTAB
NEXT,MOV A,@R0 ;高字节转换
SWAP A
ANL A,#0FH
MOVC A,@A+DPTR ; 查表
MOV @R1,A
INC R1
MOV A,@R0 ; 低字节转换
ANL A,#0FH
MOVC A,@A+DPTR ; 查表
MOV @R1,A
INC R1 ;修改地址指针
INC R0
DJNZ R7,NEXT
NOP
END
例 4-15 在一个巡回检测系统中,需对 8路输入进行控制,每路都有一最大允许值,
为双字节数,且不全相同。控制时,需将输入值与最大值比较,若超过则进行报警。
编写一子程序使其能查找每路的最大允许值。
解:该查表运算中自变量 X是单字节,而因变量 Y是两字节的数。假设被检测路数存放在 R7中(入口参数),0~7路的最大允许值依次存放在 ROM中,查询得到的最大值存放在 30H(低字节)和 31H(出口参数)中,则参考程序如下:
ORG 1000H
MOV DPTR,#TABLE
MOV A,R7
RL A ;乘 2进行地址修正
MOV R7,A
MOVC A,@A+DPTR ;查表取高字节
MOV 31H,A
INC DPTR
MOV A,R7
MOVC A,@A+DPTR ;查表取低字节
MOV 30H,A
TABLE,DW 1245H,2345H,1022H,2390H ; 最大允许值表
DW 3421H,2388H,2366H,2147H
RET
例 若累加器 A中存放的是一位 BCD码 。 通过查表将其转换成为相应的七段显示码,并存入寄存器 B中 。
七段数码显示管连接方式:共阳极和共阴极两种 。
共阳极是低电平为有效输入,
共阴极为高电平为有效输入 。
假设数码显示管为共阴极 。
0~9的七段码为 3FH,06H,5BH,4FH,66H,6DH,
7DH,07H,7FH,6FH。
由于代码没有规律,一般采用查表完成 。
h g f e d c b a
0 0 1 1 1 1 1 1
a
ce
d
bgf
h
3FH
若以 DPTR为基址,程序段如下:
ORG 2000H
MOV A,#05H
MOV DPTR,#TAB
MOVC A,@A+DPTR
MOV B,A
SJMP $
TAB,DB 3FH,06H,5BH,4FH,66H,
DB 6DH,7DH,07H,7FH,6FH
… …
若以 PC为基地址,则程序段如下:
… …
MOV A,#05H
ADD A,#01H
MOVC A,@A+PC
NOP
TAB,DB 3FH,06H,5BH,4FH,66H,
DB 6DH,7DH,07H,7FH,6FH
… …
例 4-16 若内部 RAM 30H中存放的是一位 BCD码,通过查表将其转换成为相应的七段显示码,并存入寄存器 31H中。
解,0~9的共阴极字形代码为 3FH,06H,5BH,4FH,66H,6DH,7DH,
07H,7FH,6FH。
ORG 1000H
MOV A,30H
ADD A,#04H ;地址修正
MOVC A,@A+PC
MOV 31H,A ; 2字节指令
SJMP $ ; 2字节指令
TAB,DB 3FH,06H,5BH,4FH,66H
DB 6DH,7DH,07H,7FH,6FH
END
4.2.6 逻辑操作程序例 4-17 写出图 4-7所示电路的逻辑表达式,并编程实现其功能解:该电路图的最简逻辑表达式为,
ORG 1000H
X BIT P1.0 ;输入逻辑变量
Y BIT P1.1
Z BIT P1.2
F BIT P1.3 ;输出逻辑变量
EXOR,MOV C,X
ANL C,/Y
MOV F,C ;保存结果于 F位
MOV C,Y
ANL C,/X
ORL C,F
ANL C,Z
MOV F,C ; F←()
MOV C,X
ANL C,Y ; XY
ORL C,F
MOV F,C
END
图 4-7 例 4-17的逻辑电路图
=1 & ≥
1
1
X
Y
Z F
XYZ)YX(F YXYXYX
例 4-18 在图 3-7中,假设 K1,K2和 K3分别表示 X,Y和 Z三人对某一提案的表决,
当 K闭合时,表示其同意该方案;当 K断开时,表示不同意该方案。当多数人同意时,输出 F为 1。假设 P1.7连接一共阴极 LED,将结果用 LED显示出来。编程实现此功能。
解:根据数字电路中组合逻辑电路的设计知识,得出该逻辑电路的最简逻辑函数式是 F=XY+XZ+YZ,据此设计的参考程序如下:
ORG 1000H
X BIT P1.0
Y BIT P1.1
Z BIT P1.2
MOV C,X ;实现 XY
ANL C,Y
MOV 20H,C
MOV C,X ;实现 XZ
ANL C,Z
MOV 21H,C
MOV C,Y ;实现 YZ
ANL C,Z
ORL C,21H
ORL C,20H
MOV P1.7,C
NOP
END
例 设 P1.0~P1.3为准备就绪信号输入端,当该 4位输入全为 1时说明各项工作已准备好,单片机可顺序执行主程序,否则循环等待 。
ORG 2000H
LOOP:MOV A,P1
ANL A,#0FH
CJNE A,#0FH,LOOP
MAIN:
例 用软件实现逻辑函数 。
其中 X,Y,Z,W,F均为位变量,分别对应 P1.0,P1.1、
P1.2,P1.3和 P1.7。 由开关为 P1.0,P1.1,P1.2,P1.3输入值,
而由 P1.7输出该逻辑函数值 。
X Y ZX Y Z WZYXF
ORG 5000H
X BIT P1.0
Y BIT P1.1
Z BIT P1.2
W BIT P1.3
F BIT P1.7
TEMP BIT 20H.0
MOV C,X
ANL C,Y
ANL C,Z ; C←XYZ
MOV TEMP,C ;暂存 XYZ
ANL C,W
ORL C,TEMP ; C←XYZW + XYZ
MOV TEMP,C
MOV C,X
ANL C,/Y
ANL C,W ; C←
ORL C,/TEMP ; C←
MOV F,C ;输出最后结果
END
第 4 章 汇编语言程序设计思考题与习题
4-1 把外部 RAM 5000H的内容拆开,高位送 5001H,
低位送 5002H。
4-2 把外部 RAM 3000H开始的 100字节的内容传送到从外部 RAM 3500H开始的连续单元中。
4-3 设有 100个有符号数,连续存放在外部 RAM以
2000H为首地址的存储区中,编程统计其中正数、负数和零的个数,并分别存放在 R5,R6和 R7。
4-4 外部 RAM 1000H开始连续存放 20个双字节无符号数,低字节在前,高字节在后,编写求和程序,将结果存入内部 RAM 30H,31H,32H单元中。
4-5 编程计算片内 RAM40H~47H单元中无符号数的算术平均值(假设和仍为 8位数),结果存放在 50H(商)
和 51H(余数)中。
4-6 从内部 RAM 30H开始存有一无符号数据块,其长度在 2FH单元中。求出数据块中最小值和最大值并分别存入 R6
和 R7中。
4-7 将上题中“无符号数”改为“有符号数”,求出其中的最小值和最大值(参考例 4-8)。
4-8 从外部 RAM首地址为 1000H、长度为 50H的数据块中找出第一个 ASCII码‘ #’,并将其地址送到 1050H和 1051H
单元中,如果没有‘ #’,则使 1050H和 1051H单元的内容为 0。
4-9 把外部 RAM 30H~3FH单元中的 ASCII码依次转换为十六进制数,并存入内部 RAM 60H~67H单元之中。
4-10 假设晶振频率 fosc =6MHz,根据图 3-11的线路设计灯亮程序。要求将 8只发光二极管间隔地分两组,每组 4只,
两组交叉轮流发光,反复循环不止,每组灯亮持续时间为 1s。
4-11 用软件实现逻辑函数 。其中 X,Y,Z,W,F均为位变量,分别对应 P1.0,P1.1,P1.2,P1.3和 P1.7。由 P1.0、
P1.1,P1.2,P1.3输入值,而由 P1.7输出该逻辑函数值。
4-1 把外部 RAM 5000H的内容拆开,高位送 5001H,低位送 5002H。
MOV DPTR,#5000H
MOVX A,@DPTR
MOV R1,A
SWAP A
ANL A,#0FH
INC DPTR
MOVX @DPTR,A
MOV A,R1
ANL A,#0FH
INC DPTR
MOVX @DPTR,A
4-2 把外部 RAM 3000H开始的 100字节的内容传送到从外部
RAM 3500H开始的连续单元中。
MOV DPTR,#3000H
MOV R7,#100
LOOP:MOVX A,@DPTR
MOV R1,A
MOV A,DPH
ADD A,#5
MOV DPH,A
MOV A,R1
MOVX @DPTR,A
MOV A,DPH
CLR C
SUBB A,#05
MOV DPH,A
DJNZ R7,LOOP
4-3 设有 100个有符号数,连续存放在外部 RAM以 2000H为首地址的存储区中,编程统计其中正数、负数和零的个数,并分别存放在 R5、
R6和 R7。
MOV DPTR,#2000H
MOV R0,#100
MOV R5,#0 ;正数个数
MOV R6,#0 ;负数个数
MOV R7,#0 ;0个数
LOOP:MOVX A,@DPTR
JZ ZERO
JB ACC.7,NEG
INC R5
SJMP LOOP1
NEG:INC R6
SJMP LOOP1
ZERO:INCR7
LOOP1:DJNZ R0,LOOP
4-4 外部 RAM 1000H开始连续存放 20个双字节无符号数,低字节在前,高字节在后,编写求和程序,将结果存入内部 RAM 30H,31H,
32H单元中。
MOV DPTR,#2000H
MOV R7,#20
MOV 30H,#0
MOV 31H,#0
MOV 32H,#0
LOOP:MOVX A,@DPTR
ADD A,30H
MOV 30H,A
INC DPTR
MOVX A,@DPTR
ADD A,31H
MOV 31H,A
JNC LOOP1
INC 31H
LOOP1:INC DPTR
DJNZ R7,LOOP
4-5 编程计算片内 RAM40H~47H单元中无符号数的算术平均值(假设和仍为 8位数),结果存放在 50H(商)和 51H(余数)中。
MOV R0,#40H
MOV R7,#8
CLR C
LOOP:ADD A,#@R0
INC R0
DJNZ R7,LOOP
MOV B,#8
DIV AB
MOV 50H,A
MOV 51H,B
第 4 章 汇编语言程序设计
4-6 从内部 RAM 30H开始存有一无符号数据块,其长度在 2FH
单元中。求出数据块中最小值和最大值并分别存入 R6和 R7中 。
MOV R0,#30H
MOV A,@R0
MOV R6,A
MOV R7,A
DEC 2FH
LOOP:INC R0
CLR C
MOV A,R6
SUBB A,@R0
JNC MIN
CLR C
MOV A,R7
SUBB A,@R0
JNC LOOP1
MAX,MOV A,@R0
MOV R7,A
SJMP LOOP1
MIN:MOV A,@R0
MOV R6,A
LOOP1:DJNZ 2FH,LOOP
第 4 章 汇编语言程序设计
4-8 从外部 RAM首地址为 1000H、长度为 50H的数据块中找出第一个 ASCII码‘ #’,并将其地址送到 1050H和 1051H单元中,如果没有‘ #’,则使 1050H和 1051H单元的内容为 0。
– MOV DPTR,#1000H
– MOV R7,#50H
LOOP:MOVX A,@DPTR
– CJNE A,#23H,LOOP1
– SJMP DONE1
LOOP1:INC DPTR
– DJNZ R7,LOOP
– CLR A
– MOVX @DPTR,A
– INC DPTR
– MOVX @DPTR,A
– SJMP DONE
DONE1:MOV R1,DPL
– MOV R2,DPH
– MOV DPTR,#1050H
– MOV A,R1
– MOVX @DPTR,A
– INC DPTR
– MOV A,R2
– MOVX @DPTR,A
DONE:END
第 4 章 汇编语言程序设计
4-9 把外部 RAM 30H~3FH单元中的 ASCII码依次转换为十六进制数,并存入内部 RAM 60H~67H单元之中。
ORG 5000H
MAIN,MOV R0,# 30H ;设置 ASCII码地址指针
MOV R1,# 60H ;设置十六进制数地址指针
MOV R7,# 08H ;字节个数
AB,ACALL TRAN ;调用转换子程序
SWAP A ; A高低字节交换
MOVX @R1,A
INC R0
ACALL TRAN ; 调用转换子程序
XCHD A,@R1 ;十六进制数拼装
INC R0
INC R1
DJNZ R7,AB
第 4 章 汇编语言程序设计子程序 ( TRAN),
TRAN,CLR C
MOVX A,@R0 ;取 ASCII码
SUBB A,#30H
CJNE A,#0AH,BB
AJMP BC
BB,JC DONE
BC,SUBB A,#07H ;大于等于 0AH,再减 07H
DONE,RET
END
第 4 章 汇编语言程序设计
4-10 假设晶振频率 fosc =6MHz,根据图 3-11的线路设计灯亮程序。
要求将 8只发光二极管间隔地分两组,每组 4只,两组交叉轮流发光,
反复循环不止,每组灯亮持续时间为 1s。
– ORG 1000H
NEXT,MOV P1,#55H
– ACALL DELAY
– MOV P1,#0CCH
– ACALL DELAY
– SJMP NEXT
DELAY,MOV R7,#10 ; 1s延时子程序
DELAY3,MOV R6,#200
DELAY2,MOV R5,#125
DELAY1,DJNZ R5,DELAY1 ; 125× 4= 500μs= 0.5ms
DJNZ R6,DELAY2 ; 0.5 ms× 200= 0.1s
DJNZ R7,DELAY3 ; 0.1s× 200= 1s
RET
END
第 4 章 汇编语言程序设计
4-11 用软件实现逻辑函数 。其中 X,Y,Z,W,F均为位变量,
分别对应 P1.0,P1.1,P1.2,P1.3和 P1.7。由 P1.0,P1.1,P1.2,P1.3
输入值,而由 P1.7输出该逻辑函数值。
4.2.8 实用程序设计举例
( 1)排序类程序数据排序的算法有很多,常用的有冒泡排序法
、插入排序法、选择排序法、二路归并排序法等。
例 4.15:设在内部 RAM 30H单元连续存放了 8个单字节数据,编程实现按升序排列。
ORG 2000H
STAR:MOV R0,#30H
MOV R7,#07H
CLR 00H
LOOP:MOV A,@R0
MOV 2BH,A
INC R0
MOV 2AH,@R0
CLR C
SUBB A,@R0
JC NEXT
MOV @R0,2BH
DEC R0
MOV @R0,2AH
INC R0
SETB 00H
NEXT:DJNZ R7,LOOP
JB 00H,STAR
SJMP $
END
习题 4-7:设内部 RAM的 30H和 31H单元中有两个带符号数,编写程序求出大数并存入内部 RAM的 32H单元 。
ORG 4200H
MOV A,30H
XRL A,31H
JB ACC.7,L1
MOV A,30H
CLR C
SUBB A,31H
JNC L2
AJMP L3
L1,MOV A,30H
JNB ACC.7,L2
L3,MOV 32H,31H
AJMP L4
L2,MOV 32H,30H
SJMP $
END
习题 4-8:设 X为存放在内部 RAM 30H单元的无符号数,函数 Y存放在内部 RAM
40H单元,编写满足下列关系的程序,
ORG 4200H
MOV A,30H
CJNE A,#50H,LOOP
AJMP AA
LOOP,JNC AA
CJNE A,#20H,LOOP2
AJMP BB
LOOP2,JC CC
BB,MOV A,30H
RL A
RL A
ADD A,30H
MOV 30H,A
AJMP AA
CC,RL A
MOV 30H,A
AA,MOV 40H,30H
END
习题 4-9:编写程序,求出内部 RAM 50H单元的数据含,1”的个数,并将结果存入 51H单元,
MOV A,50H
MOV 51H,#0
MOV R1,#8
LOOP:RLC A
JNC LOOP1
INC 51H
LOOP1:DJNZ R1,LOOP
习题 4-10,编写程序,将内部 RAM 30H~3FH单元的内容清零,
ORG 4200H
MOV R0,#30H
MOV R7,#10H
LOOP,MOV @R0,#0
INC R0
DJNZ R7,LOOP
END
第 4 章 汇编语言程序设计习题 4-11:从内部 RAM 30H开始存有一无符号数据块,其长度在 2FH单元,
求出数据块中最小值并存入 30H单元,
ORG 4200H
MOV R0,#30H ;数据区首地址
MOV A,@R0 ;读第一个数
DEC 2FH
LOOP,INC R0
MOV 20H,@R0 ;读下一个数
CJNE A,20H,CHECK ;数值比较
CHECK,JC LOOP1 ; A值大转
MOV A,@R0 ;大数送 A
LOOP1,DJNZ 2FH,LOOP ;未完,继续
MOV 30H,A ;极值送 28H单元
END
习题 4-12,从外部 RAM 2000H单元起存放了 100个无符号数 。 统计其奇数和偶数的个数,并存放在内部 RAM 30H和 31H单元中 。
ORG 4200H
MOV DPTR,#2000H
MOV R7,#100H
MOV 30H,#0 ;QI
MOV 31H,#0 ;OU
LOOP,MOVX A,@DPTR
JB ACC.0,QI
INC 31H
AJMP AA
QI,INC 30H
AA,INC DPTR
DJNZ R7,LOOP
END
习题 4-13,将连续存放在外部 RAM 1000H开始的 100个单字节数据,传送到外部 RAM 2000H开始的连续地址单元中 。
ORG 4200H
MOV R7,#100
MOV DPTR,#1000H
LOOP,MOVX A,@DPTR
MOV R0,A
MOV A,DPH
ADD A,#10H
MOV DPH,A
MOV A,R0
MOVX @DPTR,A
MOV A,DPH
CLR C
SUBB A,#10H
MOV DPH,A
INC DPL
DJNZ R7,LOOP
END
习题 4-14,ORG 4200H
MOV R7,#20
MOV DPTR,#1000H
MOV 30H,#0
MOV 31H,#0
MOV 32H,#0
LOOP,MOVX A,@DPTR
ADD A,30H
MOV 30H,A
INC DPTR
MOVX A,@DPTR
ADDC A,31H
MOV 31H,A
JNC LOOP1
INC 32H
LOOP1:INC DPTR
DJNZ R7,LOOP
END
习题 4-17:编写程序,实现 ∑2× i( i=1~10) 。 并将结果存放在内部 RAM 50H单元中 。
ORG 4200H
MOV R0,#10
MOV 50H,#0
LOOP,MOV A,R0
RLA
ADD A,50H
MOV 50H,A
DJNZ R0,LOOP
END