第四章 汇编语言程序设计
4.1 汇编语言 4.1.1 计算机的程序设计语言种类 ● 机器语言 ● 汇编语言 ● 高级语言
一、机器语言 是一种能被计算机直接识别和执行的机器级语言。 有两种二进制和十六进制两种表示形式。 不易被人们识别和读写,不方便进行程序设计。
二、汇编语言 是一种代替机器语言进行程序设计的语言,由助记符、保留字和伪指令等组成,易被人们识别和读写。 采用汇编语言编写的程序叫做汇编语言程序,不能直接被计算机执行,必须通过编译软件翻译成机器语言程序(即目标代码)才能被计算机执行。 采用汇编语言,用户可直接操作到单片机内部的存储单元,适用于编写实时控制程序。
机器语言与汇编语言的形式 地址 机器语言 汇编语言 二进制形式 十六进制 2000H 0111010000000101B 7405H MOV A, #05H 2002H 0010010000001010B 240AH ADD A, #0AH 2004H 1111010100100000B F520H MOV 20H, A 2006H 1000000011111110B 80FEH SJMP $
三、高级语言 相对于汇编语言,高级语言接近自然语言和数学公式的编程,与计算机的硬件结构及指令系统无关,能更好的描述各种算法,方便学习和编程。 高级语言编译生成的程序代码一般比用汇编程序语言设计的程序代码要长,执行的速度也慢。所以汇编语言适合编写一些对速度和代码长度要求高的程序和直接控制硬件的程序。
标号: 操作码 操作数 ;注释 标号: 语句地址的标志符号,有如下规定 4.1.2 汇编语言格式 长度为1~8个ASCII字符; 4.1.2 汇编语言格式 标号: 操作码 操作数 ;注释 标号: 语句地址的标志符号,有如下规定 长度为1~8个ASCII字符; 第1个字符必须是字母,其余为字母、数字或其他特定字符; 不能使用系统关键字,如指令符、寄存器名等; 后边必须跟冒号
2. 操作码 ● 表示指令的操作功能 ● 为便于记忆,通常用与操作相应的英文缩写表示,如加法用ADD,减法用SUBB,传送用MOV等,编译时还原成一定的二进制代码。
3. 操作数 无操作数 —— 操作数隐含在操作助记符中 单操作数 两操作数 三操作数 ● 指参加操作的数据或数据的地址 ● 操作数的个数 ● 在两操作数的指令中,通常目的操作数写在左边,源操作数写在右边。 ● 操作数之间用逗号隔开。
MOV A, #00110101B ;A←53 ADD A, #20 ;A←53+20 MOV R0, #20H ;R0←20H ● 操作数的二进制、十进制和十六进制形式 MOV A, #00110101B ;A←53 ADD A, #20 ;A←53+20 MOV R0, #20H ;R0←20H MOV R1, # 0BFH ;R1←BFH
4. 注释 ● 用于注解指令或程序,便于编写和阅读程序; ● 任选项; ● 以分号开头,一行不够写而需另起一行时也必须以分号开头。
指令性语句 指示性语句 4.1.3 汇编语言的指令构成 第三章所讲的111条指令的助记符语句,通过编译软件汇编成目标代码(机器码)。 4.1.3 汇编语言的指令构成 指令性语句 第三章所讲的111条指令的助记符语句,通过编译软件汇编成目标代码(机器码)。 指示性语句 又称为伪指令,在汇编时不产生机器码,主要用来完成一些特殊操作。
常用伪指令 1、ORG 起始汇编 定位程序的起始地址。用于指明程序段或数据段从ROM的什么位置开始存放。 如 ORG 0000H 在一个程序中,可以多次使用ORG规定不同程序段或数据段存放的起始地址,但要求地址值由小到大依序排列,不允许空间重叠。
2、END 结束汇编 表示汇编语言源程序结束。 注意: 一个源程序只能有一个END语句,必须放在整个程序的末尾。
3、EQU 赋值伪指令 调用格式为 字符名称 EQU 数值或汇编符号 把EQU右边的“数据或汇编符号”赋给左边的“字符名称”。 “字符名称”必须先赋值后使用,故该语句通常 放在源程序的开头。
ORG 0000H AA EQU R1 A10 EQU 10H MOV A, AA ; AA当做R1使用 MOV R0,A10 ; A10为8位直接地址 . . . END
从指定单元开始定义(存储)若干个字节的数据或ASCII码字符,常用于定义数据常数表。 4、DB 字节定义伪指令 从指定单元开始定义(存储)若干个字节的数据或ASCII码字符,常用于定义数据常数表。 12H DEH 35H 41H 61H 3000H 3001H 3002H 3003H 3004H 例: ORG 3000H TAB1: DB 12H,0DEH DB ‘5’,‘A’, ‘a’
主要用来定义16位地址,高8位在前,低8位在后。 5、DW 字定义伪指令 与DB相似,但用于定义字数据。 一个字在存储器中占两个字节。 主要用来定义16位地址,高8位在前,低8位在后。 12H 34H 56H 78H 3000H 3001H 3002H 3003H 例 ORG 3000H TAB2:DW 1234H,5678H
用在存储器中预留一定数量的字节单元,为以后存放数据。 6、DS 定义存储空间伪指令 格式为 : DS 表达式 用在存储器中预留一定数量的字节单元,为以后存放数据。 预留的单元数目由表达式的值决定。 12H 34H -- 35H 3000H 3001H 3002H 3003H 3004H 3005H 3006H 例 ORG 3000H TAB1: DB 12H,34H DS 4H DB '5'
7、BIT 位地址赋值伪指令 格式为 : 字符名称 BIT 位地址 用于给位地址赋予符号,经赋值后可用该符号代替BIT后面的位地址。 例: ABC BIT P1.1 QQ BIT P3.2
4.1.4 汇编语言程序中的文件 源程序文件 汇编语言源程序是由汇编指令和伪指令组成的文件,其扩展名为 .ASM 源程序编辑完后只有转换为机器码表示的目标程序单片机才能执行,这个转换的过程称为汇编。 目标程序文件 扩展名为 .HEX
4.2 程序设计方法及技巧 4.2.1 编程步骤 1. 任务分析(硬件、软件系统分析) 2. 确定算法和工作步骤 3. 程序总体设计和流程图绘制 4.分配内存,确定程序与数据区存放地址 5. 编写源程序 6. 调试、修改,最终确定程序。
流程图符号 流程图是编程的基础和条件,绘制一个好的流程图,是程序设计的一项重要内容。 开始 对于简单的应用程序,可以不画流程图。但是当程序较为复杂时,绘制流程图是一个良好的编程习惯。 开始 处理框 判断 程序流向 结束
4.3 基本的程序结构 顺序程序 分支程序 循环程序 查表程序 子程序
4.3.1 顺序程序 顺序程序执行流程是按指令在存储器中的存放顺序进行的。
例1 :内部RAM的2AH~2EH单元中存储的数据如图所示。试编写程序实现图示的数据传送结果。 一、数据传送 例1 :内部RAM的2AH~2EH单元中存储的数据如图所示。试编写程序实现图示的数据传送结果。 MOV A,2EH MOV 2EH,2DH MOV 2DH,2CH MOV 2CH,2BH MOV 2BH,#00H 例2: 将片内RAM104、105单元中的内容分别传送到片外RAM 104H、105H 单元中。
ORG 0000H MOV R0,#68H MOV A,@R0 ;片内RAM104单元内容送累加器 MOV DPTR ,#0104H MOVX @DPTR,A ;完成片内RAM104单元内容送片 ;外104H单元中 INC R0 MOV A,@R0 ;片内RAM105单元内容送累加器 INC DPTR MOVX @DPTR,A ;完成片内RAM105单元内容送片 ;外105H单元中 SJMP $ ;等待 END ;结束
二、简单运算 由于51指令系统中只有单字节加法指令,因此对于多字节的相加运算必须从低位字节开始分字节进行。除最低字节可以使用ADD指令外,其他字节相加时要把低字节的进位考虑进去,这时就应该使用ADDC指令。 例3: 双字节无符号数加法。 设被加数存放在内部RAM的51H、50H单元,加数存放在内部RAM的61H、60H单元,相加的结果存放在内部RAM的51H、50H单元,进位存放在位寻址区的00H位中。
ROG 0000H MOV R0,#50H ;被加数的低字节地址 MOV R1,#60H ;加数的低字节地址 MOV A,@R0 ;取被加数低字节 ADD A,@R1 ;加上加数低字节 MOV @R0,A ;保存低字节相加结果 INC R0 ;指向被加数高字节 INC R1 ;指向加数高字节 MOV A,@R0 ;取被加数高字节 ADDC A,@R1 ;加上加数高字节(带进位加) MOV @R0,A ;存高字节相加结果 MOV 00H,C ;保存进位 。 SJMP $ ; 等待 END
4.3.2 分支程序 一. 简单分支程序 N Y 跳转至目标程序 条件满足? 顺序执行
分析:将两数相减,用进位标志C做为分支转 例4:已知片内RAM 40H 开始的两个单元中各存放有一个8位无符号二进制数,要求找出其中的大数并存入片内RAM 50H单元中。 分析:将两数相减,用进位标志C做为分支转 移的条件就很容易地找出其中的大数。
ORG 0000H CLR C MOV R0, # 40H ;第一个数地址送R0中 MOV A, @R0 ;取第一个数 INC R0 ;R0指向第二个数地址 SUBB A, @R0 ;两数比较 JC BIG ;第二个数大(C=1)转BIG DEC R0 ;R0指向第一个数地址 BIG: MOV 50H , @R0 ;存大数 SJMP $ ;等待 END ;结束
例5:设变量 x 以补码的形式存放在片内RAM的30H单元,变量 y 与 x 的关系是:当 x 大于0时,y =x;当 x =0时,y =20H;当 x 小于0时,y =x+5。编制程序,根据 x 的大小求y并送回原单元。 START: MOV A, 30H JZ NEXT ANL A, #80H ;判断符号位 JZ LP MOV A, #05H ADD A, 30H MOV 30H, A SJMP LP NEXT: MOV 30H, #20H LP: SJMP $
二. 多分支程序(又称为散转程序) 通常采用查散转表法,按分支号进行转移。 使用的指令是JMP @ A+DPTR 并配合 AJMP或LJMP
查散转表法的多分支程序结构: 散转表 各功能程序入口 …… MOV DPTR, #TABLE ; 散转表首地址(基址) RL A ; 分支号乘2形成变址值 JMP @A+DPTR TABLE: AJMP K0 AJMP K1 AJMP K2 AJMP K3 K0: …… K1: …… K2: …… K3: …… 散转表 各功能程序入口
注意: AJMP指令的转移范围不超出2KB字节空间,如各分支程序比较长,在2KB范围内无法全部存放,应改为LJMP(指令长度为3字节)。
4.3.3 循环程序 循环程序由4部分组成: 1) 循环初始化 设置循环的初始状态,位于循环程序的开头位置。 2)循环处理及修改 4.3.3 循环程序 循环程序由4部分组成: 1) 循环初始化 设置循环的初始状态,位于循环程序的开头位置。 2)循环处理及修改 循环程序的主体部分,是通过反复执行来完成数据的具体处理及修改,位于循环体内。
3)循环控制 也在循环体内,是用于控制循环的继续与否。 4)循环结束 通常位于循环体后,是用来存放循环处理的最终结果及恢复各寄存器与工作单元的原始值。 循环结构一般有两种: 先进入处理部分,再控制循环; 即先执行后判断。至少执行一次循环体 先控制循环,再进入处理部分; 即先判断后执行。循环体的执行取决于判断结果。
先判断后执行 先执行后判断 开始 开始 设置循环初值 设置循环初值 Y 循环结束? 循环处理 N 循环处理 循环修改 N 循环结束? 结束处理 结束 循环结束? Y N 开始 设置循环初值 循环处理 循环修改 结束处理 结束 循环结束? N Y
一. 先执行后判断 1、延时程序 若晶振频率为12MHz,则一个机器周期为1μs。 执行一条DJNZ指令需要2个机器周期,即 2μs。 采用循环计数法实现延时,循环次数可以通过计算获得,并选择先执行后判断的循环结构。 1) 单循环延时: DELAY: MOV R7,#10 ; 1T DJNZ R7,$ ; 2T △t=(2×10+1) ×1µs=21µs
2) 双重循环延时: DELAY: MOV R7,#0AH ;1T DL: MOV R6,#64H ;1T DJNZ R6,$ ;2T DJNZ R7,DL ;2T △t=[(2×100+2+1)×10+1]×1µs=2031µs
3) 三重循环延时: =1006031µs ≈ 1 秒 DELAY: MOV R7, #10 DL2: MOV R6, #200 3) 三重循环延时: DELAY: MOV R7, #10 DL2: MOV R6, #200 DL1: MOV R5, #250 DJNZ R5, $ DJNZ R6, DL1 DJNZ R7, DL2 △t=[(((2×250+2+1)×200)+2+1)×10+1] ×1µs =1006031µs ≈ 1 秒
例6: 将片外RAM 2000H~201FH最大值保存到2020H中。 2、求数据块的最大、最小、平均值 例6: 将片外RAM 2000H~201FH最大值保存到2020H中。 MOV R3,#32 ; 将数据块大小送给R3 MOV 30H,#00 ; 30H初始值为0,存放最大值 MOV DPTR,#2000H LOOP: MOVX A,@DPTR ; 取第一个单元里的数到A CJNE A,30H,L ; A≥(30H),C=0;否则C=1 L: JC NEXT; ; C=1,则A<(30H),比较下一个 MOV 30H,A ; C=0, (30H)←A NEXT: INC DPTR DJNZ R3,LOOP ; 判断循环是否结束? MOV A,30H MOVX @DPTR,A SJMP $
例7: 求片外RAM 2000H~201FH的平均值。 MOV R3,#5 CLR C LOOP1:MOV A,R5 RRC A MOV R5,A MOV A,R6 MOV R6,A DJNZ R3,LOOP1 MOV A,R5 SJMP $ MOV R3,#32 MOV R6,#0 MOV R5,#0 MOV DPTR,#2000H LOOP:MOVX A,@DPTR ADD A,R6 MOV R6,A MOV A,R5 ADDC A,#0 MOV R5,A INC DPTR DJNZ R3,LOOP
例8: 已知片内RAM 20H~2FH单元保存着一个长16B 的数据,20H单元为低字节部分。将该数据乘以2后再存回原单元。 MOV R3,#16 MOV R0,#20H CLR C LOOP: MOV A,@R0 RLC A MOV @R0,A INC R0 DJNZ R3,LOOP SJMP $
二. 先判断后执行 例9:将内部RAM中起始地址20H的数据串传送到外部RAM中起始地址1000H的存储区域内,直到发现‘$ ’字符停止传送。 由于循环次数事先不知道,但循环条件可以测试到。所以,采用先判断后执行的结构比较适宜。
MOV R0,#20H MOV DPTR,#1000H LOOP0: MOV A,@R0 CJNE A,#'$',LOOP1 ;判是否为‘ $ ’字符 SJMP LOOP2 ;是‘ $ ’字符,转结束 LOOP1: MOVX @DPTR,A ;不是‘ $ ’字符,执行传送 INC R0 INC DPTR SJMP LOOP0 ;传送下一数据 LOOP2: SJMP $
4.3.4 查表程序 例10: 查表法求Y=X2。设X(0≤X≤15)在片内RAM的20H单元中,要求查表求Y,存入片内RAM 21H单元。 4.3.4 查表程序 例10: 查表法求Y=X2。设X(0≤X≤15)在片内RAM的20H单元中,要求查表求Y,存入片内RAM 21H单元。 MOV DPTR, #3000H ;确定表首地址(基地址) MOV A, 20H ;取 X(变量:偏移量) MOVC A, @A+DPTR ;查表求Y=X2 MOV 21H, A ;保存Y(结果) SJMP $ ORG 3000H ;常数表格首地址 TAB: DB 00,01,04,09, … , 225 ;平方表
例11: 片内RAM30H~3FH中存放了0~F 16个数字, 根据下表进行数据转换,结果保存在40H~4FH中。 MOV R0,#30H MOV R1,#40H MOV R3,#16 MOV DPTR, #TAB LOOP:MOV A, @R0 MOVC A, @A+DPTR MOV @R1, A INC R0 INC R1 DJNZ R3,LOOP SJMP $ TAB: DB 5,9,F,3,0CH, 8,’A’,’a’,0BH,2,7,6,4,0DH,0EH, 1
小结: 1、对有规律可循的运算: (1)找规律写算法,编程序; (2)利用查表指令编程序。 2、对无规律可循的: 只能利用查表指令编程序,别无它法。
4.3.5 子程序 子程序: 指完成确定任务,并能为其它程序反复调用的程序段。 主程序:调用子程序的程序。 编写子程序时的注意事项: 1、主程序调用子程序时用ACALL 或LCALL指令; 2、子程序开始必须有标号,表明其入口地址。 3、子程序末尾用RET表示调用完毕,返回主程序; 4、保护现场与恢复现场; 5、参数传递: 入口参数:由主程序提供的原始参数; 出口参数:子程序执行后要返给主程序的结果。
一. 保护现场与恢复现场 当主程序调用子程序和从子程序返回主程序时,单片机能自动保护和恢复主程序的断点地址。 在子程序执行过程中常常要用到单片机的一些通用单元,如工作寄存器R0~R7、累加器A、数据指针DPTR,以及有关标志和状态等。而这些单元中的内容在调用结束后的主程序中仍有用,所以需要进行保护,称为保护现场。 子程序执行完后,返回主程序继续执行之前,要先恢复主程序中的原内容,称为恢复现场。
保护与恢复的方法有以下两种: 在主程序中实现; 在子程序中实现。
1、在主程序中实现 示例如下: 注意:保护与恢复的顺序要对应。 PUSH PSW ;保护现场 PUSH ACC PUSH B MOV PSW,#10H ;换当前工作寄存器组 LCALL addr16 ;子程序调用 POP B ;恢复现场 POP ACC POP PSW … … 注意:保护与恢复的顺序要对应。
2、在子程序中实现 示例如下: 注意:保护与恢复的顺序要对应。 SUB1:PUSH PSW ;保护现场 PUSH ACC PUSH B … … MOV PSW,#10H ;换当前工作寄存器组 POP B ;恢复现场 POP ACC POP PSW RET 注意:保护与恢复的顺序要对应。
二. 参数传递 例12: 对片内RAM某数据区清零 MAIN: MOV R0,#30H ;传送RAM数据区的起始地址 二. 参数传递 例12: 对片内RAM某数据区清零 MAIN: MOV R0,#30H ;传送RAM数据区的起始地址 MOV R7,#0AH ;传送RAM数据区的长度 ACALL SUBRT ;调用清零子程序 SJMP $ ;结束 QL: MOV A,#00H ;清零子程序 LOOP:MOV @R0,A INC R0 DJNZ R7,LOOP RET ◆ 通过寄存器或片内RAM传递参数