汇编语言程序设计.docx
《汇编语言程序设计.docx》由会员分享,可在线阅读,更多相关《汇编语言程序设计.docx(55页珍藏版)》请在冰豆网上搜索。
汇编语言程序设计
第4章汇编语言程序设计
程序设计是单片机应用系统设计的重要组成部分,计算机的全部动作都是在程序的控制下进行的。
本章用大量的实例来介绍单片机汇编语言程序设计方法。
实训4信号灯的控制2
1.实训目的
(1)掌握汇编语言程序的基本结构。
(2)了解汇编语言程序设计的基本方法和思路。
2.实训设备与器件
(1)实训设备:
单片机开发系统,微机等。
(2)实训器件与电路:
与实训1相同。
3.实训步骤与要求
(1)运行程序1,观察8个发光二极管的亮灭状态。
(2)在实训1的实训电路上面增加一个拨动开关,如图4.1所示。
图4.1实训中的开关电路
将拨动开关K0拨到+5V位置,运行程序2,观察发光二极管的亮灭状态;
将拨动开关K0拨到接地位置,运行程序2,观察发光二极管的亮灭状态。
(3)运行程序3,观察8个发光二极管的亮灭状态。
程序1:
所有发光二极管不停地闪动。
ORG0000H;程序从地址0000H开始存放
START:
MOVP1,#00H;把立即数00H送P1口,点亮所有发光二极管
ACALLDELAY;调用延时子程序
MOVP1,#0FFH;灭掉所有发光二极管
ACALLDELAY;调用延时子程序
AJMPSTART;重复闪动
DELAY:
MOVR3,#7FH;延时子程序
DEL2:
MOVR4,#0FFH
DEL1:
NOP
DJNZR4,DEL1
DJNZR3,DEL2
RET
END;汇编程序结束
程序2:
用开关控制发光二极管的显示方式。
ORG0000H
MOVP3,#00010000B;使P3口锁存器的P3.4置位
MOVA,P3;读P3口引脚线信号
ANLA,#00010000B;“逻辑与”操作,屏蔽掉无关位
JZDDPING;判断P3.4是否接地,若是,跳转到DDPING执行
MOVP1,#00H;否则,P3.4接高电平,点亮所有发光二极管
SJMP$
DDPING:
MOVP1,#55H;P3.4接地,发光二极管交叉亮灭
SJMP$
END
程序3:
使8个发光二极管顺序点亮。
ORG0000H
START:
MOVR2,#08H;设置循环次数
MOVA,#0FEH;送显示模式字
NEXT:
MOVP1,A;点亮连接P1.0的发光二极管
ACALLDELAY
RLA;左移一位,改变显示模式字
DJNZR2,NEXT;循环次数减1,不为零,继续点亮下面一个二极管
SJMPSTART
DELAY:
MOVR3,#0FFH;延时子程序开始
DEL2:
MOVR4,#0FFH
DEL1:
NOP
DJNZR4,DEL1
DJNZR3,DEL2
RET
END
4.实训分析与总结
(1)程序1的运行结果是:
8个发光二极管同时闪动,该程序的运行过程用流程图表示如图4.2所示。
程序1的执行过程是按照指令的排列顺序逐条执行,直到全部指令执行完毕为止。
这种按照指令的排列顺序逐条执行的程序结构称为顺序结构程序,如图4.2表示该程序结构的图称为程序流程图。
关于顺序程序的详细介绍参见4.2节。
图4.2程序1流程图
(2)程序2的运行结果是:
若开关K0接+5V,8个发光二极管全部处于点亮状态;若开关K0拨到接地状态,8个发光二极管处于“亮灭亮灭亮灭亮灭”状态。
程序2的流程图如图4.3所示。
图4.3程序2流程图
程序2的特点是:
程序不按照指令的排列顺序执行,而是根据开关K0的状态,分别执行不同的内容,即程序有两个分支,执行时根据给定的条件选择其中分支,这样的程序结构称为分支结构程序。
分支结构程序的关键是如何根据条件选择正确的分支。
关于分支结构程序的详细介绍参见4.3节。
(3)程序3的运行结果是:
顺序点亮8个发光二极管。
该程序的流程图如图4.4所示。
图4.4程序3流程图
程序3的特点是“点亮-延时-移位”这一程序段重复执行了8次,这样重复执行某一程序段的程序结构称为循环结构程序。
该程序的设计过程见例4.6。
关于循环程序结构的详细介绍参见4.4节。
(4)在程序1和程序3中都使用了一段相同的延时子程序DELAY,这种供其它程序反复使用的程序或程序段称为子程序。
关于子程序的详细介绍参见4.6节。
5.思考
在程序1和程序3中,如果去掉程序中ACALLDELAY,程序运行结果是否有变化,为什么?
如果想改变8个发光二极管的闪动或点亮速度,如何修改程序?
4.1概述
实训4中,我们使用的程序都是用单片机汇编语言设计的。
除了汇编语言外,单片机程序设计语言还有两类:
机器语言和高级语言。
机器语言(MachineLanguage)是指直接用机器码编写程序、能够为计算机直接执行的机器级语言。
机器码是一串由二进制代码“0”和“1”组成的二进制数据,执行速度快,但是可读性极差。
机器语言一般只在简单的开发装置中使用,程序的设计、输入、修改和调试都很麻烦,在实训1和实训3中直接固化或输入的程序都是机器语言程序。
汇编语言(AssemblyLanguage)是指用指令助记符代替机器码的编程语言。
程序结构简单,执行速度快,程序易优化,编译后占用存储空间小,是单片机应用系统开发中最常用的程序设计语言。
汇编语言的缺点是可读性比较差,只有熟悉单片机的指令系统,并具有一定的程序设计经验,才能研制出功能复杂的应用程序,实训4中的3个程序都是用汇编语言设计的。
高级语言(High-LevelLanguage)是在汇编语言的基础上用高级语言来编写程序,例如PL/M-51、FranklinC51、MBASIC51等,程序可读性强,通用性好,适用于不熟悉单片机指令系统的的用户。
高级语言编写程序的缺点是实时性不高,结构不紧凑,编译后占用存储空间比较大,这一点在存储器有限的单片机应用系统中没有优势。
目前,大多数用户仍然使用汇编语言进行单片机应用系统的软件设计,本章介绍MCS-51单片机汇编语言的程序设计方法。
单片机汇编语言程序设计的基本步骤如下:
(1)题意分析
熟悉并了解汇编语言指令的基本格式和主要特点,明确被控对象对软件的要求,设计出算法等。
(2)画出程序流程图
编写较复杂的程序,画出程序流程图是十分必要的。
程序流程图也称为程序框图,是根据控制流程设计的,它可以使程序清晰,结构合理,按照基本结构编写程序,便于调试,在实训4中,我们给出了3个实训程序的流程图。
(3)分配内存工作区及有关端口地址
分配内存工作区,要根据程序区、数据区、暂存区、堆栈区等预计所占空间大小,对片内外存储区进行合理分配并确定每个区域的首地址,便于编程使用。
(4)编制汇编源程序。
(5)仿真调试程序。
(6)固化程序。
4.2简单程序设计
简单程序也就是顺序程序,实训4中的程序1就是顺序程序结构,它是最简单、最基本的程序结构,其特点是按指令的排列顺序一条条地执行,直到全部指令执行完毕为止。
不管多么复杂的程序,总是由若干顺序程序段所组成的。
本节通过实例介绍简单程序的设计方法。
例4.1四字节(双字)加法。
将内部RAM30H开始的4个单元中存放的四字节16进制数和内部RAM40H单元开始的4个单元中存放的四字节16进制数相加,结果存放到40H开始的单元中。
(1)题意分析
题目的要求如图4.5所示。
图4.5例4.1题意分析示意图
(2)汇编语言源程序
按照双字节加法的思路,实现四字节加法的源程序如下:
ORG0000H
MOVA,30H
ADDA,40H
MOV40H,A;最低字节加法并送结果
MOVA,31H
ADDCA,41H
MOV41H,A;第二字节加法并送结果
MOVA,32H
ADDCA,42H
MOV42H,A;第三字节加法并送结果
MOVA,33H
ADDCA,43H
MOV43H,A;第四字节加法并送结果,进位位在Cy中
END
显然,上面程序中每一步加法的步骤很相似,因此我们可以采用循环的方法来编程,使得源程序更加简洁,结构更加紧凑。
用循环方法编制的源程序见习题4.3题。
例4.2数据拼拆程序。
将内部RAM30H单元中存放的BCD码十进制数拆开并变成相应的ASCII码,分别存放到31H和32H单元中。
(1)题意分析
题目要求如图4.6所示。
图4.6例4.2题意分析示意图
本题中,首先必须将两个数拆分开,然后再拼装成2个ASCII码。
数字与ASCII码之间的关系是:
高4位为0011H,低四位即为该数字的8421码。
(2)汇编语言源程序
ORG0000H
MOVR0,#30H
MOVA,#30H
XCHDA,@R0;A的低4位与30H单元的低4位交换
MOV32H,A;A中的数值为低位的ASCII码
MOVA,@R0
SWAPA;将高位数据换到低位
ORLA,#30H;与30H拼装成ASCII码
MOV31H,A
END
4.3分支程序设计
通常,单纯的顺序结构程序只能解决一些简单的算术、逻辑运算,或者简单的查表、传送操作等。
实际问题一般都是比较复杂的,总是伴随有逻辑判断或条件选择,要求计算机能根据给定的条件进行判断,选择不同的处理路径,从而表现出某种智能。
根据程序要求改变程序执行顺序,即程序的流向有两个或两个以上的出口,根据指定的条件选择程序流向的程序结构我们称为分支程序结构,实训4中的程序2就是这样的程序结构。
本节通过实例介绍分支程序设计方法。
4.3.1分支程序实例
1.两分支程序设计
例4.3两个无符号数比较(两分支)。
内部RAM的20H单元和30H单元各存放了一个8位无符号数,请比较这两个数的大小,比较结果显示在实训的实验板上。
如下:
若(20H)>=(30H),则P1.0管脚连接的LED发光;
若(20H)<(30H),则P1.1管脚连接的LED发光;
(1)题意分析
本例是典型的分支程序,根据两个无符号数的比较结果(判断条件),程序可以选择两个流向之中的某一个,分别点亮相应的LED。
比较两个无符号数常用的方法是将两个数相减X-Y,然后判断有否借位Cy,若Cy=0,无借位,X>=Y;若Cy=1,有借位,X程序的流程图如图4.7所示。
图4.7两数比较流程图
(2)汇编语言源程序
XDATA20H;数据地址赋值伪指令DATA
YDATA30H
ORG0000H
MOVA,X;(X)→A
CLRC;Cy=0
SUBBA,Y;带借位减法,A-(Y)-Cy→A
JCL1;Cy=1,转移到 L1
CLRP1.0;Cy=0,(20H)>=(30H),点亮P1.0连接的LED
SJMPFINISH;直接跳转到结束等待
L1:
CLRP1.1;(20H)<(30H),点亮P1.1连接的LED
FINISH:
SJMP$
END
(3)执行结果
执行该程序之前,利用单片机开发系统先往内部RAM的20H和30H单元存放两个无符号数(可以任意设定),执行后观察点亮的LED是否和存放的数据大小相一致。
2.三分支程序设计
例4.4两个有符号数比较(三分支程序)。
内部RAM的20H单元和30H单元各存放了一个8位有符号数,请比较这两个数的大小,比较结果显示在实训实验板上。
结果如下:
若(20H)=(30H),则P1.0管脚连接的LED发光;
若(20H)>(30H),则P1.1管脚连接的LED发光;
若(20H)<(30H),则P1.2管脚连接的LED发光。
(1)题意分析
有符号数在计算机中的表示方式与无符号数是不相同的:
正数以原码形式表示,负数以补码形式表示,8位二进制数的补码所能表示的数值范围为+127~-128。
计算机本身无法区分一串二进制码组成的数字是有符号数或无符号数,也无法区分它是程序指令还是一个数据。
编程员必须对程序中出现的每一个数据的含义非常清楚,并按此选择相应的操作。
例如:
数据FEH看作无符号数其值为254,看作有符号数为-2。
比较两个有符号数X和Y大小要比无符号数麻烦得多。
这里提供一种比较思路如下:
先判别两个有符号数X和Y的符号,如果X、Y两数符号相反,则非负数大;如果X、Y两数符号相同,将两数相减X-Y,根据借位标志Cy进行判断即可。
这一比较过程如图4.8所示。
图4.8两个有符号数X、Y比较流程图
(2)汇编语言源程序
XDATA20H
YDATA30H
ORG0000H
MOVA,X
XRLA,Y;(X)与(Y)进行异或操作
JBACC.7,NEXT1;累加器A的第7位为1,二数符号不同,转移到
;NEXT1
MOVA,X
CJNEA,Y,NEQUAL;(X)<>(Y),转移到NEQUAL
CLRP1.0;(X)=(Y),点亮P1.0连接的LED
SJMPFINISH
NEQUAL:
JCXXY;(X)<(Y),转移到XXY
SJMPXDY;否则,(X)>(Y),转移到XDY
NEXT1:
MOVA,X
JNBACC.7,XDY;判断(X)的最高位D7,以确定其正负
XXY:
CLRP1.2;(X)<(Y),点亮P1.2连接的LED
SJMPFINISH
XDY:
CLRP1.1;(X)>(Y),点亮P1.1连接的LED
FINISH:
SJMP$
END
(3)程序说明
①判断两个有符号数符号异同的方法
本例中使用逻辑异或指令,将(X)与(Y)进行异或操作,那么(X)的符号位(X)7与(Y)的符号位(Y)7异或,结果如下:
若(X)7与(Y)7相同,则(X)7⊕(Y)7 = 0
若(X)7与(Y)7不相同,则(X)7⊕(Y)7 = 1
本例中,(X)与(Y)的异或结果存放在累加器A中,因此判断ACC.7是否为零即可知道两个数的符号相同与否。
②比较两个有符号数的其它方法
除了本例中使用的比较两个有符号数的方法之外,我们还可以利用溢出标志OV的状态来判断两个有符号数的大小。
具体算法如下:
若X-Y为正数,则OV=0时X>Y
OV=1时X若X-Y为负数,则OV=0时XOV=1时X>Y
采用这种比较方式的汇编语言源程序见习题4.9。
3.散转程序
散转程序是指经过某个条件判断之后,程序有多个流向(三个以上)。
在后面的键盘接口程序设计中经常会用到散转功能——根据不同的键码跳转到相应的程序段。
例4.5在实训4程序2的基础之上,先设计两个开关,使CPU可以察知两个开关组合出的4种不同状态。
然后对应每种状态,使8个LED显示出不同的亮暗模式。
(1)硬件设计
在实训1的电路中,我们使用了单片机的并行口P1的输出功能来控制8个LED的显示,现在我们使用其P3口的输入功能设计两个输入开关,硬件原理图如图4.9所示。
图4.9在实训1原理图基础之上例4.5硬件原理图
如图4.9所示,当开关K0接通2时,P3.4管脚接地,P3.4=0;当K0接通1时,P3.4接+5V,P3.4=1。
同样,当开关K1接通2时,P3.5管脚接地,P3.5=0;当K1接通1时,P3.5接+5V,P3.5=1。
假设要求P3口的开关状态对应的P1口的8个LED的显示方式如下:
P3.5P3.4显示方式
00全亮
01交叉亮
10低四位连接的灯亮,高四位灭
11低四位连接的灯灭,高四位亮
(2)软件设计
①程序设计思想
散转程序的特点是利用散转指令实现向各分支程序的转移,程序流程图如图4.10所示。
图4.10散转程序流程图
②汇编语言源程序
ORG0000H
MOVP3,#00110000B;使P3口锁存器相应位置位
MOVA,P3;读P3口相应引脚线信号
ANLA,#00110000B;“逻辑与”操作,屏蔽掉无关位
SWAPA;将相应位移位到低位
RLA;循环左移一位,A*2→A
MOVDPTR,#TABLE;转移指令表的基地址送数据指针DPTR
JMP@A+DPTR;散转指令
ONE:
MOVP1,#00H;第一种显示方式,K0通,K1通
SJMP$
TWO:
MOVP1,#55H;第二种显示方式,K0断,K1通
SJMP$
THREE:
MOVP1,#0FH;第三种显示方式,K0通,K1断
SJMP$
FOUR:
MOVP1,#0F0H;第四种显示方式,K0断,K1通
SJMP$
TABLE:
AJMPONE;转移指令表
AJMPTWO
AJMPTHREE
AJMPFOUR
END
(3)程序说明
①读P3口的管脚状态
MCS-51的四个I/O端口共有三种操作方式:
输出数据方式,读端口数据方式和读端口引脚方式。
输出数据方式举例:
MOVP1,#00H;输出数据00H→P1端口锁存器→P1引脚
读端口数据方式举例:
MOVA,P3;A←P3端口锁存器
读端口引脚方式举例:
MOVP3,#0FFH;P3口端口锁存器各位置1
MOVA,P3;A←P3端口引脚状态
注意:
读引脚方式必须连续使用两条指令,首先必须使欲读的端口引脚所对应的锁存器置位,然后再读引脚状态。
图4.11指令转移表的存储格式
②散转指令JMP@A+DPTR
散转指令是单片机指令系统中专为散转操作提供的无条件转移指令,指令格式如下:
JMP@A+DPTR;PC←DPTR+A
一般情况下,数据指针DPTR固定,根据累加器A的内容,程序转入相应的分支程序中去,本例采用最常用的转移指令表法,就是先用无条件转移指令按一定的顺序组成一个转移表,再将转移表首地址装入数据指针DPTR中,然后将控制转移方向的数值装入累加器A中作变址,最后执行散转指令实现散转。
指令转移表的存储格式如图4.11所示。
由于无条件转移指令AJMP是两字节指令,因此控制转移方向的A中的数值为:
A=0转向AJMPONE
A=2转向AJMPTWO
A=4转向AJMPTHREE
A=6转向AJMPFOUR
程序中从P3口读入的数据分别为0、1、2、3,因此必须乘以2用来修正A的值。
如果A=2,散转过程如下:
JMP@A+DPTR→PC=TABLE+2→AJMPTWO
③三种无条件转移指令LJMP、AJMP和SJMP的比较
三种无条件转移指令在应用上的区别有以下三点:
一是转移距离不同,LJMP可在64Kbytes范围内转移,AJMP指令可以在本指令取出后的2Kbytes范围内转移,SJMP的转移范围是以本指令为核心的-126~+129bytes范围内转移;
二是汇编后机器码的字节数不同,LJMP是三字节指令,AJMP和SJMP都是两字节指令。
三是LJMP和AJMP都是绝对转移指令,可以计算得到转移目的地址,而SJMP是相对转移指令,只能通过转移偏移量来进行计算。
选择无条件转移指令的原则是根据跳转的远近,尽可能选择占用字节数少的指令。
例如动态暂停指令一般都选用SJMP$,而不用LJMP$。
4.3.2分支程序结构
分支程序比顺序程序的结构复杂得多,其主要特点是程序的流向有两个或两个以上的出口,根据指定的条件进行选择确定。
编程的关键是如何确定供判断或选择的条件以及选择合理的分支指令。
通常根据分支程序中出口的个数分为单分支结构程序(两个出口)和多分支结构程序(三个或三个以上出口)。
1.单分支结构程序的形式
单分支结构在程序设计中应用最广,拥有的指令也最多。
单分支结构一般为:
一个入口,两个出口。
如图4.12所示,单分支程序结构有以下两种典型形式:
(a)(b)
图4.12单分支程序结构的典型形式
图(a)当条件满足时执行分支程序1,否则执行分支程序2,例4.3就是这样一种结构;
图(b)当条件满足时跳过程序段2,从程序段3往下执行,否则顺序执行程序段2和3。
另外,分支结构程序允许嵌套,即一个分支接一个分支,形成树形多级分支程序结构,例4.4就是这样的结构。
2.散转程序
在实际应用中,常常需要从两个以上的出口中选一,称为多分支程序或散转程序。
MCS-51单片机指令系统中专门提供了散转指令,使得散转程序的编制更加简洁。
例4.5中采用转移地址表法实现散转程序,转移表是由双字节短转移指令“AJMP”组成,各转移指令地址依次相差两个字节,所以累加器A中变址值必须作乘2修正。
若转移表是由三字节长转移指令“LJMP”组成,则累加器A中变址值必须乘3。
当修正值有进位时,则应将进位先加在数据指针高位字节DPH上。
此外,转移表中使用“AJMP”指令,这就限制了转移的入口地址ONE、TWO、THREE、FOUR必须和散转表首地址TABLE位于同一个2K字节范围内。
为了克服上述局限性,除了可以使用“LJMP”指令组成跳转表外,还可采用双字节的寄存器存放散转值,并利用对DPTR进行加法运算的方法,直接修改DPTR,然后再用散转指令实现散转。
散转程序除了转移指令表法之外,还可以采用地址偏移量表法、转向地址表法及利用“RET”指令(子程序返回指令)实现散转程序等,具体实现参见习题4.11题。
3.转移条件的形成
分支程序中的转移条件一般都是程序状态字(PSW)中的标志位的状态,因此,保证分支程序正确流向的关键如下:
(1)在判断之前,应执行对有关标志位影响的指令,使该标志位能够适应问题的要求,这就要求编程员要十分了解指令对标志位的影响情况。
(2)当某一标志位处于某一状态时,在未执行下一条影响此标志位的指令前,它一直保持原状态不变。
(3)正确理解PSW中各标志位的含义及变化情况,才能正确地判断转移。
4.4循环程序设计
循环结构程序是把需要多次重复使用的程序段,利用转移指令反复转向该程序段,从而大大缩短程序代码,减少占用程序空间,程序结构也大大优化。
实训4中的程序3就是循环结构程序。
本节用实例介绍循环程序设计方法。
4.4.1循环程序实例
1.单重循环程序设计
例4.6实训4的程序3设计。
用P1口连接的8个LED模拟霓虹灯的显示方式。
编程实现P1口连接的8个LED显示方式如下:
一个发光二极管顺序从P1.0亮到P1.7所连接的LED
(1)题意分析
这种显示方式是一种动态显示方式,逐一点亮一个灯,使人们感觉到