第 4章 汇编语言程序设计
80C51汇编语言语句结构的基本格式伪指令的功能和应用汇编的概念程序设计的步骤和基本方法顺序程序分支程序循环程序查表程序散转程序本章要点标号:操作码 [目的操作数,源操作数 ];注释
§ 4-1 汇编语言程序设计基本概念
4.1.1 汇编语言及其语句结构汇编语言程序设计中的数计算机只能识别二进制数 —— 机器指令,它原本是不认识常用的十六进制数和十进制数的。
机器指令 /目标代码:
由 0/1代码组成的操作码与操作数。
BCD码:用二进制数表达的十进制数。
( 0~ 9表示为,0000~ 1001B 后缀,B/b,H/h)
二进制数:由 0/1组成、“逢 2进 1” 的数制。
如,01011110B ( 0~ 1 后缀,B/b)
十六进制数:便于读写记忆的二进制数的简写形式。
( 0~ 9,A~ F 后缀,H/h)
汇编语言编程基本规则汇编语言,用助记符描述的指令的集合。
√ 指令中以 A— F 开头的十六进制数前必须添一个
,0”。
√ 二进制数必须带后缀,B”或,b”;十六进制数必须带后缀,H”或,h”;十进制数的后缀是,D”或
,d”或无,
[标号,]操作码 [目的操作数 ][,源操作数 ][;注释 ]
汇编程序,汇编语言编写的程序借助编译工具编译成为目标代码,计算机才能识别。这个编译工具称为汇编程序。
4.1.2 伪指令在汇编时起控制作用,自身 并不产生 机器码,而仅是为汇编服务的一些指令,称为 伪指令 。伪指令不属于 80C51指令系统。
常用的伪指令有以下几种:
⒈ 起始伪指令 ORG(Origin)
功能:规定 ORG下面目标程序的起始地址 。
格式,ORG 16位 地址
ORG 0100H
START,MOV A,#05H
ADD A,#08H
MOV 20H,A
ORG 0100H表示该伪指令下面第一条指令的起始地址是 0100H,即 MOV A,#05H指令的第一个字节地址为 0100H,
或标号 START代表的地址为 0100H。
功能:将一个数据或特定的汇编符号赋予规定的字符名称。
⒉ 结束伪指令 END
功能:汇编语言源程序的结束标志。
在 END后面的指令,汇编程序不再处理 。
格式,END
⒊ 等值伪指令 EQU( Equate)
格式,字符名称 EQU 数据或汇编符号例如,PP EQU R0 ; PP=R0
MOV A,PP ; A R0
这里将 PP等值为汇编符号 R0,在指令中 PP就可以代替
R0来使用。
⒋ 数据地址赋值伪指令 DATA
格式,字符名称 DATA 表达式功能:将数据地址或代码地址赋予规定的字符名称。
⒌ 定义字节伪指令 DB( Define Byte)
格式,DB 8位二进制数表功能:从指定的地址单元开始,定义若干个 8位内存单元的数据。
数据与数据之间用“,”分割。
例如:
ORG 4000H
TAB,DB 73H,45,,A”,,2”
TAB1,DB 101B
以上指令经汇编后,将对 4000H开始的若干内存单元赋值。 (4000H)=73H,(4001H),2DH(注,45的 16
进制数 ),
格式,字符名称 BIT 位地址
⒍ 定义字伪指令 DW( Define Word)
格式,DW 16位二进制数表功能:从指定的地址单元开始,定义若干个 16位数据。
⒎ 定义位地址伪指令 BIT
功能:将位地址赋予所规定的字符名称。
AQ BIT P0.0
DEF BIT 30H
把 P0,0的位地址赋给字符 AQ,把位地址 30H赋给字符 DEF。在其后的编程中,AQ可作 P0.0使用,DEF可作位地址 30H使用。
三,汇编将汇编语言源程序转换为计算机所能识别的机器语言代码程序的过程称为汇编。
汇编可分为:
⒈ 手工汇编
⒉ 计算机汇编
【 例 4-1】 对下段程序进行手工汇编。
⑷ 汇编和调试四,程序设计的基本方法编写程序要求:
不仅要完成规定的功能任务,而且还要求:
执行速度快,占用内存少,条理清晰,
阅读方便,便于移植,巧妙而实用 。
一般应按以下几个步骤进行:
⑴ 分析问题,确定算法或解题思路
⑵ 画流程图
⑶ 编写源程序流程图符号和说明,
顺序程序是指按顺序依次执行的程序,也称为简单程序或直线程序。
顺序程序结构虽然比较简单,但也能完成一定的功能任务,是构成复杂程序的基础。
第二节 汇编语言程序设计举例一,顺序程序
CONT,MOV A,R0 ;读低 8位
CPL A ;取反
ADD A,#1 ;加 1
MOV R2,A ;存低 8位
MOV A,R1 ;读高 8位
CPL A ;取反
ADDC A,#80H ;加进位及符号位
MOV R3,A ;存高 8位
RET ;
【 例 4-2】 已知 16位二进制负数存放在 R1R0中,
试求其补码,并将结果存在 R3R2中。
解:二进制负数的求补方法可归结为“求反加 1”,符号位不变。利用 CPL指令实现求反;加 1时,则应低 8位先加 1,
高 8位再加上低位的进位。注意这里不能用 INC指令,因为
INC指令不影响标志 位。
程序如下:
ORG 2000H
BCD2B,MOV A,R2 ;取入口数据
ANL A,#0F0H ;取出十位
SWAP A ;高 4位 低 4位
MOV B,#0AH
MUL AB ;十位乘 10
MOV R3,A ;积暂存进 R3
MOV A,R2 ;再取入口数据
ANL A,#0FH ;取出个位
ADD A,R3
MOV R2,A ;结果 R2
RET ;若不是子程序可用 END
例,将 R2中 BCD码数转为二进制数存进 R2。
(此子程序在下一例中还要用到)
根据不同条件转向不同的处理程序,
这种结构的程序称为分支程序。
80C51指令系统中的 条件转移指令,
比较转移指令 和 位转移指令,可以实现分支程序。
二,分支程序
【 例 4,5】 已知 X,Y均为 8位二进制有符号数,
分别存在 30H,31H中,试编制能实现下列符号函数的程序:
实现程序如下,
【 例 4,6】 将 ASCII码转换为十六进制数。设 ASCII
码放在累加器 A中,转换结果放到 B中。
解:由 ASCII码表 (表 1-4)可知,30H~ 39H为
0~ 9的 ASCII码,41H~ 46H为 A~ F的 ASCII码。将
ASCII码减 30H(0~ 9)或 37H(A~ F)就可获得对应的十六进制数。程序如下:
① S0单独按下,
红灯亮,其余灯灭;
② S1单独按下,
绿灯亮,其余 灯灭;
③ 其余情况,
黄灯亮。
【 例 4.8】 已知电路如图 4-5所示,要求实现:
解:程序如下
SGNL:ANL P1,#11100011B;红绿黄灯灭
ORL P1,#00000011B;置 P1.0,P1.1输入态,P1.5~ P1.7状态不变
SL0,JNB P1.0,SL1 ;P1.0=0,S0未按下,转判 S1
JNB P1.1,RED ;P1.0=1,S0按下 ;且 P1.1=0,S1未按下,转红灯亮
YELW:SETB P1.4 ;黄灯亮
CLR P1.2 ;红灯灭
CLR P1.3 ;绿灯灭
SJMP SL0 ;转循环
SL1,JNB P1.1,YELW ;P1.0=0,S0未按下 ;P1.1=0,S1未按下,转黄灯亮
GREN:SETB P1.3 ;绿灯亮
CLR P1.2 ;红灯灭
CLR P1.4 ;黄灯灭
SJMP SL0 ;转循环
RED,SETB P1.2 ;红灯亮
CLR P1.3 ;绿灯灭
CLR P1.4 ;黄灯灭
SJMP SL0 ;转循环课堂练习题:
电路及灯亮灭要求同上述 【 例 】 题,其中第 3,4
两条指令 JNB P1.0和 JNB P1.1按下列要求修改,
试重新编程。
⑴ JB P1.0,?
JB P1.1,?
⑵ JB P1.0,?
JNB P1.1,?
⑶ JNB P1.0,?
JB P1.1,?
循环程序一般包括以下几个部分:
⑴ 循环初值;
⑵ 循环体;
⑶ 循环修改;
⑷ 循环控制;
以上四部分可以有两种组织形式,
其结构如 图 4-6所 示。
三,循环程序
【 例 4.10】 设 Xi均为单字节数,并按顺序存放在以 50H
为首地址的内 RAM存储单元中,数据长度(个数) N存在 R2中,试编程求和 S=X1+X2+‥‥ +XN,并将 S(双字节)存放在 R3R4中,(设 S< 65536)。
解:程序如下:
SXN,MOV R2,#N ; 置数据长度 (循环次数 )
MOV R3,#00H ; 和单元 (高 8位 )清 0
MOV R4,#00H ; 和单元 (低 8位 )清 0
MOV R0,#50H ; 求和数据区首址
LOOP:MOV A,R4 ; 读前次低 8位和
ADD A,@R0 ; 低 8位累加
MOV R4,A ; 存低 8位和
CLR A ;
ADDC A,R3 ; 高 8位加进位
MOV R3,A ; 存高 8位和
INC R0 ; 指向下一数据 循环修改
DJNZ R2,LOOP ; 判 N个数据累加完否? 循环控制
RET ; 退出循环 退出循环置循环初值循环体
【 例 4,11】 设在内 RAM 40H开始的存储区有若干个字符和数字,已知最后一个为字符,$”(并且只有惟一一个 ),
试统计这些字符和数字的个数,统计结果存人 30H单元。
解:程序如下:
【 例 4,12】 内部 RAM 20H单元开始存有 8个数,
试找出其中最大的数,送入 MAX单元。
解:程序如下:
ORG 1000H
DELAY,MOV R6,#200 ; 1Tm
LOOP1,MOV R7,#248 ; 1Tm
NOP ; 1Tm
LOOP2,DJNZ R7,LOOP2 ; 2Tm
DJNZ R6,LOOP1 ; 2Tm
RET ; 1Tm
补充例,软件循环延时 100mS(晶振 12MHz)
☆ 1Tm =1uS 100mS = 100000 uS
☆ MOV Rn #data 和 NOP 是单机器周期指令
☆ DJNZ Rn,rel 是双机器周期指令
☆ 200 = 0C8H ; 248 = 0F8H
1+1+
248x
2
=498
(498+2
)x200
ORG 1000H
DL1S,MOV R7,#10
DL1,MOV R6,#200
DL2,MOV R5,#250
DL3,DJNZ R5,DL3
DJNZ R6,DL2
DJNZ R7,DL1
RET
补充例,软件循环延时 1S(晶振 12MHz)
☆ 1Tm =1uS 1S = 1000000 uS
☆ MOV Rn #data 是单机器周期指令
☆ DJNZ Rn,rel 是双机器周期指令
1+
250x2
1+
[(1+250x2)
+2)]x200
1+
[(1+250x2+2)
x200+2]x10=
【 例 4-13(2)】 编写延时 10ms子程序,fosc=12MHz。
解,fosc=12MHz,一个机器周期为 1?s。
DY10ms:MOV R6,#20 ; 置外循环次数
DLP1,MOV R7,#250 ; 置内循环次数
DLP2,DJNZ R7,DLP2 ; 2机周 × 250 =500机周
DJNZ R6,DLP1 ; 500机周 × 20= 10000机周
RET ;
说明,MOV Rn指令为 1个机器周期 ;
DJNZ指令为 2个机器周期 ;
RET指令为 2个机器周期 ;
{[(2机周 × 250)+1+2]× 20+1+2}× 1?s/机周 =10063?s≈10ms
【 课堂练习题 】 按下列要求编写延时子程序:
⑴ 延时 2ms,fosc=6MHz;
⑵ 延时 5ms,fosc=12MHz;
⑶ 延时 10s,fosc=12MHz;
设 80C51单片机的 P1口作为输出口,经驱动电路
74LS240(8反相三态缓冲 /驱动器 )接 8只发光二极管,如图
4-8所示。当输出位为,1” 时,发光二极管点亮,输出位为
,0” 时为暗。试编程实现,每个灯 闪烁点亮 10次,再转移到下一个灯 闪烁点亮 10次,循环不止。
【 例 4.15】 编制一个循环闪烁灯的程序。
FLASH:MOV A,#01H ;置灯亮初值
FSH0,MOV R2,#0AH;置闪烁次数
FLOP,MOV P1,A ;点亮
LCALL DY1s ;延时 1s
MOV P1,#00H;熄灭
LCALL DY1s ;延时 1s
DJNZ R2,FLOP;闪烁 10次
RL A ;左移一位
SJMP FSH0 ;循环
RET ;
解:程序如下:
延时子程序可根据延时长短,自行编写。
【 课堂练习题 】
根据图 4-8电路,设计灯亮移位程序,
要求 8只发光二极管每次点亮一个,点亮时间为 250ms,顺序是从下到上一个一个地循环点亮。设 fosc=6MHz。
【 例 4.16】 已知 P1口数据每隔 10ms刷新一次,
试求其 1s内的平均值,平均值存 30H。
解:本题需求 100个数据的平均值,
一般有两种方法:
一种是全部累加后再平均;
另一种是边平均边累加,
现给出两种方法的程序。
AVRG1,MOV R2,#0 ;低 8位累加寄存器清 0
MOV R3,#0 ;高 8位累加寄存器清 0
MOV R4,#100 ;置平均次数
ALOP,MOV A,P1 ;读 P1口数据
ADD A,R2 ;低 8位累加
MOV R2,A ;回存
CLR A ;高 8位与进位累加
ADDC A,R3 ;
MOV R3,A ;回存
LCALL DY10ms ;延时 10ms
DJNZ R4,ALOP ;判 100次累加完否?未完继续
MOV A,R3 ;100次累加完,求平均值,被除数 → A,B
MOV B,R2 ;
MOV R0,30H ;置商间址
MOV R6,#0 ;置除数 100,除数 → R6R5
MOV R5,#100 ;
LCALL SUM ;(A,B)÷ (R6,R5)= 商 @R0,余数 A
CJNE A,#50,NEXT ;四舍五入
NEXT,JC GRET ;C=1,< 50,舍
INC 30H ;C=0,≥50,入
GRET,RET ;

全部累加后再平均注,SUM子程序可参阅例 4-9
AVRG2,MOV 30H,#0 ;商累加寄存器清 0
MOV 31H,#0 ;余数累加寄存器清 0
MOV R4,#100 ;置平均次数
ALOP,MOV A,P1 ;读 P1口数据
MOV B,#100 ;置除数 (平均次数 )
DIV AB ;P1口数据除以 100
ADD A,30H ;商累加
MOV 30H,A ;回存
MOV A,B ;
ADD A,31H ;余数累加
MOV 31H,A ;回存
CLR C ;
SUBB A,#100 ;
JC GON ;余数累加< 100,余数累加寄存器不变
INC 30H ;余数累加 ≥ 100,商累加寄存器 +1
MOV 31H,A ;减去 100后差 → 余数累加寄存器
GON,LCALL DY10ms ;延时 10ms
DJNZ R4,ALOP ;判 100次累加完否?未完继续
MOV A,31H ;100次累加完毕,余数累加四舍五入
CJNE A,#50,NEXT ;
NEXT,JC GRET ;C=1,< 50,舍
INC 30H ;C=0,≥50,入
GRET,RET ;

边平均边累加
【 课堂练习题 】
已知某单片机系统每隔 20ms测一次温度,8位温度 A/D值存在特殊功能寄存器 SBUF中,试分别求其 1s和 1分内的平均值,分别存 30H和 31H。
当用 PC作基址寄存器时,其表格首地址与 PC值间距不能超过 256字节,且编程要事先计算好偏移量,比较麻烦。 因此,一般情况下用 DPTR作基址寄存器 。
四,查表程序用于查表的指令有两条:
⑴ MOVC A,@A+DPTR
⑵ MOVC A,@A+PC
当用 DPTR作基址寄存器时,查表的步骤分三步:
① 基址值(表格首地址) → DPTR;
② 变址值(表中要查的项与表格首地址之间的间隔字 节数) → A;
③ 执行 MOVC A,@A+DPTR。
解:编程如下:
CHAG:MOV DPTR,#TABD;置共阴字段码表首址
MOV A,30H ;读显示数字
MOVC A,@A+DPTR ;查表,转换为显示字段码
MOV 30H,A ;存显示字段码
RET ;
TABD:DB 3FH,06H,5BH,4FH,66H;0~ 4共阴字段码表
DB 6DH,7DH,07H,7FH,6FH;5~ 9共阴字段码表
【 例 4.17】 在单片机应用系统中,常用 LED数码管显示数码,但显示数字 (≤ 9)与显示数字编码并不相同,
需要将显示数字转换为显示字段码,通常是用查表的方法。现要求将 30H中的显示数字转换为显示字段码并存入 30H。已知共阴字段码表首址为 TABD。
【 课堂练习题 】
已知 8位显示数字已存入首址为 30H
的内 RAM中,试将其转换为共阴显示字段码,存入首址为 40H的内 RAM中。
解:编程如下:
CUBE,MOV DPTR,#TAB ;置立方表首址
MOV A,30H ;读数据
ADD A,30H ;数据 × 2→A
MOV 30H,A ;暂存立方表数据序号
MOVC A,@A+DPTR;读立方数据高 8位
XCH A,30H ;存立方数据高 8位,立方表数据序号
→ A
INC A ;指向立方数据低 8位
MOVC A,@A+DPTR ;读立方数据低 8位
MOV 31H,A ;存立方数据低 8位
RET ;
TAB,DW 0,0,0,1,0,8,0,27,0,64 ;0~ 40立方表
DW 0,125,0,216,?,0FAH,00H ;
说明:数据 × 2→A 原因是立方表数据为双字节
【 例 4.19】 用查表程序求 0~ 40之间整数的立方。已知该整数存在内 RAM 30H中,查得立方数存内 RAM 30H(高 8位)
31H。已知立方表 (双字节 )首地址为 TAB。
在单片机系统中设置 +,?,?,?四个运算命令键,它们的键号分别为 0,1,2,3。当其中一个键按下时,进行相应的运算。操作数由 P1口和
P3口输入,运算结果仍由 P1口和 P3口输出。具体如下,P1口输入被加数、被减数、被乘数和被除数,输出运算结果的低 8位或商; P3口输入加数、
减数、乘数和除数,输出进位(借位)、运算结果的高 8位或余数。键盘号已存放在 30H中。
五,散转程序散转程序是 一种并行多分支程序。
【 例 4-20】 单片机四则运算系统。
解:程序如下:
PRGM,MOV P1,#0FFH ;P1口置输入态
MOV P3,#0FFH ;P3口置输入态
MOV DPTR,#TBJ ;置“+- ×÷,表首地址
MOV A,30H ;读键号
RL A ;键号?2→A
ADD A,30H ;键号?3→A
JMP @A+DPTR ;散转
TBJ,LJMP PRGM0 ;转 PRGM0(加法 )
LJMP PRGM1 ;转 PRGM1(减法 )
LJMP PRGM3 ;转 PRGM3(除法 )
LJMP PRGM2 ;转 PRGM2(乘法 )
PRGM0,MOV A,P1 ;读加数
ADD A,P3 ;P1+P3
MOV P1,A ;和 → P1
CLR A ;
ADDC A,#00H ;进位 → A
MOV P3,A ;进位 → P3
RET ;
PRGM1,MOV A,P1 ;读被减数
CLR C ;
SUBB A,P3 ;P1-P3
MOV P1,A ;差 → P1
CLR A ;
RLC A ;借位 → A
MOV P3,A ;借位 → P3
RET ;
PRGM2,MOV A,P1 ;读被乘数
MOV B,P3 ;置乘数
MUL AB ;P1× P3
MOV P1,A ;积低 8位 → P1
MOV P3,B ;积高 8位 → P3
RET ;
PRGM3,MOV A,P1 ;读被除数
MOV B,P3 ;置除数
DIV AB ;P1÷ P3
MOV P1,A ;商 → P1
MOV P3,B ;余数 → P3
RET ;
说明:由于 LJMP为 3字节指令,因此键号需先乘 3,
以便转到正确的位置。
OVER !