D幻灯片定稿MCS汇编语言程序设计.pptx
2汇编语言是一种符号语言,指令由助记符表示。与机器语言相比,汇编语言具有指令容易记忆、理解、识别和可读性好的优点,但实质上由于汇编语言也是面向机器的语言,是机器语言程序的符号表示,所以用户必须熟悉机器的硬件结构和指令系统,掌握计算机的工作过程才能熟练编程。汇编语言程序通常用于实时控制领域,其所能完成的操作不是一般高级语言所能实现的,而且源程序经汇编生成的可执行文件一般运行效率较高。另外,汇编语言所编写的源程序与经过汇编所产生的机器代码程序之间有明显的一一对应关系,所以汇编语言编写的程序同样也存在通用性差,程序不能移植的缺点。第1页/共86页3高级语言高级语言是接近于人的自然语言形式的计算机编程语言的总称,例如C语言、BASIC语言等都是高级语言。和汇编语言相比,高级语言指令简单易学,用户容易掌握,且高级语言程序不依赖于具体的硬件结构和指令系统,程序可移植性好,但是高级语言编写的源程序必须经过编译或解释程序翻译成目标程序,机器才能执行,而生成的目标程序需占用较多的存储单元,执行时间较长,运行效率较低。由于目前有些高级语言不具有直接处理接口和中断技术的功能,因此高级语言一般很少应用于实时控制,但随着语言的发展,这种情况将会有所改变。第2页/共86页4.12 单片机汇编语言源程序的编辑和汇编汇编语言的指令类型用汇编语言编写的、具有特定功能的指令序列,称为汇编语言源程序。源程序由两种类型的汇编语言语句(即指令)构成。语句是汇编语言的基本组成单位,按性质不同分为两类:指令性语句(机器指令)和指示性语句(伪指令)。(1)机器指令 即指令系统中的全部指令,每条指令都有对应的机器代码,是机器真正能够执行的指令。(2)伪指令为汇编程序在汇编过程中提供控制或指示信息,并不直接产生机器代码,属于机器不能执行的指令类型。汇编计算机不能直接识别和执行源程序。因此源程序必须经过汇编程序汇编产生机器码目标程序文件,程序才能执行。这种将汇编语言源程序转换成机器语言程序的过程称为汇编。对于初学者来说,应注意汇编语言源程序和汇编程序两个术语的区别,它们的功能示例如图4-1所示。第3页/共86页图4-1 汇编过程汇编语言源程序汇编程序目标文件和源程序列表经过汇编后第4页/共86页汇编语言源程序的汇编过程分为手工汇编和机器汇编。所谓手工汇编,即采用人工查指令表的方法将汇编指令翻译成相应机器代码。通常源程序的人工汇编需要两次才能完成,第一次汇编查找每条指令的机器代码,第二次汇编完成地址偏移量的计算。由于手工汇编需要计算和查找,繁琐而且容易出错,而且程序修改时可能会引起指令的地址变化,转移指令的偏移量也随之改变,需要重新计算,所以手工汇编主要应用于设计短小程序或调试程序的场合。所谓机器汇编,即采用汇编程序对源程序进行自动汇编,由于单片机软硬件资源的限制,该过程通常借助于PC系统机实现,象这种借助于一种计算机而为另一种计算机产生目标代码的汇编方式又称为交叉汇编。交叉汇编的原理类似于手工汇编,在汇编程序中通常存入了两张表,即MCS-51单片机的指令代码表和伪指令表。汇编程序通常通过两次扫描完成汇编,第一次扫描查找源程序中每条指令的机器代码,第二次扫描完成地址偏移量的计算,汇编后生成目标文件和列表文件。这是目前应用较广的汇编方法。第5页/共86页纵上,汇编的主要功能为:(1)确定程序中每条汇编语言指令的指令机器码。(2)确定每条指令在程序存储器中的存放地址。(3)提供语法、编辑等方面的错误信息,但不能提供程序的逻辑错误。(4)产生目标执行文件(*.OBJ/*.HEX)和列表文件(*.LST)。第6页/共86页4.1.3 MCS51汇编语言的伪指令所谓“伪”指令,即不是真正的可执行指令。如前所述,伪指令只能在对源程序进行汇编时起控制作用,例如设置程序的起始地址,定义符号,给程序分配一定的存储空间等。常用的伪指令共有8条,下面分别介绍。1ORG(Origin)设置起始地址指令用来设定程序或数据存储区的起始地址。指令格式为:ORG16位地址例如,有程序段如下,则表示程序存放的起始地址为1000H。指令地址机器码源程序ORG1000H1000H782MAIN:MOVR0,#20H1002HE6MOVA,R0若省略ORG指令后的16位地址,则汇编后目标程序的起始地址默认为0000H。ORG指令在一个源程序中可以多次使用以指定不同程序段的起始地址,但是指定的多个地址应从小到大,不能使程序段之间产生重叠。第7页/共86页2DB(DefineByte)定义字节型常数指令指令的功能为从指定的存储单元开始,定义或存储一个或多个字节数据。指令格式为:标号:DB字节常数表例如,ORG1000HTAB:DB0A3H,18,AB汇编结果为:(1000H)=A3H(1001H)=12H(1003H)=41H(1004H)=42H3DW(DefineWord)定义字型常数指令指令的功能为从指定的存储单元开始,定义或存储一个或多个字数据。通常用于在程序中定义地址表。指令格式为:标号:DW字常数表一个字数据占用两个存储单元,存放时高字节存入低位地址,低字节存入高位地址。例如,ORG1000HTAB:DW1234H,18,00A3H汇编结果:(1000H)=12H(1001H)=34H(1002H)=00H(1003H)=12H(1004H)=00H(1005H)=A3H第8页/共86页4.EQU(Equate)等值指令指令功能是把操作数段中的地址或数据赋值给标号。赋值后的标号可在整个源程序中使用。指令格式如下:标号EQU数据或汇编符号注意,与程序中一般意义的标号不同的是,这里的标号后不能加“:”。例如,ADDR EQU1000H;给ADDR赋值1000HDAT1 EQU0AH;给DAT1赋值0AHMAIN:MOVDPTR,#ADDR;DPTR1000HMOVA,DAT1;A(0AH)上例中,ADDR被赋值以后,在程序中作为立即数使用,而DAT1赋值后被当作直接地址使用。需要说明的是,使用EQU指令时必须先赋值后使用。第9页/共86页5DS(DefineStore)定义存储区指令指令功能为从指定的单元地址开始,保留一定数量的存储单元,以备使用。指令格式如下:标号:DS表达式其中,表达式指定保留的内存单元个数。例如,ORG1000HDS10TAB:DB10H汇编结果:从1000H地址处开始,保留10个字节单元。(100AH)=10H。6BIT位地址符号指令指令功能为将位地址赋值给指定的符号名称,通常用于位符号地址的定义。指令功能如下:字符名称BIT位地址例如,X1BITP1.0汇编结果为将位地址P1.0赋给变量X1,即在程序中便可使用X1代替位地址P1.0。第10页/共86页7DATA数据地址赋值指令指令功能为将数据地址或指令代码地址赋值给所指定的标号。通常在程序中用于定义数据地址。指令格式如下:标号:DATA表达式例如,AA:DATA2000H汇编结果为:AA=2000H。注意,DATA与EQU的区别在于:EQU定义的标识符在汇编时不在符号表中登记,因此必须先定义后使用,而DATA定义的标识符汇编时在符号表中登记,所以可以先使用后定义。8.END汇编语言源程序结束指令指令功能,表示源程序到此结束,END指令以后的指令汇编程序将不予处理。一个程序中只能在末尾有一个END指令。例如,START:ENDSTART第11页/共86页4.2汇编语言程序设计对于简单程序的编写,程序设计者往往能够立即完成软件的构思与编写,而对于比较复杂的程序设计问题,则需要科学合理的程序设计步骤。从软件工程角度来说,开发一个应用程序,一般需要经过以下几个步骤:1分析问题,建立数学模型如果拿到问题,立即着手编写程序代码往往是很难成功的,通常需要首先分析题目的已知条件,了解系统的硬件配制,明确题目的要求和要实现的功能,然后建立数学模型。2了解系统的硬件配置和性能指标,确定方案算法所谓算法就是为了解决问题而采取的方法和步骤。在分析问题后,应根据系统的具体硬件配置和性能指标等实际情况确定具体有效且计算机能够执行的方法和步骤。3用流程图表示出程序算法确定算法后,应用简单直观的方法描述算法,以便为进一步编程做好准备。算法的描述方法很多,其中比较常用的是流程图法。一般流程图的符号如图4-2所示。4根据流程图编写源程序使用汇编语言编写源程序时,应首先合理规划和分配存储器单元,确定程序和数据的存放区域,了解系统的I/O接口地址,最后按照流程图写出源程序。5调试运行程序通常情况下,源程序编制好后必须经过上机调试才能运行。调试程序的一个重要功能是修正语法和逻辑错误,直到达到题目的要求为止。第12页/共86页4.2.1 顺序程序设计顺序程序是程序设计中最基本的结构,又称为简单程序。特点为顺序执行每一条指令,直到最后。其执行过程如图4-3所示。S1S2图4-3 顺序结构第13页/共86页【例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所示。程序如下:ORG2000HMOVDPTR,#1000HMOVR0,AANLA,#00001111BORLA,#30HMOVDPTR,AMOVA,R0SWAPAORLA,#30HINCDPTRMOVXDPTR,AEND第14页/共86页【例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;高位字节之和存入35H中 MOV A,#0 ADDC A,#0 DA AMOV 36H,A;高位字节的进位存入36H中END。从该例可以看出,多字节BCD码相加时,应从低位字节开始相加,每进行一次加法运算进行一次BCD码调整才能得到正确结果。第15页/共86页4.2.2 分支程序设计1分支程序结构的基本形式 分支程序是程序设计中应用非常广泛的一种基本结构。比如我们经常遇到需要计算机进行逻辑判断的情况,然后根据判断的结果进行不同的处理。例如,比较两个数的大小,并输出判断结果;根据输入压力的不同,用不同的方法计算发动机功率等这些问题都是顺序结构程序所无法实现的,而是属于分支结构程序设计的范畴。具体来说,如图4-6所示,分支程序结构具有以下三种形式。第16页/共86页条件S1S2YN条件S1条件NS2S1Sn(a)单分支(b)双分支(c)多分支第17页/共86页2分支程序的设计方法依据分支结构程序的执行过程,结合MCS-51的指令系统可以得出,分支程序的实现需要通过条件转移指令完成。因此如何设定分支条件便成为分支程序设计的关键。具体来说,其设计要点可归纳如下:(1)设定可供条件转移指令测试的条件。通常,可以作为转移指令判断条件的有标志位状态(如进位位C的状态),累加器A或片内RAM中某位的结果状态等。因此为了提供条件转移指令的测试条件,应在程序的转移指令前设定影响判断条件的标志位状态,或通过逻辑运算、算术运算等指令影响标志位。例如,若要使用JZ指令实现分支,则应在该指令前执行影响累加器A的指令。(2)根据标志位状态选择正确的条件转移指令。例如,要判断进位位,可使用JNC或JC等指令。(3)应在转移的目的地址处设定标号。第18页/共86页【例4-4】求8位有符号数的补码。设8位二进制数存放在片内RAM30H单元内。分析:对于二进制数的补码负数可用取反加1的方法求得,而正数不变。ORG 2000HMOV A,30HJNB ACC.7,NEXT;为正数,不进行处理CPL A ;负数取反INC A ;加1MOV 30H,ANEXT:SJMP NEXT ;结束可以看出,该程序通过符号位是否为0来判断该数为正数还是负数,若为正数则程序结束,若为负数则变补。显然,这是一个单分支的例子。第19页/共86页【例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,转至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该例是一个双分支程序,通过比较转移指令CJNEA,#10,L1和条件判断指令JCNEXT实现程序的分支,这种编程方法在分支程序设计中应用非常广泛,对于初学者应仔细分析体会。第20页/共86页【例4-6】求符号函数Y=F(X)+1 (当 X0时)F(X)=0 (当 X=0时)-1 (当 X0时)Y=0Y=1Y=1保存Y结束X0?NY开始取XYNX0?分析:这是一个多分支程序示例。设计时可考虑将判断变量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$第21页/共86页Y=0Y=1Y=1保存Y结束X0?NY开始取XYNX0?第22页/共86页3.分支表法实现多向分支程序的设计上例中符号函数程序是一个多向分支程序的例子,直接采用了条件转移指令来实现,但在实际应用时,通常遇到根据某变量取值不同从而转向不同分支的题目,这时经常采用分支表法。常用的分支表主要有三种形式:转移指令表、分支地址表和地址偏移量表。虽然分支表的构成各异,具体的编程方法也略有不同,但实质上都是利用散转指令JMP A+DPTR来实现的。实现散转的方法主要有两种:(1)累加器A清零,根据DPTR的内容决定转移的目标地址。(2)DPTR的值作为基址不变,根据累加器A的内容决定转移的目标地址。下面通过一个具体的例题,说明使用分支表实现多向分支程序设计的方法。第23页/共86页【例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转移指令表第24页/共86页设转移指令表的标号为JMPTAB,分支数为5,解题思路分析如图4-9所示。DPTRJMPTABA+DPTRJMPTAB+R23JMP A+DPTRLJMP PROG0LJMP PROGnLJMP PROG1第25页/共86页MOV 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(当使用短转移指令表时M的值为2,当使用长转移指令表时M的值为3),然后使用JMPA+DPTR指令实现分支。具体程序如下:第26页/共86页说明,因为转移指令表由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 ;若有进位,将进位加到高字节DPHNOADD:JMP A+DPTR ;散转JMPTAB:AJMP PROG0 ;转移指令表AJMP PROG1 AJMP PROG5 PROG0:PROG1:PROG5:第27页/共86页(2)使用分支地址表。所谓分支地址表是指由各个分支程序的入口地址组成的线性表,每个入口地址占两个连续字节单元。设PROG0PROGn为分支程序入口地址,分支地址表如图4-10所示。BRANCHTAB:DW PROG0 DW PROG1 DW PROGn图4-10分支地址表DPTRBRANCHTABAR22JMP A+DPTR 分支程序PROG0 分支程序PROGn分支程序PROG1DPTR应用MOVC A,A+DPTR取分支地址A0图4-11分支地址表的使用设分支地址表的标号为BRANCHTAB,分支数为n,使用分支地址表实现多向分支的解题思路分析如图4-11所示。第28页/共86页DPTRBRANCHTABAR22JMP A+DPTR 分支程序PROG0 分支程序PROGn分支程序PROG1DPTR应用MOVC A,A+DPTR取分支地址A0图4-11分支地址表的使用第29页/共86页根据以上解题思路,例4-7程序编写如下:ORG 1000HMAIN:MOV DPTR,#BRANCHTAB ;取分支表入口地址 MOV A,R2 CLR C ;AR22 RLC A JNC NOADD INC DPH ;进位加到DPH中NOADD: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第30页/共86页(3)使用地址偏移量表。所谓地址偏移量表,是指由各分支程序段的入口地址与地址偏移量表的标号差(即地址偏移量)形成的线性表。其中地址偏移量表中每项占一个字节,如图4-12所示。TAB:DB PROG0-TAB ;PROG0PROGn为分支程序入口地址 DB PROG1-TAB DB PROGn-TAB图4-12 地址偏移量表第31页/共86页DPTRTABAR2JMP A+DPTR 分支程序PROG0 分支程序PROGn分支程序PROG1应用MOVC A,A+DPTR取分支地址偏移量图4-13地址偏移量表的使用第32页/共86页根据以上解题思路,例4-7程序编写如下:ORG 1000HMOV DPTR,#TAB ;取偏移量表首地址MOV A,R2 MOVC A,A+DPTR ;将查表所得PROGi-TABAJMP A+DPTR ;由A+DPTR=PROGi-TAB+TAB=PROGi获得分支程序地址实现跳TAB:DB PROG0-TAB ;地址偏移量表DB PROG1-TAB DB PROGn-TABPROG0:PROGn:使用地址偏移量表实现多向分支时,应注意要使地址偏移量表与各分支程序的长度和在同一页(256字节)范围内,因此该方法适用于分支较少的情况。第33页/共86页4.2.3 循环程序设计在程序设计中,常常会涉及到重复执行的程序段,这可以通过循环结构来实现,循环结构可使程序更加紧凑。1循环结构程序的构成与高级语言中循环程序的构成相似,汇编语言的循环程序结构主要包括以下四个部分:(1)循环初始化部分。所谓初始化即设置循环开始时的状态,如清结果单元、设置地址指针、设定寄存器初值、循环次数等。(2)循环体部分。循环体是循环结构的主体,为需要重复执行的程序段。(3)循环控制部分。这一部分主要完成循环条件的设定,循环控制变量的修改以及检测循环条件是否仍然满足,若条件成立则继续循环,否则结束循环。(4)结束部分。该部分主要完成循环结束后的结果处理工作,例如结果的保存、计算等。其执行过程如图4-12所示。第34页/共86页循环初始化执行循环体循环条件是否成立?N开始循环结束,结果处理Y结束修改循环变量循环初始化执行循环体循环条件是否成立?N开始循环结束,结果处理Y结束修改循环变量体第35页/共86页2简单循环程序所谓简单循环即一重循环,也就是循环程序中只包含一个循环,不嵌套其它循环的循环程序。另外,设定循环执行的条件非常重要,否则可能形成死循环。编写汇编语言程序时,通常采用两种控制循环的方法。一种是使用计数的方法实现循环,即将循环次数作为循环计数器的初值,当计数器的值加满(称为正计数)或减为0(称为倒计数)时结束循环,否则继续循环,该方法适用于循环次数已知的情况。另一种为通过设定特定条件控制循环,若设定条件满足则执行循环,否则结束循环,例如设定特定的循环结束标志等,该方法适用于循环次数未知的情况。第36页/共86页【例4-8】数据块求和。设内部RAM中有一连续单字节数据块,首地址为BLOCK单元,数据块长度存于LEN单元,若数据累加和也为单字节数据,并存于RESULT单元,编程求数据块之和。分析:累加和的求解应使用循环,且数据长度已知,所以循环次数已知,因此可考虑使用指令DJNZ来控制循环条件,其算法流程图如图4-15所示。A0,R0BLOCK,R1LEN累加求和R10?N开始RESULT和Y结束R0+1,R1-1第37页/共86页程序如下: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可以看出,这是一个循环次数已知的例题。第38页/共86页【例4-9】求均值。已知一控制系统,从P1口读入采样值,每周期采样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次采样值是否累加完?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第39页/共86页【例4-10】求最小值。设内部RAM中有一无符号数数据块,其首地址为BLOCK,长度存于LEN单元,试求出数据块中的最小值,存入MIN单元。分析:求最小值时,通常采用比较交换的方法。即首先取第一个数作为基准,然后将基准数与第二个数进行比较,若基准数大于第二个数,则两数进行交换;若基准数小于第二个数则不进行交换,总之保证基准数单元中的值为最小值,再取下一个数与基准数进行比较,一直到所有数据比较完为止,则基准数单元中的数则为最小值。依此类推,求最大值也可采用类似方法。其算法流程图如图4-16所示。第40页/共86页A0,R1BLOCK,R2LEN清CY AA(R1)R20?N开始MINAY结束R1+1,R21CY=1?A(R1)NAA+(R1)Y第41页/共86页具体程序编写如下: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 ;A(R1),则A(R1)SJMP NEXT1NEXT:ADD A,R1 ;A(R1),恢复ANEXT1:INC R1DJNZ R2,LOOPMOV MIN,A ;存最小值HERE:SJMP HERE 第42页/共86页【例4-11】设片内RAM中有一无符号数数据块,其首地址为BLOCK,长度未知,但数据结束标志存于LEN单元,求数据块的最小值并存于MIN单元中。ORG 1000HMIN:DATA 20HLEN:DATA 21HBLOCK:DATA 22HMAIN:MOVR1,#BLOCK ;数表首地址 MOV B,R1 ;取第一个数作为基准NEXT:INCR1 ;修改指针 MOV A,R1 CJNE A,LEN,NEXT1 ;是否为数表结尾?SJMP DONE ;循环结束NEXT1:CJNE A,B,NEXT2 ;比较NEXT2:JNCNEXT ;AB,转NEXT继续取数 MOVB,A ;保存较小值 SJMP NEXTDONE:SJMP DONE可以看出,这是一个循环次数未知的例题,通过设定特征值控制循环结束条件。第43页/共86页3多重循环多重循环又称之为循环的嵌套,即在一个循环结构的循环体内,又包含另一个完整的循环结构。在实际应用中,有时一重循环并不能解决问题,所以循环的嵌套应用还是比较广泛的。在嵌套循环的使用中,被嵌套的循环可以不止一个,并且可以嵌套多层,但不论是哪种情况,内层循环和外层循环都必须是一个完整的结构,不允许有相互交叉的情况出现。即如出现图4-17(a)所示的情况则是非法的嵌套,而图4-17(b)所示为正确的嵌套。内层循环外层循环内层循环外层循环图4-17(a)非法嵌套示意图图4-17(b)正确嵌套示意图第44页/共86页【例4-12】数据排序程序。设有N个数据,存于首地址为BLOCK的内存单元中,试设计程序将数据从小到大按升序排列。分析:数据排序的方法很多,这里采用沉底法。沉底法的基本思想:通过相邻两个数之间的比较和交换,使排序码(数值)较小的数逐渐从底部移向顶部,排序码较大的数逐渐从顶部移向底部,就像较大的数往下沉底一样,故而得名。设由R0为数据存放区的首地址,则(R0+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所示。第45页/共86页开始外循环次数R4内循环次数R3(R0)(R0+1)(R0)(R0+1)R0R0+1R3R31=0?R4R41=0?循环结束YNNNYY第46页/共86页ORG 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 ;修改数据指针 MOV R0,ANEXT:DJNZ R3,LOOP2;内循环 DJNZ R4,LOOP1 ;外循环HERE:SJMP HERE在数据采集系统中,若被采集的信号变化较慢,经常采用中值滤波的方法去掉由于偶然因素造成的干扰误差。中值滤波时,首先将采样数据排序,再取中间值作为本次采样的终值。第47页/共86页4.2.4 子程序设计第三章指令系统中已经介绍过子程序的概念与相关指令。子程序是一种重要的程序结构,主要由需要反复执行的操作或程序段构成,以供主程序调用。另外,子程序也可嵌套,即子程序调用子程序,只要堆栈深度足够,子程序可嵌套多层。主程序与子程序以及子程序嵌套调用的关系如图4-19所示。第48页/共86页与一般程序的编写方法相比,子程序具有以下几个特点。(1)子程序应命名。子程序入口即子程序的第一条指令应加标号作为子程序名,以便主程序调用。(2)子程序中应注意堆栈的使用,以保护和恢复现场。在有些情况下,若子程序需要改变主程序中某些寄存器或存储单元的结果,而这些结果又不能被修改或在子程序调用之后仍然需要则应在子程序中首先将这些内容使用进栈指令保护,在子程序返回之前使用出栈指令恢复现场。(3)子程序的结尾必须为子程序返回指令RET,并保证堆栈栈顶为调用程序的返回地址。(4)子程序嵌套时应考虑堆栈的深度。(5)能够正确传送参数。参数分为入口与出口参数。所谓入口参数,即调用子程序之前,需要传给子程序的参数。所谓出口参数,即子程序返回时应送回调用程序的结果参数。应根据具体情况选择不同的参数传递方式,通常使用寄存器、存储器或堆栈的方式传送参数。(6)子程序应具有一定的功能和通用性。对于操作数应尽量使用以地址或寄存器形式给出,一般不针对具体的数据编写子程序。(7)为了便于调用,子程序应提供足够的信息。如:子程序名、子程序功能、入口参数和出口参数、子程序占用的硬件资源、子程序中调用的其他子程序名。第49页/共86页【例4-13】编程实现C=a2+b2。设a、b均小于10且分别存于外部RAM的100H,101H单元,要求运算结果C存于外部RAM102H单元。分析:本题可利用子程序完成求单字节数据的平方,然后通过调用子程序求出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+b2MOV DPTR,#0102H MOVX DPTR,A ;存结果 SJMP$第50页/共86页【例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第51页/共86页【例4-15】编写将累加器A中的ASCII码转换为1位十六进制数的子程序。分析:十六进制数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。第52页/共86页另外,根据ASCII码和十六进制数的对应关系,将1位十六进制数转换为ASCII码的程序可编写如下:HEXASC:CJNE A,#0AH,L1 L1:JNC L2 ADD A,#30H SJMP HERE L2:ADD A,#37HHERE:RET 请读者自行分析该子程序每条语句的功能及入口和出口参数。第53页/共86页4.3 MCS51汇编语言实用程序举例在程序设计时,通常把需要多次使用的程序段设计成具有特定功能的子程序,以供调用,这样可使程序设计避免了重复性的工作,从而使效率提高,而程序也更加灵活。下面将分类介绍一些实用的子程序。4.3.1 代码转换程序【例4-16】将累加器A中的8位二进制数转换成3位BCD码。3位BCD码占用两个字节,结果的百位数存于BAI单元,十位和个位数占用一个字节单元SHIGE。分析:可应用除法指令实现数制的转换,将被转换的数除以100得百位数,再将余数除以10得十位数,余数为个位数。例如:设要转换得二进制数据为0EFH,则0EFH除以100,商=02H,即百位数为2,余数=27H,27H除以10,商=03H,即十位数为3,余数=09H,即个位数为9。其具体程序如下:第54页/共86页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商,B余数SWAP A ;十位数交换到A的高半字节ORL A,B ;与B中的个位组合成一个字节数据MOV SHIGE,A;十位、个位送入单元SHIGE RET该子程序入口参数:累加器A 出口参数:地址单元BAI和地址单元GESHI第55页/共86页【例4-17】编写将多字节二进制数转换成BCD码的子程序。分析:上例中介绍了一种使用除法实现将二进制数转换成BCD码的算法,但上述方法一般针对转换数较小的情况,若被转换数较大,则该算法需要进行多字节运算,运算速度较慢,程序效率较低。若用b代表各二进制位数,则有下式成立:BCD=bn-12n-1+bn-22n-2+b121+b020 =(