D幻灯片定稿第4章MCS-51汇编语言程序设计

上传人:m**** 文档编号:578460393 上传时间:2024-08-24 格式:PPT 页数:87 大小:572.10KB
返回 下载 相关 举报
D幻灯片定稿第4章MCS-51汇编语言程序设计_第1页
第1页 / 共87页
D幻灯片定稿第4章MCS-51汇编语言程序设计_第2页
第2页 / 共87页
D幻灯片定稿第4章MCS-51汇编语言程序设计_第3页
第3页 / 共87页
D幻灯片定稿第4章MCS-51汇编语言程序设计_第4页
第4页 / 共87页
D幻灯片定稿第4章MCS-51汇编语言程序设计_第5页
第5页 / 共87页
点击查看更多>>
资源描述

《D幻灯片定稿第4章MCS-51汇编语言程序设计》由会员分享,可在线阅读,更多相关《D幻灯片定稿第4章MCS-51汇编语言程序设计(87页珍藏版)》请在金锄头文库上搜索。

1、第4章MCS51汇编语言程序设计本章要点:程序设计语言分类及特点汇编语言程序编写方法顺序结构程序的编写方法分支结构程序的编写方法循环结构程序的编写方法子程序的编写方法4.1 汇编语言概述4.1.1 程序设计语言的分类及特点能够完成一定功能的指令序列称为程序,用来设计计算机程序的语言称为程序设计语言,按照语言的结构及功能程序设计语言可分为:机器语言、汇编语言和高级语言。1机器语言是计算机所能识别的唯一语言,由二进制代码0和1构成指令和数据。其具有效率高、响应速度快的特点,但由于机器语言编写的程序依赖于计算机的结构,可移植性差,繁琐且难于记忆、识别和调试,通常编程时,不采用机器语言。2汇编语言是一

2、种符号语言,指令由助记符表示。与机器语言相比,汇编语言具有指令容易记忆、理解、识别和可读性好的优点,但实质上由于汇编语言也是面向机器的语言,是机器语言程序的符号表示,所以用户必须熟悉机器的硬件结构和指令系统,掌握计算机的工作过程才能熟练编程。汇编语言程序通常用于实时控制领域,其所能完成的操作不是一般高级语言所能实现的,而且源程序经汇编生成的可执行文件一般运行效率较高。另外,汇编语言所编写的源程序与经过汇编所产生的机器代码程序之间有明显的一一对应关系,所以汇编语言编写的程序同样也存在通用性差,程序不能移植的缺点。3高级语言高级语言是接近于人的自然语言形式的计算机编程语言的总称,例如C语言、BAS

3、IC语言等都是高级语言。和汇编语言相比,高级语言指令简单易学,用户容易掌握,且高级语言程序不依赖于具体的硬件结构和指令系统,程序可移植性好,但是高级语言编写的源程序必须经过编译或解释程序翻译成目标程序,机器才能执行,而生成的目标程序需占用较多的存储单元,执行时间较长,运行效率较低。由于目前有些高级语言不具有直接处理接口和中断技术的功能,因此高级语言一般很少应用于实时控制,但随着语言的发展,这种情况将会有所改变。4.12 单片机汇编语言源程序的编辑和汇编汇编语言的指令类型用汇编语言编写的、具有特定功能的指令序列,称为汇编语言源程序。源程序由两种类型的汇编语言语句(即指令)构成。语句是汇编语言的基

4、本组成单位,按性质不同分为两类:指令性语句(机器指令)和指示性语句(伪指令)。(1)机器指令 即指令系统中的全部指令,每条指令都有对应的机器代码,是机器真正能够执行的指令。(2) 伪指令为汇编程序在汇编过程中提供控制或指示信息,并不直接产生机器代码,属于机器不能执行的指令类型。汇编计算机不能直接识别和执行源程序。因此源程序必须经过汇编程序汇编产生机器码目标程序文件,程序才能执行。这种将汇编语言源程序转换成机器语言程序的过程称为汇编。对于初学者来说,应注意汇编语言源程序和汇编程序两个术语的区别,它们的功能示例如图4-1所示。图4-1 汇编过程汇编语言源程序汇编程序目标文件和源程序列表经过汇编后汇

5、编语言源程序的汇编过程分为手工汇编和机器汇编。所谓手工汇编,即采用人工查指令表的方法将汇编指令翻译成相应机器代码。通常源程序的人工汇编需要两次才能完成,第一次汇编查找每条指令的机器代码,第二次汇编完成地址偏移量的计算。由于手工汇编需要计算和查找,繁琐而且容易出错,而且程序修改时可能会引起指令的地址变化,转移指令的偏移量也随之改变,需要重新计算,所以手工汇编主要应用于设计短小程序或调试程序的场合。所谓机器汇编,即采用汇编程序对源程序进行自动汇编,由于单片机软硬件资源的限制,该过程通常借助于PC系统机实现,象这种借助于一种计算机而为另一种计算机产生目标代码的汇编方式又称为交叉汇编。交叉汇编的原理类

6、似于手工汇编,在汇编程序中通常存入了两张表,即MCS-51单片机的指令代码表和伪指令表。汇编程序通常通过两次扫描完成汇编,第一次扫描查找源程序中每条指令的机器代码,第二次扫描完成地址偏移量的计算,汇编后生成目标文件和列表文件。这是目前应用较广的汇编方法。纵上,汇编的主要功能为:(1)确定程序中每条汇编语言指令的指令机器码。(2)确定每条指令在程序存储器中的存放地址。(3)提供语法、编辑等方面的错误信息,但不能提供程序的逻辑错误。(4)产生目标执行文件(*.OBJ/*.HEX)和列表文件(*.LST)。4.1.3 MCS51汇编语言的伪指令所谓“伪”指令,即不是真正的可执行指令。如前所述,伪指令

7、只能在对源程序进行汇编时起控制作用,例如设置程序的起始地址,定义符号,给程序分配一定的存储空间等。常用的伪指令共有8条,下面分别介绍。1ORG(Origin)设置起始地址指令用来设定程序或数据存储区的起始地址。指令格式为:ORG 16位地址例如,有程序段如下,则表示程序存放的起始地址为1000H。指令地址 机器码源程序 ORG 1000H 1000H 78 2 MAIN:MOV R0,#20H 1002H E6 MOV A,R0 若省略ORG指令后的16位地址,则汇编后目标程序的起始地址默认为0000H。ORG指令在一个源程序中可以多次使用以指定不同程序段的起始地址,但是指定的多个地址应从小到

8、大,不能使程序段之间产生重叠。2DB(Define Byte) 定义字节型常数指令指令的功能为从指定的存储单元开始,定义或存储一个或多个字节数据。指令格式为:标号:DB 字节常数表例如, ORG 1000HTAB: DB 0A3H,18,AB汇编结果为:(1000H)=A3H (1001H)=12H (1003H)=41H (1004H)=42H3DW(Define Word)定义字型常数指令指令的功能为从指定的存储单元开始,定义或存储一个或多个字数据。通常用于在程序中定义地址表。指令格式为:标号:DW 字常数表一个字数据占用两个存储单元,存放时高字节存入低位地址,低字节存入高位地址。例如,

9、ORG 1000HTAB:DW 1234H,18,00A3H汇编结果:(1000H)=12H (1001H)=34H (1002H)=00H (1003H)=12H (1004H)=00H (1005H)=A3H4. EQU(Equate)等值指令指令功能是把操作数段中的地址或数据赋值给标号。赋值后的标号可在整个源程序中使用。指令格式如下:标号 EQU 数据或汇编符号注意,与程序中一般意义的标号不同的是,这里的标号后不能加“:”。例如, ADDR EQU 1000H ;给ADDR赋值1000H DAT1 EQU 0AH ;给DAT1赋值0AH MAIN:MOV DPTR,#ADDR;DPTR1

10、000H MOV A,DAT1 ;A(0AH)上例中,ADDR被赋值以后,在程序中作为立即数使用,而DAT1赋值后被当作直接地址使用。需要说明的是,使用EQU指令时必须先赋值后使用。5DS(Define Store)定义存储区指令指令功能为从指定的单元地址开始,保留一定数量的存储单元,以备使用。指令格式如下:标号:DS 表达式其中,表达式指定保留的内存单元个数。例如,ORG 1000H DS 10TAB: DB 10H 汇编结果:从1000H地址处开始,保留10个字节单元。(100AH)=10H。6BIT位地址符号指令指令功能为将位地址赋值给指定的符号名称,通常用于位符号地址的定义。指令功能如

11、下:字符名称BIT 位地址例如,X1 BIT P1.0汇编结果为将位地址P1.0赋给变量X1,即在程序中便可使用X1代替位地址P1.0。7DATA数据地址赋值指令指令功能为将数据地址或指令代码地址赋值给所指定的标号。通常在程序中用于定义数据地址。指令格式如下:标号:DATA 表达式例如,AA:DATA 2000H汇编结果为:AA=2000H。注意,DATA与EQU的区别在于:EQU定义的标识符在汇编时不在符号表中登记,因此必须先定义后使用,而DATA定义的标识符汇编时在符号表中登记,所以可以先使用后定义。8. END 汇编语言源程序结束指令指令功能,表示源程序到此结束,END指令以后的指令汇编

12、程序将不予处理。一个程序中只能在末尾有一个END指令。例如, START: END START4.2 汇编语言程序设计对于简单程序的编写,程序设计者往往能够立即完成软件的构思与编写,而对于比较复杂的程序设计问题,则需要科学合理的程序设计步骤。从软件工程角度来说,开发一个应用程序,一般需要经过以下几个步骤:1分析问题,建立数学模型如果拿到问题,立即着手编写程序代码往往是很难成功的,通常需要首先分析题目的已知条件,了解系统的硬件配制,明确题目的要求和要实现的功能,然后建立数学模型。2了解系统的硬件配置和性能指标,确定方案算法所谓算法就是为了解决问题而采取的方法和步骤。在分析问题后,应根据系统的具体

13、硬件配置和性能指标等实际情况确定具体有效且计算机能够执行的方法和步骤。3用流程图表示出程序算法确定算法后,应用简单直观的方法描述算法,以便为进一步编程做好准备。算法的描述方法很多,其中比较常用的是流程图法。一般流程图的符号如图4-2所示。4根据流程图编写源程序使用汇编语言编写源程序时,应首先合理规划和分配存储器单元,确定程序和数据的存放区域,了解系统的I/O接口地址,最后按照流程图写出源程序。5调试运行程序通常情况下,源程序编制好后必须经过上机调试才能运行。调试程序的一个重要功能是修正语法和逻辑错误,直到达到题目的要求为止。 4.2.1 顺序程序设计顺序程序是程序设计中最基本的结构,又称为简单

14、程序。特点为顺序执行每一条指令,直到最后。其执行过程如图4-3所示。 S1S2图4-3 顺序结构【例4-1】编写一程序,将累加器A中的两位压缩BCD转换成ASCII码存入1000H、1001H片外存储单元中。开始R0 AA取A的低4位AA+30H存入1000H单元A取A的高4位AR0AA+30H存入1001H单元结束分析:由ASCII码表可知,09的的ASCII码为30H39H,即BCD码与ASCII码值的差值为30H。因此只要将两位BCD码分别取出与30H相加即可获得相应的ASCII码值,其算法流程图如图4-4所示。程序如下: ORG 2000HMOV DPTR,#1000HMOV R0,A

15、ANL A,#00001111BORL A,#30HMOV DPTR,AMOV A,R0SWAP AORL A,#30HINC DPTRMOVX DPTR,AEND【例4-2】已知如图4-5所示,双字节4位压缩BCD码数 X 存于片内RAM30H、31H单元,Y存于32H、33H单元,编程求 Z=X+Y,并将结果Z存入片内RAM单元34H、35H、36H中ADDSUM:MOV A,30HADD A,32H ;低位字节相加DA A ;BCD码修正MOV 34H,A ;低位字节存入34H中MOV A,31HADDC A,33H ;高位字节与低位进位相加 DA A MOV 35H,A ;高位字节之和

16、存入35H中 MOV A,#0 ADDC A,#0 DA AMOV 36H,A ;高位字节的进位存入36H中END。从该例可以看出,多字节BCD码相加时,应从低位字节开始相加,每进行一次加法运算进行一次BCD码调整才能得到正确结果。4.2.2 分支程序设计1分支程序结构的基本形式 分支程序是程序设计中应用非常广泛的一种基本结构。比如我们经常遇到需要计算机进行逻辑判断的情况,然后根据判断的结果进行不同的处理。例如,比较两个数的大小,并输出判断结果;根据输入压力的不同,用不同的方法计算发动机功率等这些问题都是顺序结构程序所无法实现的,而是属于分支结构程序设计的范畴。具体来说,如图4-6所示,分支程

17、序结构具有以下三种形式。条件S1S2YN条件S1条件NS2S1Sn(a)单分支 (b)双分支 (c)多分支2分支程序的设计方法依据分支结构程序的执行过程,结合MCS-51的指令系统可以得出,分支程序的实现需要通过条件转移指令完成。因此如何设定分支条件便成为分支程序设计的关键。具体来说,其设计要点可归纳如下:(1)设定可供条件转移指令测试的条件。通常,可以作为转移指令判断条件的有标志位状态(如进位位C的状态),累加器A或片内RAM中某位的结果状态等。因此为了提供条件转移指令的测试条件,应在程序的转移指令前设定影响判断条件的标志位状态,或通过逻辑运算、算术运算等指令影响标志位。例如,若要使用JZ指

18、令实现分支,则应在该指令前执行影响累加器A的指令。(2)根据标志位状态选择正确的条件转移指令。例如,要判断进位位,可使用JNC或JC等指令。(3)应在转移的目的地址处设定标号。【例4-4】 求8位有符号数的补码。设8位二进制数存放在片内RAM30H单元内。分析:对于二进制数的补码负数可用取反加1的方法求得,而正数不变。ORG 2000HMOV A,30HJNB ACC.7,NEXT;为正数,不进行处理CPL A ;负数取反INC A ;加1MOV 30H,ANEXT:SJMP NEXT ;结束可以看出,该程序通过符号位是否为0来判断该数为正数还是负数,若为正数则程序结束,若为负数则变补。显然,

19、这是一个单分支的例子。【例4-5】 已知压力P和功率W之间存在如下关系: (其中M为功率修正系数)当P10, W=P5+M当P10, W=P5-M分析:首先判断压力P是否大于或等于10,然后根据判定结果计算功率W。设P存在40H单元中,M存放于41H中,结果W存放于42H中,程序如下: ORG 2000HMOV A,40H ;取压力值 MOV R3,A MOV B,#05H ;P5 MUL AB MOV R1,A ;将P5的结果暂存入R MOV A,R3 ;取回PCJNE A,#10,L1 ;P10,转L1SJMP NEXT ;P=10,转NEXTL1: JC NEXT ;CY=1,P10,转

20、至NEXT MOV A,R1 ;CY=0,P10,W=P5-M SUBB A,41H SJMP RESULTNEXT:MOV A,41H ;取M的值 ADD A,R1 ;W=P5+MRESULT:MOV 42H,A ;存结果 END该例是一个双分支程序,通过比较转移指令CJNE A,#10,L1和条件判断指令JC NEXT实现程序的分支,这种编程方法在分支程序设计中应用非常广泛,对于初学者应仔细分析体会。【例4-6】 求符号函数Y=F(X) +1 (当 X0时)F(X)= 0 (当 X=0时) -1 (当 X0时)Y= 0Y= 1Y=1保存Y结束X0?NY开始取XYNX0?分析:这是一个多分支

21、程序示例。设计时可考虑将判断变量X转化为判断累加器A,从而使用条件判断指令JZ来判断是否为零。另外,由于X是有符号数,因此可使用JB或JNB指令判断符号位来实现分支。其流程图如图4-7所示。设变量X存于片内RAM20H单元,结果Y存于21H单元,程序如下:ORG 2000HMOV A,20H ;取XJZ RESULT ;X=0,Y=0JB ACC.7,NEG ;判符号位,若为1,则X0MOV A,# 1 ;X0,Y=1SJMP RESULTNEG :MOV A,#0FFH ;X0,Y= -1,将-1的补码送ARESULT:MOV 21H,A ;保存YSJMP $Y=0Y=1Y=1保存Y结束X0

22、?NY开始取XYNX0?3.分支表法实现多向分支程序的设计上例中符号函数程序是一个多向分支程序的例子,直接采用了条件转移指令来实现,但在实际应用时,通常遇到根据某变量取值不同从而转向不同分支的题目,这时经常采用分支表法。常用的分支表主要有三种形式:转移指令表、分支地址表和地址偏移量表。虽然分支表的构成各异,具体的编程方法也略有不同,但实质上都是利用散转指令JMP A+DPTR来实现的。实现散转的方法主要有两种:(1)累加器A清零,根据DPTR的内容决定转移的目标地址。(2)DPTR的值作为基址不变,根据累加器A的内容决定转移的目标地址。下面通过一个具体的例题,说明使用分支表实现多向分支程序设计

23、的方法。【例4-7】根据R2的内容,转向相应的分支程序。即:R2=0,转向PROG0R2=1,转向PROG1R2=n,转向PROGn(1)使用转移指令表。所谓转移指令表即由转移指令组成的分支表,如图4-8所示。JMPTAB:AJMP PROG0 JMPTAB: LJMP PROG0AJMP PROG1 LJMP PROG1 AJMP PROGn LJMP PROGn (a) (b)图4-8转移指令表设转移指令表的标号为JMPTAB,分支数为5,解题思路分析如图4-9所示。DPTRJMPTABA+DPTRJMPTAB+R23JMP A+DPTRLJMP PROG0LJMP PROGnLJMP P

24、ROG1MOV DPTR,#JMPTAB;将JMPTABDPTRMOV A,R2MOV B,#03MUL AB ;R23BAPUSH A ;暂存乘积的低位字节AMOV A,BADD A,DPH ;将乘积的高位字节B+DPHDPHMOV DPH,A ;POP A ;将暂存的A内容恢复 JMP A+DPTR ;散转JMPTAB:AJMP PROG0 ;转移指令表AJMP PROG1 AJMP PROG5 PROG0: PROG1: PROG5: 采用第二种散转程序设计方法,即DPTR内容固定,根据A的值转向不同的分支程序。则应首先将JMPTABDPTR,使DPTR的内容固定,将R2MA(当使用短转

25、移指令表时M的值为2,当使用长转移指令表时M的值为3),然后使用JMP A+DPTR指令实现分支。具体程序如下:说明,因为转移指令表由LJMP长跳转指令构成,因此M取值为3,其乘积的高位字节应加在DPH上。若转移指令表由2字节指令AJMP构成,M取值应为2,且各分支程序的入口地址PROG0、PROG1必须与转移指令表处于同一个2KB的存储地址空间之内,而LJMP指令表则没有这个限制。使用AJMP转移指令表的程序如下:MOV DPTR,#JMPTAB ;将JMPTABDPTRCLR CMOV A,R2RLC A ;R22AJNC NOADD ;判断是否有进位INC DPH ;若有进位,将进位加到

26、高字节DPHNOADD: JMP A+DPTR ;散转JMPTAB:AJMP PROG0 ;转移指令表AJMP PROG1 AJMP PROG5 PROG0: PROG1: PROG5: (2)使用分支地址表。所谓分支地址表是指由各个分支程序的入口地址组成的线性表,每个入口地址占两个连续字节单元。设PROG0PROGn为分支程序入口地址,分支地址表如图4-10所示。BRANCHTAB: DW PROG0 DW PROG1 DW PROGn图4-10分支地址表DPTRBRANCHTABAR22JMP A+DPTR 分支程序PROG0 分支程序PROGn分支程序PROG1DPTR应用MOVC A,

27、A+DPTR取分支地址A0图4-11分支地址表的使用设分支地址表的标号为BRANCHTAB,分支数为n,使用分支地址表实现多向分支的解题思路分析如图4-11所示。DPTRBRANCHTABAR22JMP A+DPTR 分支程序PROG0 分支程序PROGn分支程序PROG1DPTR应用MOVC A,A+DPTR取分支地址A0图4-11分支地址表的使用根据以上解题思路,例4-7程序编写如下: ORG 1000HMAIN: MOV DPTR,#BRANCHTAB ;取分支表入口地址 MOV A,R2 CLR C ;AR22 RLC A JNC NOADD INC DPH ;进位加到DPH中NOAD

28、D:MOV R3,A ;R3R22 MOVC A,A+DPTR ;A分支地址的高位字节 XCH A,R3 ;R3分支地址的高位字节,AR22 INC A ;指向下一个存储单元 MOVC A,A+DPTR ;取分支地址的低位字节 MOV DPL,A ; MOV DPH,R3 ; CLR A JMP A+DPTR ;分支地址PC,转移BRANCHTAB:DW PROG0 ;分支地址表 DW PROG1 PROG0: ;分支程序0 PROG5: ;分支程序5(3)使用地址偏移量表。所谓地址偏移量表,是指由各分支程序段的入口地址与地址偏移量表的标号差(即地址偏移量)形成的线性表。其中地址偏移量表中每项

29、占一个字节,如图4-12所示。TAB:DB PROG0-TAB ;PROG0PROGn为分支程序入口地址 DB PROG1-TAB DB PROGn-TAB图4-12 地址偏移量表DPTRTABAR2JMP A+DPTR 分支程序PROG0 分支程序PROGn分支程序PROG1应用MOVC A,A+DPTR取分支地址偏移量图4-13地址偏移量表的使用根据以上解题思路,例4-7程序编写如下:ORG 1000HMOV DPTR,#TAB ;取偏移量表首地址MOV A,R2 MOVC A,A+DPTR ;将查表所得PROGi-TABAJMP A+DPTR ;由A+DPTR=PROGi-TAB+TAB

30、=PROGi获得分支程序地址实现跳TAB:DB PROG0-TAB ;地址偏移量表DB PROG1-TAB DB PROGn-TABPROG0:PROGn:使用地址偏移量表实现多向分支时,应注意要使地址偏移量表与各分支程序的长度和在同一页(256字节)范围内,因此该方法适用于分支较少的情况。4.2.3 循环程序设计在程序设计中,常常会涉及到重复执行的程序段,这可以通过循环结构来实现,循环结构可使程序更加紧凑。1循环结构程序的构成与高级语言中循环程序的构成相似,汇编语言的循环程序结构主要包括以下四个部分:(1)循环初始化部分。所谓初始化即设置循环开始时的状态,如清结果单元、设置地址指针、设定寄存

31、器初值、循环次数等。(2)循环体部分。循环体是循环结构的主体,为需要重复执行的程序段。(3)循环控制部分。这一部分主要完成循环条件的设定,循环控制变量的修改以及检测循环条件是否仍然满足,若条件成立则继续循环,否则结束循环。(4)结束部分。该部分主要完成循环结束后的结果处理工作,例如结果的保存、计算等。其执行过程如图4-12所示。循环初始化执行循环体循环条件是否成立?N开始循环结束,结果处理Y结束修改循环变量循环初始化执行循环体循环条件是否成立?N开始循环结束,结果处理Y结束修改循环变量体2简单循环程序所谓简单循环即一重循环,也就是循环程序中只包含一个循环,不嵌套其它循环的循环程序。另外,设定循

32、环执行的条件非常重要,否则可能形成死循环。编写汇编语言程序时,通常采用两种控制循环的方法。一种是使用计数的方法实现循环,即将循环次数作为循环计数器的初值,当计数器的值加满(称为正计数)或减为0(称为倒计数)时结束循环,否则继续循环,该方法适用于循环次数已知的情况。另一种为通过设定特定条件控制循环,若设定条件满足则执行循环,否则结束循环,例如设定特定的循环结束标志等,该方法适用于循环次数未知的情况。【例4-8】数据块求和。设内部RAM中有一连续单字节数据块,首地址为BLOCK单元,数据块长度存于LEN单元,若数据累加和也为单字节数据,并存于RESULT单元,编程求数据块之和。分析:累加和的求解应

33、使用循环,且数据长度已知,所以循环次数已知,因此可考虑使用指令DJNZ来控制循环条件,其算法流程图如图4-15所示。A0,R0BLOCK,R1LEN累加求和R10?N开始RESULT和Y结束R0+1 ,R1-1程序如下:ORG 1000HLEN: DATA 20HRESULT: DATA 21HBLOCK: DATA 22HMOV A,#0MOV R0,#BLOCKMOV R1,#LENLOOP: ADD A,R0INC R0DJNZ R1,LOOP MOV RESULT ,AHERE: SJMP HERE可以看出,这是一个循环次数已知的例题。 【例4-9】求均值。已知一控制系统,从P1口读入

34、采样值,每周期采样16次,试编程求其每周期的采样均值。分析:要求平均值,应先求得16次采样的数据和,然后除以16即可。由于16=24,因此可使用指令右移指令RRC完成除法运算。设累加和存于寄存器R0R1中,其具体程序如下:ORG 1000HMOV R0,#0 ;清零,保存和的高位字节MOV R1,#0 ;清零,保存和的低位字节MOV R3,#16 ;R3用作计数器L1:MOV P1,#0FFH ;置P1为输入口MOV A,P1 ;读入采样值ADD A,R1 ;累加JNC L2 ;若无进位,转L2INC R0 ;进位加到高位字节L2:MOV R1,A DJNZ R3,L1 ;16次采样值是否累加

35、完? MOV R4,#4 ;R4右移次数4L3:MOV A,R0 RRC A ;高位字节右移1位 MOV R0,A MOV A,R1 RRC A ;低位字节右移1位 MOV R1,A DJNZ R4,L3 ;循环4次完成除以16HERE:SJMP HERE【例4-10】求最小值。设内部RAM中有一无符号数数据块,其首地址为BLOCK,长度存于LEN单元,试求出数据块中的最小值,存入MIN单元。分析:求最小值时,通常采用比较交换的方法。即首先取第一个数作为基准,然后将基准数与第二个数进行比较,若基准数大于第二个数,则两数进行交换;若基准数小于第二个数则不进行交换,总之保证基准数单元中的值为最小值

36、,再取下一个数与基准数进行比较,一直到所有数据比较完为止,则基准数单元中的数则为最小值。依此类推,求最大值也可采用类似方法。其算法流程图如图4-16所示。A0,R1BLOCK,R2LEN清CY AA(R1)R20?N开始MINAY结束R1+1 ,R21CY=1?A(R1)NAA+(R1)Y具体程序编写如下:ORG 1000HMIN: DATA 20HLEN: DATA 21HBLOCK: DATA 22HCLR AMOV R2,LENMOV R1,#BLOCK ;取数据块首地址LOOP: CLR C ;清CY准备做减法SUBB A,R1JC NEXT ;A(R1),转NEXTMOV A,R1

37、;A(R1),则A(R1)SJMP NEXT1NEXT: ADD A,R1 ;A(R1),恢复ANEXT1: INC R1DJNZ R2,LOOPMOV MIN,A ;存最小值HERE: SJMP HERE 【例4-11】设片内RAM中有一无符号数数据块,其首地址为BLOCK,长度未知,但数据结束标志存于LEN单元,求数据块的最小值并存于MIN单元中。 ORG 1000HMIN: DATA 20HLEN: DATA 21HBLOCK: DATA 22HMAIN: MOVR1,#BLOCK ;数表首地址 MOV B,R1 ;取第一个数作为基准NEXT: INCR1 ;修改指针 MOV A,R1

38、CJNE A,LEN,NEXT1 ;是否为数表结尾? SJMP DONE ;循环结束NEXT1: CJNE A,B,NEXT2 ;比较NEXT2: JNCNEXT ;AB,转NEXT继续取数 MOV B,A ;保存较小值 SJMP NEXTDONE: SJMP DONE可以看出,这是一个循环次数未知的例题,通过设定特征值控制循环结束条件。3多重循环多重循环又称之为循环的嵌套,即在一个循环结构的循环体内,又包含另一个完整的循环结构。在实际应用中,有时一重循环并不能解决问题,所以循环的嵌套应用还是比较广泛的。在嵌套循环的使用中,被嵌套的循环可以不止一个,并且可以嵌套多层,但不论是哪种情况,内层循环

39、和外层循环都必须是一个完整的结构,不允许有相互交叉的情况出现。即如出现图4-17(a)所示的情况则是非法的嵌套,而图4-17(b)所示为正确的嵌套。内层循环外层循环内层循环外层循环图4-17(a)非法嵌套示意图 图4-17(b)正确嵌套示意图【例4-12】数据排序程序。设有N个数据,存于首地址为BLOCK的内存单元中,试设计程序将数据从小到大按升序排列。分析:数据排序的方法很多,这里采用沉底法。沉底法的基本思想:通过相邻两个数之间的比较和交换,使排序码(数值)较小的数逐渐从底部移向顶部,排序码较大的数逐渐从顶部移向底部,就像较大的数往下沉底一样,故而得名。设由R0为数据存放区的首地址,则(R0

40、+N)中存放第N个数据,进行沉底排序的过程可以描述为:(1)首先将相邻的(R0)与(R0+1)进行比较,如果(R0)的值小于(R0+1)的值,则不交换两者的位置,否则不交换,即使较小的上浮,较大的下沉;接着比较(R0+1)与(R0+2),同样的方法使小的上浮,大的下沉。依此类推,直到比较完(R0+N-1)和(R0+N)后,(R0+N)为具有最大排序码(数值)的元素,称第一趟排序结束。(2)然后在(R0+N-1)R0区间内,重新进行第二趟排序,使剩余元素中排序码最大的元素沉底到(R0+N-1);重复进行n-1趟后,整个排序过程结束。其算法流程图如图4-18所示。开始外循环次数R4内循环次数R3(

41、R0)(R0+1)(R0)(R0+1)R0R0+1R3R31=0?R4R41=0?循环结束YNNNYYORG 1000HBLOCK: DATA 20HSORT: MOV A,#N-1 ;N个数据排序 MOV R4,A;外循环次数LOOP1: MOV A,R4 MOV R3,A;内循环次数 MOV R0,#BLOCK;设数据指针LOOP2: MOV A,R0 MOV B,A ;B(R0) INC R0 MOV A,R0 ;A(R0+1) CJNE A,B,L1 ;两数比较L1:JNC NEXT;若BA,即(R0)(R0+1)不交换 DEC R0 ;否则交换数据 XCH A ,R0 INC R0

42、;修改数据指针 MOV R0,ANEXT: DJNZ R3,LOOP2;内循环 DJNZ R4,LOOP1 ;外循环HERE: SJMP HERE在数据采集系统中,若被采集的信号变化较慢,经常采用中值滤波的方法去掉由于偶然因素造成的干扰误差。中值滤波时,首先将采样数据排序,再取中间值作为本次采样的终值。 4.2.4 子程序设计第三章指令系统中已经介绍过子程序的概念与相关指令。子程序是一种重要的程序结构,主要由需要反复执行的操作或程序段构成,以供主程序调用。另外,子程序也可嵌套,即子程序调用子程序,只要堆栈深度足够,子程序可嵌套多层。主程序与子程序以及子程序嵌套调用的关系如图4-19所示。与一般

43、程序的编写方法相比,子程序具有以下几个特点。(1)子程序应命名。子程序入口即子程序的第一条指令应加标号作为子程序名,以便主程序调用。(2)子程序中应注意堆栈的使用,以保护和恢复现场。在有些情况下,若子程序需要改变主程序中某些寄存器或存储单元的结果,而这些结果又不能被修改或在子程序调用之后仍然需要则应在子程序中首先将这些内容使用进栈指令保护,在子程序返回之前使用出栈指令恢复现场。(3)子程序的结尾必须为子程序返回指令RET,并保证堆栈栈顶为调用程序的返回地址。(4)子程序嵌套时应考虑堆栈的深度。(5)能够正确传送参数。参数分为入口与出口参数。所谓入口参数,即调用子程序之前,需要传给子程序的参数。

44、所谓出口参数,即子程序返回时应送回调用程序的结果参数。应根据具体情况选择不同的参数传递方式,通常使用寄存器、存储器或堆栈的方式传送参数。(6)子程序应具有一定的功能和通用性。对于操作数应尽量使用以地址或寄存器形式给出,一般不针对具体的数据编写子程序。 (7)为了便于调用,子程序应提供足够的信息。如:子程序名、子程序功能、入口参数和出口参数、子程序占用的硬件资源、子程序中调用的其他子程序名。【例4-13】编程实现C=a2+b2。设a、b均小于10且分别存于外部RAM的100H,101H单元,要求运算结果C存于外部RAM102H单元。分析:本题可利用子程序完成求单字节数据的平方,然后通过调用子程序

45、求出a2和b2。其具体程序如下:SQR:INC A MOVC A,A+PC ;使用查表指令求平方RETTAB: DB 0,1,4,9,16,25,36,49,64,81可以看出SQR子程序的入口参数为A,即将要求平方值的数送入A;其出口参数也为A,即求出的平方值也送入A。该例的主程序如下:ORG 1000HSTART:MOV DPTR,#0100H MOVX A,DPTR ;取a的值ACALL SQR ;调用子程序求a的平方MOV R1,A ;R1a2MOV DPTR,#0101H MOVX A,DPTR ;取b的值ACALL SQR ;调用子程序求b的平方ADD A,R1 ;Aa2+b2MO

46、V DPTR,#0102H MOVX DPTR,A ;存结果 SJMP $【例4-14】设计子程序将单字节数据对半拆分,变成2个字节存放。分析:子程序入口参数:累加器A,即将要拆分的数据送入A累加器中。出口参数:R1,即将拆分后的数据存入(R1)指向的两个相邻地址单元。FEN1: MOV R1,#0 ;将(R1)指向的地址单元清零 XCHD A,R1 ;低半字节存入指定单元 INC R1 ;修改指针,指向下一个地址单元 MOV R1,#0 ;清零 SWAP A ;高低半字节交换 XCHD A,R1 ;保存高半字节 RET【例4-15】编写将累加器A中的ASCII码转换为1位十六进制数的子程序。

47、分析:十六进制数09的ASCII码为30H39H,即十六进制数(09)= ASCII码-30H;十六进制数AF的ASCII码为41H46H,即十六进制数(AF)= ASCII码-37H。根据此对应关系,子程序可编写如下:ASCHEX:CLR C ;准备相减 SUBB A,#30H ;AA-30H CJNE A,#0AH,L2 ;转移判断L2: JC L1 ;若A中的值0AH则求得十六进制数 SUBB A,#07H ;若A中的值0AH,则AA-07H,求得十六进制数L1: RET可以看出该子程序的入口参数为累加器A,出口参数也为累加器A。另外,根据ASCII码和十六进制数的对应关系,将1位十六进

48、制数转换为ASCII码的程序可编写如下:HEXASC:CJNE A,#0AH,L1 L1:JNC L2 ADD A,#30H SJMPHERE L2: ADD A,#37HHERE:RET 请读者自行分析该子程序每条语句的功能及入口和出口参数。4.3 MCS51汇编语言实用程序举例在程序设计时,通常把需要多次使用的程序段设计成具有特定功能的子程序,以供调用,这样可使程序设计避免了重复性的工作,从而使效率提高,而程序也更加灵活。下面将分类介绍一些实用的子程序。4.3.1 代码转换程序【例4-16】将累加器A中的8位二进制数转换成3位BCD码。3位BCD码占用两个字节,结果的百位数存于BAI单元,

49、十位和个位数占用一个字节单元SHIGE。分析:可应用除法指令实现数制的转换,将被转换的数除以100得百位数,再将余数除以10得十位数,余数为个位数。例如:设要转换得二进制数据为0EFH,则0EFH除以100,商=02H,即百位数为2,余数=27H,27H除以10,商=03H,即十位数为3,余数=09H,即个位数为9。其具体程序如下:BAI: DATA 30HSHIGE:DATA 31HBINBCD: MOV B,#100 DIV AB ;除以100,A商,B余数MOV BAI,A ;BAI商MOV A,#10 ;XCH A,B ;B中的余数与A中除数10互换DIV AB ;余数除以10, A商

50、,B余数SWAP A ;十位数交换到A的高半字节ORL A,B ;与B中的个位组合成一个字节数据MOV SHIGE,A;十位、个位送入单元SHIGE RET该子程序入口参数:累加器A 出口参数:地址单元BAI和地址单元GESHI【例4-17】编写将多字节二进制数转换成BCD码的子程序。分析:上例中介绍了一种使用除法实现将二进制数转换成BCD码的算法,但上述方法一般针对转换数较小的情况,若被转换数较大,则该算法需要进行多字节运算,运算速度较慢,程序效率较低。若用b代表各二进制位数,则有下式成立:BCD= bn-12n-1 + bn-22n-2 + + b121 + b020 =( bn-12 +

51、 bn-2 )2 + bn-3)2 + b1)+ b0因此可采用从最高位开始,按十进制运算法则循环“乘2加次低位”的算法:D=D2+di。设子程序入口参数:R0、R7。R0为二进制数低位字节地址指针,R7为要转换的二进制字节数。出口参数为:R1。R1为压缩BCD码高位字节地址指针。其流程图如图4-20所示。开始R3二进制位数二进制数左移1位BCD2+进位,调整R3-1=0?结束YN存储单元清零字节数R2-1=0?YN具体程序如下:ORG 1000HBINBCD:MOV A,R0MOV R5,A ;R0R5 MOV A,R1MOV R6,A ;R1R6MOV A,R7 ;取二进制字节数INC A

52、 ;二进制字节数加1AMOV R3,ACLR AL0:MOV R1,A ;对BCD码存储单元清零INC R1DJNZ R3,L0MOV A,R7 ;取二进制字节数MOV B,#08 MUL AB ;二进制字节数8二进制位数MOV R3,A ;二进制位数R3图4-20 【例4-17】程序流程图L3: MOV A,R5MOV R0,A ;二进制数低位字节地址R0MOV A,R7MOV R2,A ;二进制数字节数R2CLR CL1: MOV A,R0 ;二进制数左移1位RLC AMOV R0,AINC R0DJNZ R2,L1MOV A,R6 ;BCD码地址指针R1MOV R1,A MOV A,R7

53、MOV R2,AINC R2 ;BCD码字节数R2L2: MOV A,R1 ;BCD码2+CYADDC A,R1DA AMOV R1,AINC R1DJNZ R2,L2DJNZ R3,L3RET【例4-18】编程将4位单字节BCD码转换成二进制数。设4位BCD码为D3D2D1D0,子程序入口参数:R0。R0为BCD码的高位字节D3地址指针(设BCD码的高位在前,低位在后),出口参数为:R3R4,即转换后的二进制数存放到寄存器R3R4中。开始R30R4(R0)R3R4R3R410R0R0+1R3R4R3R4+(R0)R2-1=0?结束YNR23分析: 由于D3D2D1D0的二进制数可表示成:D3

54、103+D2102+D1101+D0100=(D310+D2)10+D1)10+D0,所以算法上可采用循环“高位10+次低位”的方法。其流程图如图4-21所示。 具体程序如下:ORG 1000HBCDBIN:MOV R2,#03 ;R2为计数器MOV R3,#00HMOV A,R0 ;取D3MOV R4,ALOOP: MOV A,R4 ;R4R410MOV B,#0AHMUL AB MOV R4,A ;R410的低位字节R4MOV R1,B ;R410的高位字节R1暂存MOV A,R3 ;R3R310MOV B,#0AHMUL ABADD A,R1 ;R310的低位字节+R1AMOV R3,A

55、 ;R3高位字节INC R0 ;修改指针MOV A,R0 ;取下一个数ADD A,R4 ;R3R4+(R0)R3R4MOV R4,AMOV A,R3ADDC A,#00HMOV R3,ADJNZ R2,LOOPRET开始R30R4(R0)R3R4R3R410R0R0+1R3R4R3R4+(R0)R2-1=0?结束YNR23【例4-19】多字节二进制数变补码子程序。分析:多字节二进制数求补,可先将低位字节取反加1变补,然后将高位字节依次取反再加上低位字节求补后的进位即可。设入口参数为:R0,存放将要取补的二进制数的低位字节指针,R2:字节数 出口参数为:R0,取补后数据的高位字节指针具体程序如下

56、: CPLD:SETB C LOOP:MOV A,R0 ;取数CPL A ;取反ADDC A,#0 ;加进位求补MOV R0,A ;保存转换结果INC R0DJNZ R2,LOOPDEC R0RET4.3.2算术运算程序【例4-20】多字节无符号BCD码加/减法运算程序。分析:实现多字节的加/减法运算,可利用指令ADDC/SUBB,从低位字节到高位字节依次相加或相减,由于是BCD码运算,所以应在加法运算后使用十进制调整指令DA A。设子程序入口参数为:R0、R1和R2,即R0为被加数或被减数的低位地址指针,R1为加数或减数的低位地址指针,R2为字节数;出口参数为:R0,即R0为和或差的低位地址

57、指针。具体程序如下:ORG 1000HADDBCD:CLR CLOOP: MOV A,R0 ;取被加数ADDC A,R1 ;与加数相加DA A ;十进制调整MOV R0,A ;存放结果INC R0 ;指向被加数的下一个字节INC R1 ;指向加数的下一个字节DJNZ R2,LOOPJNC HERE ;判断高字节是否有进位?若没有,则结束MOV R0,#1 ;否则将进位加到结果中HERE: RET由于减法运算与加法类似,只要使用SUBB指令代替ADDC指令即可,请读者自行分析完成。【例4-21】双字节无符号数乘法。R2R3R7R6R3R7LHR3R7R2R7LR2R7HR3R6LR3R6HR2R

58、6LR2R6HR2R3R4R5+分析:双字节二进制无符号数乘法可利用单字节乘法指令来实现,操作时按照以字节为单位的竖式乘法运算表来完成。例如设R2R3为被乘数,R6R7为乘数,乘积为R2R3R4R5,由高到低排列,则竖式运算的算法如图4-22所示 R2R3R7R6R3R7 LHR3R7R2R7 LR2R7 HR3R6 LR3R6 HR2R6 LR2R6 HR2R3R4R5+具体程序如下:入口参数:被乘数在R2、R3中,乘数在R6、R7中。 出口参数:乘积在R2、R3、R4、R5中。MULD: MOV A,R3 ;计算R3R7MOV B,R7MUL ABMOV R4,B ;R4= R3R7HMO

59、V R5,A ;R5= R3R7LMOV A,R3 ;计算R3R6MOV B,R6MUL ABADD A,R4 ;R3R7H+ R3R6LR4MOV R4,ACLR AADDC A,B MOV R3,A ;R3R6H+CYR3MOV A,R2 ;计算R2R7MOV B,R7MUL ABADD A,R4 ;R2R7L+ R3R7H+ R3R6LR4MOV R4,AMOV A,R3ADDC A,B ;R2R7H+ R3R6H+CYR3MOV R3,ACLR ARLC AXCH A,R2 ;计算R2R6MOV B,R6MUL ABADD A,R3 ;R2R7H+ R3R6H+ R2R6LR3MOV

60、R3,AMOV A,R2 ADDC A,B ;R2R6H+CYR2MOV R2,ARET【例4-22】双字节无符号数除法。分析:对于多字节除法,不能像多字节乘法一样使用单字节除法指令,通常用的算法是参照手算除法的方法进行“移位相减”:首先判断被除数(后为余数)是否大于除数,若成立,则该位商上1,从被除数(余数)中减去除数;否则,商上0,不减除数。然后把被除数的下一位左移到余数后面,再通过比较其大小决定是否与除数相减。重复这个过程知道余数为0或商的位数满足要求为止。通常情况下,若除数和商均为双字节,则被除数为4个字节;若被除数的2个高位字节大于或等于除数则商不能用两个字节表示,发生溢出。因此做除

61、法时应首先判断除数是否为0或发生溢出,若溢出则子程序返回,否则进行除法运算。设有算式:R2R3R4R5R6R7,商=R4R5 ,余数=R2R3,则其算法流程图如图4-23所示。开始B循环次数R2R3R4R5左移1位商上1结束YNB-1=0?YN除数=0?R2R3R6R7?被除数-除数够减?F00NYYNFO1其具体程序如下:入口参数:被除数为R2R3R4R5,除数在R6、R7中。出口参数:F0=0 时,双字节商在R2、R3中,F0=1 时溢出或除数为0。DIVD: MOV A,R6JNZ OVER ;除数不为0,转移MOV A,R7 JZ RE1 ;除数为0,置结束标志返回OVER:CLR C

62、 ;被除数高2个字节除数?MOV A,R3 SUBB A,R7MOV A,R2SUBB A,R6JC DIV1 ;若高2个字节除数,则转移RE1:SETB F0 ;否则溢出标志置1返回RETDIV1: MOV B,#16 ;无溢出,作除法DIV2: CLR C ;部分商和余数同时左移一位MOV A,R5RLC AMOV R5,AMOV A,R4RLC AMOV R4,AMOV A,R3图4-23 【例4-22】程序流程图RLC AMOV R3,AXCH A,R2RLC AXCH A,R2MOV F0,C ;保存溢出位CLR CSUBB A,R7 ;计算(R2R3R6R7)MOV R1,AMOV

63、 A,R2SUBB A,R6JB F0,DIV3 ;结果判断JC DIV4DIV3:MOV R2,A ;够减,存放新的余数MOV A,R1MOV R3,AINC R5 ;DIV4: DJNZ B,DIV2 ;商在R4R5中MOV A,R4 ;将商送到出口参数R2R3中MOV R2,AMOV A,R5MOV R3,ACLR F0 ;F00,设置除法完成标志RET【例4-23】单字节整数平方根运算。分析:求平方根的算法主要有牛顿迭代法、直接法等,这里介绍利用等差数列求和公式求平方根的算法。等差数列求和公式:n2=1+3+5+(2n-1) (1)对于任一正整数N,总可以找到这样的n,使得公式:N=n

64、2+ (2)成立,其中n为N的平方根,为误差,将(1)式代入(2)式,则有:N=1+3+5+(2n-1)+因此只要从N中减去1,3,5,(2n-1),直到不够减为止,则减去的奇数的个数则为N的平方根的整数部分。设子程序入口参数为:R1,出口参数为R2,其具体程序如下: SQRT:MOV R2,#0 CLR CLOOP:MOV A,R2 ;RLC A ;R22AINC A ;A加1,求奇数MOV R4,AMOV A,R1SUBB A,R4 ;R1-奇数MOV R1,AJC DONE ;不够减,结束INC R2 ;R2+1R2SJMP LOOPDONE:RET4.3.3 延时程序在单片机应用系统中

65、,常常需要用到准确的延时,一般来说,延时的设计主要通过两种途径实现:使用硬件定时器延时或使用软件延时。这里介绍软件延时。所谓软件延时,即通过程序达到延时的目的。具体来说,即设计一循环子程序,子程序的功能为用循环程序将指令重复多次执行一些无用的操作以达到延时的目的,通过修改循环次数,便可获得不同的延时时间。下面将通过具体的例题介绍延时程序的编写方法。注意:编写软件延时程序时,应特别要禁止中断,否则会影响软件延时的精度。【例4-24】已知单片机系统的晶振频率为12MHZ,试设计一软件延时程序,延时时间为50ms。程序如下:源程序机器周期(M) 指令执行次数DELAY: MOV R1,#50 1 1

66、 D1: MOV R2,#M1 R1 D2: NOP 1 R1R2 NOP 1 R1R2DJNZ R2,D2 2 R1R2DJNZ R1,D1 2 R1 RET 2 1外循环内循环该例题采用双重循环结构来实现延时。程序中内循环次数以M代替,M的实际数值可通过延时时间计算得出,现分析如下:机器周期数是执行一条指令所需的时间,所以延时时间可通过下式计算得出:T总=延时子程序总机器周期数机器周期。由于该单片机晶振频率fosc为12MHZ,因此其机器周期tj为:其中,内循环机器周期数=1+1+2,假定内循环实现延时1ms,则应有下式成立:1ms=内循环机器周期数机器周期内循环次数,即1103=(1+1

67、+2)1M,由此得: M= 将M代入子程序中,可计算得出总的机器周期数为: (1+1+2)250 + 2 + 1)50 + 1 + 2 =50153 其中,为内循环机器周期数为MOV R2,#M和DJNZ R1,D1两条指令的机器周期数为外循环次数 为MOV R1,#50和RET两条指令的机器周期数所以延时时间T总=501531s=50.153ms可以看出,决定延时时间的因素主要有两个:即循环次数与晶振频率。在晶振频率一定的情况下,可以通过增大循环次数和使用多重循环的方法增长延时时间。此外,还应注意将循环以外指令所花费的时间计算在内,否则将影响精度。思考:设有延时子程序如下,若单片机系统的晶振

68、频率为6MHZ,请读者分析它的延时时间。DELAY:MOV R6,#100D1:MOV R7,#100D2:NOP DJNZ R7,D2 DJNZ R6,D1 RET 4.3.4查表程序表格是一种基本数据结构,在实际应用中,有些时候可以通过计算得到结果,有些时候也可以通过查表法来解决。所谓查表法,即预先将计算或实验得出的数据组成表格,存放在程序存储器中,使用时根据给定条件在表中查找对应的数据。在很多情况下,由于查表法无需通过复杂计算即可获得所需结果,因而大大简化了程序,提高了程序效率,但是如果表格太大则会占用较多的存储空间资源。查表程序在单片机应用系统中应用较为广泛,常应用于代码转换、代码显示

69、、查表取得分支程序地址实现散转、查找指定值等,例如子程序章节中介绍的【例4-13】就是一个通过查表求平方的例子。查表程序的效率取决于表格的排列组织及查表算法。一般来说,分为无序表和有序表。所谓无序表,即表中数据任意排列,没有规律。所谓有序表,即表中数据按一定规律排列。根据表的组织特点不同,可采用不同的查表方法,如:计算查表法、顺序查表法、对分查表法等,其中尤以计算查表应用较多。MCS-51指令系统提供了两条实现查表的指令:MOVC A,A+DPTRMOVC A,A+PC不管使用哪种查表方法,其步骤基本相同,即:使用第一条指令查表时, 一般以DPTR作为基址寄存器。首先将所查表格的首地址存入DP

70、TR中,然后将所要查找的表格元素在表中位置项数送到累加器A中,最后执行查表指令MOVC A,A+DPTR,即可把表中找到的相应数据送回累加器A。使用第二条指令查表时,一般以PC的内容作为基址。首先将所要查找的表格元素在表中的位置项数送入累加器A,然后通过执行相应指令将偏移量(指在执行MOVC A,A+PC后,从当前PC值到表格首地址之间的距离)加到累加器A中,最后执行查表指令MOVC A,A+PC获得查表结果。注意,使用该指令时,偏移量的计算是实现正确查表的关键,通常可按下式计算:偏移量=表格首地址-(MOVC A,A+PC指令所在的地址+1)下面将通过例题对几种查表方法进行介绍。【例4-25

71、】分别用上述两条查表指令求Y=X!。设X(0X5)分析:首先通过计算将05的阶乘值求出组成表格,然后分别应用查表指令。设X在片内RAM的30H单元中,查表结果Y存入片内RAM31H单元。应用MOVC A,A+DPTR查表指令,程序编写如下:ORG 1000HFACT:MOV DPTR,#TAB;取表首地址MOV A,30H;XAMOVC A,A+DPTR;查表求Y=X!MOV 31H,A;保存YRET ;子程序结束 ;其它程序段ORG 2000H;常数表格首地址TAB: DB 01,01,02,06,24,120 ;阶乘表应用MOVC A,A+PC查表指令,程序编写如下:ORG 1000H F

72、ACT:MOV A,30H ;取 XA ADD A,#3 ;3为偏移量MOVC A,A+PC ;查表求Y=X!,1字节指令MOV 31H,A ;存结果 ,2字节指令RET ;返回,1字节指令TAB:DB 01,01,02,06,24,120 ;阶乘表可以看出,从查表指令MOVC到表格首地址TAB之间的距离(即偏移量)为3个字节单元,所以MOVC指令之前使用ADD A,#3指令跳过表格前的两条指令以获得相应的表格数据。【例4-26】现有一长度为200个字节的无序表,存放于首地址为TAB的地址单元中,试编程查找表中是否存在KEY值,若找到,则将KEY值所在的地址单元存于R4R5中,若未找到则将R4

73、R5清0,并置标志F0为1。分析:对于无序表的查找,通常采用顺序查表法,即从表的第一个元素开始依次比较查找,直到找到所查关键字或已到表的结尾为止。其算法流程图如图4-24所示。 开始指向下一个地址R2表长度,F01R4R50结束YN与KEY相等?R2=0?F00NYYR4R5DPTRKEY关键字取表中的数KEY EQU 20HTEST EQU 21HORG 0100HMOV TEST,KEY ;取关键字MOV R2,#200 ;R2表的长度SETB F0 ;置查找标志MOV DPTR,#TAB ;取表首地址FIND: MOV A,#0MOVC A,A+DPTR ;取数程序如下:CJNE A,T

74、EST,L1 ;比较,未找到,转L1MOV R4,DPH ;已找到R4R5地址MOV R5,DPLCLR F0 ;置找到标志 DONE: SJMP DONE L1: INC DPTR ;取下一个数的地址DJNZ R2,FIND ;未完,继续 MOV R4,#0 ;已找完,未找到R4R50 MOV R5,#0 AJMP DONETAB: DB xxH本章小结本章小结本章重点介绍了汇编语言程序设计的方法。现小结如下:1汇编语言程序设计步骤:(1)分析问题,建立数学模型(2)了解系统的硬件配置和性能指标,确定方案算法(3)用流程图表示出程序算法(4)根据流程图编写源程序(5)调试运行程序。2汇编控制

75、指令伪指令ORG DB DW DS EQU DATA BIT END3常用程序结构: 顺序程序、分支程序、循环程序、子程序顺序程序又称简单程序,程序的走向只有顺序执行一条路径。分支程序由条件转移指令构成程序判断部分,根据条件是否成立转向不同的程序分支。包括:单分支、双分支和多向分支程序结构。其中,多向分支的实现方法有四种:(1)多次使用条件转移指令(2)使用分支地址表(3)使用转移指令表(4)使用地址偏移量表循环程序主要针对具有多次重复执行的程序段的程序结构,包括:单重循环和多重循环(即循环嵌套),其程序结构为:(1)初始化部分(2)循环体(3)循环控制部分(4)结束部分 循环结束的控制方法主

76、要有两种:对于已知循环次数的情况采用计数控制(如使用DJNZ指令);对于循环次数未知的情况采用设定循环结束特征标志的方法实现。子程序是能完成某项特定功能的独立程序段,可被反复调用。子程序应满足通用性的要求,一般不针对具体数据编写子程序。对于使用子程序应注意的事项:(1)子程序入口即子程序的第一条指令,应使用标号作为子程序名。(2)调用子程序之前设置好堆栈,对于子程序嵌套须考虑堆栈容量。(3)用返回指令RET结束子程序,并保证堆栈栈顶为调用程序的返回地址。(4)为了便于调用,子程序应提供足够的信息。如:子程序名、子程序功能等。(5)正确传递参数,可根据具体情况选用不同的参数传递方式,如:寄存器传

77、送、存储器传送或堆栈传送。思考与练习思考与练习一、思考题1手工汇编与机器汇编有何不同?2使用汇编语言编写程序与使用高级语言编写程序有何不同?3实现多向分支程序的方法有几种?试举例说明。4举例说明循环程序的结构。5子程序应中如何对参数进行正确传递?6为什么子程序嵌套要考虑堆栈容量?二、习题1.经过汇编后,下列各语句的标号是什么数值?ORG 1000HTAB:DS 10WORD:DB 15,20,22,30START: MOV A,R12.经过汇编后,从1000H开始后的各存储单元的内容如何?ORG 1000HTAB:DS 5DB 10H,10DW 1100HORG 1050HDW TABDB “

78、ABCD”3. 将 片 内 RAM 3AH和 3BH单 元 中 的 BCD码 转 换 成 ASCII码 , 并 存 入 40H、 41H单 元中。4.编程将片外RAM2000H开始的100个连续单元清零。5.统计一班考试为100分和不及格人编程将片内RAM20H单元中的8位二进制数转换为ASCII码,并保存到DATA开始的存储单元区域。6.数,并将结果存入60H和61H单元中。已知成绩单存放在20H起始的单元中,且该班有30人。7.试用转移指令表和分支地址表法编写程序实现:根据累加器A中的命令字转至不同的分支子程序,设A中的命令字分别为1、2、3、4、5、6,转向的6个相对应的子程序分别为SU

79、B1、SUB2、SUB3、SUB4、SUB5、SUB6。8.设X、Y为4位压缩BCD码,求 Z=X+Y。9.编程将片外RAM2000H单元2100H单元中的数据逐个搬到1000H开始的区域。10.已知片内RAM中以ADDR为起始地址的区域中存放着20个无符号数,编程找出最大值和最小值,并将结果送入MAX和MIN单元。11.编程计算片内RAM30H39H单元共10个数的算术平均值,结果存入3AH单元中。12.从片内RAM的BLOCK单元开始存放一组用补码表示的带符号数,其数目已存放在30H单元。编程统计出其中正数、负数和0的个数,并将结果存入31H、32H、33H单元中。 13.编写子程序将片外

80、1000H单元100A单元中的单字节正整数按从小到大顺序排列。14.若晶振为6MHZ,试编写延时10ms、100ms、1s的子程序,并列出延时时间的计算分析。15.设某单片机晶振为12MHZ,分析下列程序段的功能。DELY:MOV R1,#63HD1:NOPNOPDJNZ R1,D1RET16.编程将两个4位二进制数存于片内的某一个单元中。17.编程将片内RAM中20H、21H单元中的16位数求补,并将结果存入片外1000、1001单元中。18.编程将累加器A内容低4位送片外RAM60H单元,高4位送片外61H单元,并将60H和61H单元的高4位均清零。 19.编程求分段函数的值。已知x值存放

81、于1000H单元中,将函数值Y存于1001单元中。X+10X1020.17编写一查表程序,已知数据块的首地址为BLOCK,数据块以-1作为结尾,要求找出ASCII码F,并将其地址存入200A和200B单元中,若未找到,则将FFH分别存入200A和200B单元中。21.在BUF为首地址的片外RAM存放一个数组,以FFH为结束符。试编程序,将该数组的存放区域清零,保留结束符FFH22.请使用查表指令编程将ROM中以TAB为首地址的24个单元的内容依次传送到片外RAM1000H为首地址的区域中。23.已知存储单元20H和21H中分别存放一8位无符号二进制数a和b,编程求2a+3b,并将结果存入22H

82、单元中,设结果小于255。24.已知下列程序段要将R3、R2(低位)中的16位二进制数乘2后,存回R3、R2中(设结果仍为两字节)。请找出错误,并改正之。MOV A,R2ADD A,R2MOV R2,AMOV A,R3ADD A,R3MOV R3,A25阅读下列程序,(1)说明程序的功能;(2)写出执行程序后寄存器R3的结果。MOV R0,#01H CLR A MOV R2,#09HLOOP:ADD A,R0 INC RO DJNZ R2,LOOP MOV R3,AHERE:SJMP HERE26阅读下列程序,分析程序的功能及程序执行后各寄存器及片内RAM单元内容的变化。 (1)MOV R0,#40H MOV A,R0 INC R0ADD A,R0INC R0MOV R0,ACLR AADDC A,#0INC R0MOV R0,A(2)已知片内RAM的DATA单元中存放着一个小于20的无符号数。MOV R0,#DATAMOV A,R0RL AMOV R1,ARL ARL AADD A,R1MOV R0,A

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 研究生课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号