教学难点: 分支程序、散转程序与子程序的设计 第5章 汇编语言程序设计 教学目的:熟悉80C51系列单片机的汇编语言,能够熟练编写汇编语言源程序。 教学重点:常用汇编语言程序设计方法。 教学难点: 分支程序、散转程序与子程序的设计
5. 1 概述 5.1.1 程序设计语言 1. 机器语言 2. 汇编语言 图5-1 源程序、汇编程序、目的程序之间关系示意图 3. 高级语言
5.1.1 汇编语言规范 汇编语句格式与常用伪指令 1. 汇编语句格式 [标号:][指令助记符] [操作数][;注释] [标号:][指令助记符] [操作数][;注释] 不同仿真器提供的汇编环境对标号有不同的要求。但一般均应符合如下要求: 指令助记符、寄存器名、伪指令记忆符等不能作标号; 同一标号在一个独立程序中只能定义一次; 标号由1~8个ASCII字符组成,第一个字符必须是字母。
2. 80C51汇编程序设计中常用的伪指令 (1)ORG 汇编起始指令 规定目标程序的起始地址,格式如下: 标号 操作码 操作数 规定目标程序的起始地址,格式如下: 标号 操作码 操作数 ORG 表达式(exp) (2) EQU 赋值指令 把操作数段中的地址或数据赋值给标号,格式如下: 标号 操作码 操作数 字符名称 EQU 数或汇编符号 例1 AA EQU R1 ;R1与AA等值 则 MOV A, AA 指令与 MOV A,R1指令结果相同。
(3) DB定义字节指令 定义程序存储器中存放的8位常数表,格式如下: 操作码 操作数 DB 字节常数或ASCII字符 例2 ORG 1000H DB 76H,73,‘C’,‘B’ ;在表示ASCII字 符时要用‘ ’括号 DB OACH 则 (1000H)=76H (1001H)=49H (1002H)=43H (1003H)=42H (1004H)=0ACH
(4) DW 定义字指令 定义程序存储器中存放的16位常数表 格式 DW<16位数据表>。 例3 ORG 2200H DW 1246H,7BH,10 则 (2200H)=12H (2201H)=46H (2202H)=00 (2203H)=7BH (2204H)=00 (2205H)=0AH (5) BIT 定义位地址指令 格式 <字符名称>BIT<位地址> 例4 ABC BIT P1.0 Q4 BIT P2.2 则汇编后,位地址P1.0,P1.2分别赋给变量ABC和Q4。 (6) END 汇编结束指令
5.2 顺序与循环程序设计 5.2.1 顺序程序设计 例1 将20H单元的两个BCD码拆开并变成ASCII码,存入21H、22H单元。注意ASCII码0~9为30H~39H。 解:采用先把20H中低4位BCD码交换出来加以转换、存放,然后再把高4位BCD码交换至低4位加以转换、存放。
源程序如下: 地址 机器码 周期数 源程序 ORG 0000H 0000H 02 02 00 3 LJMP MAIN MAIN: 200H 78 22 1 MOV R0,#22 202H 76 00 1 MOV @R0,#0 204H E5 20 1 MOV A,20H 206H D6 1 XCHD A, @R0 207H 43 22 30 2 ORL 22H,#30H 20AH C4 1 SWAP A 20BH 44 30 1 ORL A,#30H 20DH F5 21 1 MOV 21H,A 20FH 80 FE 2 SJMP $ END
5.2.2 循环程序设计 例3 已知:80C51单片机使用的晶振为6MHz,要求设计一个软件延时程序,延时时间为10ms。 5.2.2 循环程序设计 例3 已知:80C51单片机使用的晶振为6MHz,要求设计一个软件延时程序,延时时间为10ms。 解:延时程序的延时时间主要与两个因素有关,一个是所用晶振,一个是延时程序中的循环次数。一旦晶振确定之后,则主要是如何设计与计算需给定的延时循环次数。在本题中已知晶振为6MHz,则可知一个机器周期为2s,可预计采用单重循环是有可能实现1ms的延时的。现根据题意编写源程序如下:
周期数 1 MOV R0,#0AH ;毫秒数R0 1 DL2: MOV R1,#MT ;1ms延时的预 定值MTR1 1 DL1: NOP 1 NOP 2 DJNZ R1,DL1 ;lms延时循环 2 DJNZ R0,DL2 ;毫秒数减1,不等于0,继续循环,等于0结束
例4 从22H单元开始有一个无符号数据块,其长度在20H单元。求出数据块中最大值,并存入21H单元 ORG 200H CLR A ;清A作为初始最大值 MOV R2,20H ;数据个数初值 MOV R1,#22H ;数据块首地址初值 LP:CLR C ;清进位 SUBB A,@R1 ;最大值减队列中数 JNC NEXT ;小于最大值继续 SJMP NEXT1
SUBB A,@R1 ;最大值减队列中数 JNC NEXT ;小于最大值继续 SJMP NEXT1 MOV A, @R1 ;大于最大值,则用此值代换 NEXT:ADD A, @R1 ;小于最大值,则恢复 NEXT1:INC R1 ;修改地址指针 DJNZ R2,LP ;依次重复比较,直至R2=0 MOV 21H ,A ;最大值存入21H单元
5.3 分支程序设计 5.3.1 分支程序设计综述 分支程序应用要点是正确使用转移指令,通常有如下3种指令。 1. 无条件转移 5.3 分支程序设计 5.3.1 分支程序设计综述 分支程序应用要点是正确使用转移指令,通常有如下3种指令。 1. 无条件转移 2. 条件转移 3. 散转
例3 设5AH单元中有一变量X,请编写计算下述函数式的程序,结果存入5BH单元 5.3.2 无条件/条件转移程序 例3 设5AH单元中有一变量X,请编写计算下述函数式的程序,结果存入5BH单元 Y= 3X , X < 10 Y= 2X +10 , 10= <X= <15 Y= 40 , X >15
ORG 200H MOV A,5AH ADD A,5AH ;2X→A MOV R1,A MOV A,5AH ;重新把X装入A CJNE A,#10,L1 L1:JC L2 ;X<10转L2 MOV R0,#40 ;先假设X>15 CJNE A,#10H,L3 ;与16比 L3:JNC L4 ;X>15转L4 MOV A,R1 ADD A,#10 ;10≤X≤15,Y=2X+10
MOV R0,A SJMP L4 L2: MOV A,R1 ADD A,5AH ;X<10,Y=3X L4 : MOV 5BH,R0 ;存结果 SJMP $ END
5.3.3 散转程序设计 例2 根据R3的内容,转向各个操作程序。 R3=0, 转入OPR0 R3=1, 转入OPR1 ………. R3=n, 转入OPRn 解:程序清单如下。 MOV DPTR,#TAB1 ;跳转表首地址送数据指针 MOV A,R3 ;R3×2 A(修正变址值) ADD A,R3 JNC NOAD ;判有否进位 INC DPH ;有进位则加到高字节地址 NOAD: JMP @A+DPTR ;转向形成的散转地址入口 TAB1: AJMP OPR0 ;转移到OPR0 AJMP OPR1 …….. AJMP OPRn
5.4 查表程序设计 5.4.1 查表程序综述 为了实现查表功能,在80C51汇编语言中专门设置了两条查表指令: MOVC A, @A+DPTR MOVC A, @A+PC 这2条指令特点不同,在应用时要注意区别。 为了便于查表,要求表中的数或符号按照便于查找的次序排列,并将它存放在从指定的首地址(或称基地址)开始的存储单元。
例1 设计一个将16进制数转换成ASCII码的子程序,设16进制数存放在R0的低4位,要求将转换后的ASCII码送回R0。 ORG 30H MOV A,R0 ANL A,#0FH ; 保留低4位 ADD A,#2 ;变址调整 MOVC A,@A+PC;查表获取ASCII码值 MOV R0,A RET TAB:DB 30H,31H,32H,33H,34H,35H DB 36H,37H,38H,39H,41H,42H,43H,44H,45H,46H
5.5 子程序设计 5.5.1 子程序结构与设计注意事项 子程序结构 编写子程序时的注意事项
5.5.2 子程序设计 例1 用程序实现c =a2+b2。设a、b均小于10。a存在31H单元中,b存在32H单元,把c存入33H单元。 解:因本题二次用到平方值,所以在程序中采用把求平方的程序段编为子程序的方法。依题意编写主程序和子程序如下:
地址 机器码 源程序 ORG 200H 200 75 81 3F MOV SP,#3FH ;设堆栈指针 地址 机器码 源程序 ORG 200H 200 75 81 3F MOV SP,#3FH ;设堆栈指针 203 E5 31 MOV A,31H ;取a值 205 12 04 00 LCALL SQR ;求a2 208 F9 MOV R1,A ;a2值暂存R1 209 E5 32 MOV A,32H ;取b值 20B 12 04 00 LCALL SQR ;求b2 20E 29 ADD A,R1 ;求a2+b2 20F F5 33 MOV 33H,A ;存入33H
; 子程序 地址 机器码 源程序 ORG 400H 400 04 SQR: INC A 401 83 MOVC A,@A+PC 401 22 RET 403 1 4 9 16 TAB: DB 0,1, 4, 9,16 408 25 36 49 DB 25,36,49 40B 64,81 DB 64,81
5.6 综合编程举例 例1 将R0所指出单元中的ASCII码转换成十六进制数,并把结果仍存于原单元中. 解:对于小于、等于 9的数,ASCII代码减去30H得一位十六进制数,对于大于9的ASCII代码减去37H,则恰好是“0AH~0FH”的结果。 编程如下: HEX: MOV A,@R0 ;取操作数 CLR C SUBB A,#30H ;0~9的转换 MOV @R0,A ;暂存结果 SUBB A,#0AH ;结果是否大于9 JC SB2 ;小于、等于9,则 返回 XCH A,@R0 SUBB A,#07H ;大于9,则减37H MOV @R0,A ;存结果 SB2: RET
例2 P1.0端口输出1kHZ和2kHZ的变频音调,每隔1S交替变换一次。 DLV:MOV R2,#08 ; 1kHZ的持续时间 DLV1:MOV R3 ,#0FAH DLV2:CPL P1.0 ; 输出1kHZ方波 LCALL D0.5ms ; 延时D0.5ms,省略 DJNZ R3,DLV2 DJNZ R2,DLV1 ; 持续1s DLV3: MOV R3,#0FAH DLV4: CPL P1.0 ;输出2kHZ方波 LCALL D0.25ms DJNZ R3,DLV4 DJNZ R2,DLV3 ;持续1s SJMP DLV ; 反复循环