单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,第4章 汇编语言程序设计,计算机在完成一项工作时,必须按照顺序执行各项操作。这些操作是计算机程序设计人员按照解决问题的步骤事先描述好的算法,设计出相应的程序,然后由计算机去执行。,程序设计就是用计算机所能接受的形式把解决问题的步骤描述出来。,本章介绍汇编语言程序设计的基础知识,通过实例让学习汇编语言程序的设计方法。,本章总体要求:,熟练掌握汇编语言语句的格式、常用的伪指令和程序流程图,1,掌握汇编语言程序设计的步骤和技巧,2,熟悉顺序、分支、循环结构和子程序调用等程序的应用特征,3,能够独立完成本章作业,4,本章重点:,汇编语言程序设计中常用伪指令的使用,1,汇编语言程序设计的步骤,2,分支与循环描述的理解与实现,3,综合程序的设计,4,本章难点:,汇编语言程序设计算法的确定及绘制程序流程图,1,多重循环程序设计及子程序设计与调用,2,综合应用程序设计的实现,3,4.1.1,程序设计语言,1,机器语言:机器语言是直接面向硬件的二进制代码指令。,2,汇编语言:汇编语言是机器语言的符号表示,与机器语言一一对应,编程效率高,实时性好,但也不能跨平台工作。,3,高级语言:高级语言是一种面向算法和过程的语言 。,4.1,汇编语言简介,4.1.2 汇编语言格式,汇编语言语句格式为:,标号:操作码 操作数 ;注释,1.,标号是由用户定义的符号地址,指明该指令的起始地址。,标号是由英文字母开头的,18,个字母和数字组成的字符串,以冒号“:”结尾。,2,操作码,操作码表示该语句要执行的操作内容,是每一条汇编语言必有的部分。操作码用助记符表示,在操作码后面至少要有一个空格,使它与操作数分开。,3,操作数,操作数是该指令操作所需要的数据。,注意:这一字段可能有也可能没有,当多于一个操作数时,操作数之间用逗号分隔。,操作数字段的内容可以包括以下几项:,(,1,)工作寄存器名;,(,2,)特殊功能寄存器名;,(,3,)标号名;,(,4,)常数;,(,5,),$,:该符号用来表示程序计数器的当前值。该符号常出现在转移指令中;,(,6,)表达式:在汇编时,计算出表达式的值,并把该值填入目标码中。,4,注释,注释不是汇编语言的功能部分,它是语句的说明部分,用于改善程序的可读性,它以分号“;”开始。注释可为一行或多行(每行均以分号开始)。,4.1.3,伪指令,所谓伪指令又称为伪操作,它不象机器指令那样是在程序运行期间由计算机来执行的,它是在汇编程序对源程序汇编时,由汇编程序处理的操作,它们可以完成如数据定义、分配存储器、指示程序结束等功能。,1,汇编起始地址命令,格式:,ORG nn,功能:规定此命令之后的程序或数据的存放起始地址。,ORG,伪指令总是出现在每段源程序或数据块的开始。,2,定义字节伪指令,格式:,标号:, DB X1,X2,Xn,功能:从指定地址开始,存放若干字节数据。,注意:在程序存储器空间定义,8,位单字节数据,通常用于定义一个常数表。,Xi,为单字节数据,它为二进制、八进制、十进制或十六进制数,也可以为一个表达式,还可以是由两个单引号所括起来的一个字符串,或单引号括起来的字符。,3,字定义伪指令,格式:,标号:, DW Y1,,,Y2,,,,,Yn,功能:从指定地址开始,存放若干字数据。,注意:在程序存储器空间定义双字节数据,经常用于定义一个地址表。,Yi,为双字节数据,它可以为十进制或十六进制的数,也可以为一个表达式。高位数在前,低位数在后。,例如:,ORG 1000H,DATA,:,DW 3241H,,,1234H,,,78H,上述程序将对从,1000H,单元开始的,6,个单元赋值,赋值情况如何呢?,(,1000H,),=32H,,(,1001H,),=41H,,(,1002H,),=12H,,(,1003H,),=34H,,(,1004H,),=00H,,(,1005H,),=78H,。,4,汇编结束伪指令,END,该伪指令指出结束汇编,即使后面还有指令,汇编程序也不处理。,5,赋值伪指令,格式:标号,EQU,表达式,功能:将表达式的值(数据或地址)赋给标 号。,注意:标号为字符名称,其后无冒号。在一个程序中对于某一个标号只能赋一次值,一旦赋值在本程序的任意位置就可以引用该标号。,例如:,ORG 3000H,STAEQU 80H,TABEQU 10,MULEQU 4000H,MOV A,,,STA,MOV B,,,TAB,LCALL MUL,定义了三个标号:,STA=80H,,,TAB=10,,,MUL=4000H,,在程序中直接引用这三个标号来代替,80H,、,10,、,4000H,。,6,位定义伪指令,格式:,标号, BIT,位地址,功能:将位地址赋给标号。,其中,标号为字符名称,其后无冒号。,例如:,A1 BIT P0.1,A2 BIT PSW.3,4.2,汇编语言程序设计,4.2.1,汇编程序设计基础,1,汇编语言程序设计的步骤:,(,1,)分析问题,明确任务,(,2,)确定算法,(,3,)画出程序流程图,(,4,)编写源程序,(,5,)汇编和调试,2,汇编语言程序的格式,ORG,起始地址,;数据存放,;程序代码,SJMP $,;原地踏步,END,3,汇编语言源程序的汇编,汇编语言源程序必须转换成计算机能过识别的目标程序后才能执行,汇编方法通常有,机器汇编,和,手工汇编,两种方法。,4,汇编语言程序设计的技巧,(,1,)尽量采用模块化、结构化的程序设计方法。,(,2,)合理地绘制程序流程图,(,3,)少用无条件转移指令,尽量采用循环结构和子程序结构。,(,4,)充分利用累加器。,(,5,)精心设计主要程序段。,(,6,)对于中断要注意保护和恢复现场。,4.2.2,顺序程序设计,顺序结构程序又称为简单结构程序,是指一种顺序执行的程序,程序从第一条指令到最后一条指令的整个执行过程中既无分支又无循环。,【,例,4.1】,两个,8,位无符号数相加,和仍为,8,位。,分析:假设两个无符号数,X,、,Y,分别存放于内部,RAM,的,60H,、,61H,单元中,求和并存于,62H,单元中。,程序如下:,ORG 2000H,MOVR0,,,#60H,;设,R0,为数据指针,MOVA,,,R0,;将,X,值送入,A,中,INCR0,ADDA,,,R0,;,X+Y,INCR0,MOVR0,,,A,;保存结果,SJMP $,;原地踏步,END,【,例,4.2】,假设两个双字节无符号数,分别存放在,R1R0,和,R3R2,中,高字节在前,低字节在后。编程使两数相加,用,R2R1R0,存放和。,对多字节的加法,存在最高位的进位问题。如果最高位有进位,则和的字节数要比加数或被加数的字节数多一个。,ORG 1000H,CLR C,MOV A,,,R0,;取被加数低字节,A,ADD A,,,R2,;与加数低字节相加,MOV R0,,,A,;存和数低字节,MOV A,,,Rl,;取被加数高字节,A,ADDC A,,,R3,;与加数高字节相加,MOV R1,,,A,;存和数高字节,MOV A,,,#0,ADDC A,,,#0,;加进位位,MOV R2,,,A,;存和数进位位,SJMP $,;,END,【,例,4.3】16,位二进制数求补,设两个字节原码数存在,R1R0,中,求补后结果存在,R3R2,中。,分析:二进制数求补操作,可采用“原码求反,+1”,的方法来实现,可使用求反指令和加法指令来实现。,程序如下:,ORG 1000H,MOVA,,,R0,;低,8,位送,A,CPLA,;,ADDA,,,#01H,;,MOVR2,,,A,;送结果低位,MOVA,,,R1,;高,8,位送,A,CPLA,;,ADDCA,,,#00H,;加进位,MOVR3,,,A,;送结果高位,SJMP$,END,【,例,4.4】,将内部,RAM,的,60H,单元中的无符号二进制数转换为,3,位,BCD,码,并将结果按由高位到低位的顺序存放到,63H,、,62H,、,61H,单元中。,分析:,8,位无符号二进制数表示的数据范围,0255,,所以用,BCD,码,最多,3,位。可将被转换的数除以,100,,商即百位数;余数再除以,10,得十位数;最后余数即为个位数。,编程如下:,ORG 1000H,HBCD,:,MOV A,,,60H,;取原数据,MOV B,,,#100,;除数,100B,DIV AB,;除,100,MOV 63H,,,A,;百位,BCD,MOV A,,,B,MOV B,,,#10,;除数,10B,DIV AB,;,MOV 62H,,,A,;存十位,BCD,MOV 61H,,,B,;存个位,BCD,SJMP $,END,4.2.3,分支程序设计,图,4-1,分支程序流程图,【,例,4.5】,设内部,RAM 60H,和,61H,单元中存放两个无符号数,试比较它们的大小。将较小的数存放在,60H,单元,较大的数存放在,61H,单元中。,分析:这是一个简单分支程序,可以使两数相减,用,JC,指令进行判断。若,CY=1,,则被减数小于减数。,ORG1000H,START,:,CLR C,;,0CY,MOVA,,,60H,SUBBA,,,61H,;做减法比较两数,JC NEXT,;若,(60H),小,则转移,MOVA,,,60H,XCH A, 61H,;交换两数,MOV 60H,A,NEXT,:,NOP,SJMP $,END,【,例,4.6】,将,R2,中的一位十六进制数转换为,ASCII,码,结果仍存放于,R2,中。,分析:数字,09,的,ASCII,码分别是,30H39H,,英文大写字母,AF,的,ASCII,码分别是,4146H,。因此,若该十六进制数小于,10,,要转换为,ASCII,码应加,30H,,若该十六进制数大于,10,,则加,37H,。,ORG 1000H,MOV A,,,R2,CJNE A,,,#0AH,,,L1,L1,:,JNC ADD37,ADD30,:,ADD A,,,#30H,MOV R2,,,A,SJMP FINISH,ADD37,:,ADD A,,,#37H,MOV R2,,,A,FINISH,:,SJMP $,END,【,例,4.7】,设变量,x,存入,30H,单元,求得函数,y,存入,31H,单元。按下式要求给,y,赋值:,x+1 (10x),y= 0 (5x10),x-1 (x10,时,采用,CJNE,和,JC,以及,CJNE,和,JNC,指令进行判断。,程序如下:,ORG 1000H,MOV A,,,30H,;取,X,CJNE A,,,#5,,,NEXTl,;与,5,比较,NEXT1,:,JC NEXT2,;,X5,,则转,NEXT2,MOV R0,,,A,;,INC R0,;设,1010,,则转,NEXT4,MOV R0, #0,;,5X10,,,Y=0,SJMP NEXT4,NEXT2,:,MOV R0,,,A,DEC R0,;,X5,,,Y=X-1,NEXT4,:,MOV 31H,R0,;存结果,SJMP $,END,4.2.4,循环程序设计,循环程序一般包括以下,5,部分:,初始化部分:循环程序的准备部分,把初始参数赋给控制变量。,循环处理部分:循环程序反复执行的部分,这是循环程序的实体。,修改部分:修改控制变量的值,为进入下一轮循环做准备。,控制部分:根据循环结束条件,判断循环是否结束。,结束部分:分析、处理及存放程序执行结果。,循环程序的结构一般由两种形式:,1,单循环程序设计,【,例,4.8】,循环次数已知的单重循环。,在内部,RAM,的,20H,2FH,连续,16,个单元中存放单字节无符号数。求这,16,个无符号数之和,并存入内部,RAM 61H,和,60H,中。,分析:这是重复相加问题,且事先知道循环次数,故采用计数器控制的循环程序设计方法。设用,R0,作加数地址指针,,R7,作循环次数计数器,,R3,作和数的高字节寄存器。,ORG 0200H,START,:,MOV R7,,,#15,;循环次数,MOV R3,,,#0,;装和的高字节,MOV A, 20H,;,MOV R0,,,#21H,;加数的地址,LOOP,:,ADD A,,,R0,;累加和在,A,中,JNC NEXT,;没进位则跳,NEXT,INC R3,;有进位,则高位加,1,NEXT,:,INC R0,;加数地址加,1,DJNZ R7,,,LOOP,MOV 61H,,,R3,;将和的高位传,51H,中,MOV 60H,,,A,;将和的低位传,50H,中,SJMP $,END,【,例,4.9】,循环次数未知的单重循环。,设有一字符串存放在内部,RAM 21H,开始的单元中,并以“,$”,作为结束标志,请计算该字符串的长度,并将其存入,20H,单元。,分析:该字符串长度事先未知,但知道结束条件,故采用条件控制的循环程序设计方法。,程序如下:,ORG 0200H,MOVA, #0,MOVR0, #21H,LOOP:CJNER0, #24H, NEXT,SJMPEXIT,NEXT:INCA,INCR0,SJMPLOOP,EXIT:MOV20H, A,SJMP $,END,2,多重循环程序设计,【,例,4.11】,设单片机使用,12MHz,晶振(机器周期,T,为,1s,),设计延时,1s,的程序。,分析:因为执行每条指令都需要一定的时间,其时间长短由指令周期和晶振频率决定(,t=,机器周期数,12,振荡周期)。单条指令的执行时间只有,1,到几个微秒,若需要较长时间的延时就需要执行多条指令,在单片机应用中一般采用多重循环方式来实现。,程序如下:,ORG200H,DEL1s,:,MOVR3,,,#10,;单周期,,1T,DEL1,:,MOVR4,,,#200,;单周期,,1T,DEL2,:,MOVR5,,,#248,;单周期,,1T,DEL3,:,DJNZR5,,,DEL3,;双周期,,2T,NOP,;单周期,,1T,DJNZR4,,,DEL2,;双周期,,2T,DJNZR3,,,DEL1,;双周期,,2T,RET,;双周期,,2T,END,【,例,4.12】,将片内,RAM 40H4FH,单元的,16,个无符号数按从大到小的顺序排列,排序后仍存放在原数据区中。,分析:这是一个数据排序问题。,数据排序常用的方法有“冒泡法”和“选择法”。下面采用“冒泡法”设计程序。,所谓“冒泡法”是指依次将两个相邻单元的内容进行比较,如果符合由大到小的顺序,数据保持原状,否则相邻单元的数据互换。经过一轮比较以后,这一组数中最小的数已经找出,存放于数据区的最后位置。然后再进行第二轮的比较,循环结束时找出次小数。继续循环,直到原数据区中存放的是从大到小的排序的数组。,例,4.12,程序流程图,ORG1000H,SORT,:,MOV R0,,,#40H,;置数组首地址,MOVR7,,,#10H,;数据块长度,CLRF0,;交换标志清,0,DECR7,;循环次数,SORT1,:,MOV A,,,R0,;取前数,MOV20H,,,A,;把前数暂存在,20H,单元,INCR0,MOV21H,,,R0,;后数暂存于,21H,单元,CJNEA,,,21H,,,SORT2,;相邻两数比较,SORT2,:,JNC SORT3,;若前数大于等于后数,转,SORT3,MOVA,,,R0,;若前数小于后数,两者交换,MOVR0,,,20H,DECR0,MOVR0,A,INCR0,;恢复,R0,原值,SETBF0,;置交换标志位为,1,SORT3,:,DJNZ R7,,,SORT1,;如计数器长度不为,0,,则继续比较,JB F0,,,SORT,;若判断标志位为,1,,则继续循环,否结束循环,SJMP$,END,注意:对于循环结构程序设计,需要注意:,(,1,)在进入循环之前,应合理设置循环初始变量。,(,2,)循环体只能执行有限次,如果无限执行的话,称之为“死循环”,这是应当避免的。,(,3,)不能破坏或修改循环体,要特别注意的是避免从循环体外直接跳转到循环体内。,(,4,)多重循环的嵌套是从外层向内层一层层进入,从内层向外层一层层退出。不要在外层循环中用跳转指令直接转到内层循环休内。,(,5,)循环体内可以直接转到循环体外或外层循环中,实现一个循环由多个条件控制结束的结构。,(,6,)对循环体的编程要仔细推敲,合理安排,对其进行优化时,应主要放在缩短执行时间上,其次是程序的长度。,4.2.5,子程序调用设计,1,子程序的结构,在,MCS-51,指令系统中,提供了两条子程序指令,ACALL,、,LCALL,以及一条返回主程序的指令,RET,。,一个子程序在运行的过程中还可以调用其它的子程序,这称为子程序嵌套。,2,子程序设计时的注意事项,(,1,)每个子程序都应有唯一的入口,子程序的第一条指令的地址称为入口地址。该指令前必须有标号,最好以子程序的任务定名,以便主程序正确的调用它。,(,2,)调用子程序的指令在主程序中,子程序以,RET,指令结束,这样才能返回主程序。,(,3,)子程序调用和返回指令能自动保护和恢复断点,但对于需要保护的寄存器和内存单元的内容,必须在子程序开始和末尾安排保护和恢复他们的指令。,(,4,)为使所编的子程序可以放在,64KB,程序存储器的任何地方并能被主程序调用,子程序内部必须使用相对转移指令而不使用其它转移指令,以便汇编时生成浮动代码。,3,参数传递方法,在调用子程序时会遇到主程序和子程序之间参数如何传递的问题。,入口参数:,主程序调用子程序时,传入子程序的参数称为入口参数。,出口参数:,子程序运算出的结果称为出口参数。,常用的参数传递方法有:工作寄存器或累加器、指针寄存器、堆栈和程序段等。,【,例,4.13】,用子程序调用的方法实现,C=a,2,+ b,2,,假设,a,、,b,均小于,10,。,a,存于,31H,单元,,b,存于,32H,单元 ,结果,C,存于,30H,单元。,分析:因为,a,、,b,均小于,10,,则它们的平方和为单字节数,因为本题两次用到平方的运算,所以把求平方运算设计成子程序。,子程序和主程序如下:,ORG1000H,MAIN,:,MOV SP,,,#60H,;设置堆栈指针,MOVA,,,31H,;取,a,的值,LCALL SQR,;调子程序,求,a2,MOV30H,,,A,;,a2,存储于,30H,单元,MOVA,,,32H,;取,b,的值,LCALL SQR,;调子程序,求,b2,ADDA,,,30H,;求,a2 + b2,MOV30H,,,A,;结果存于,30H,单元,SJMP$,ORG2000H,SQR,:,MOVB,,,A,;求平方子程序,MULAB,RET,END,4.3,综合程序设计,【,例,4.15】,求,Y=n!(n=0,,,1,,,2,,,,,9),的值。,分析:如果按照求阶乘的运算,程序设计十分繁琐,需连续做,n-1,次乘法。但如果将函数值列成表格,每个函数值占用,3,个字节的存储单元,则每个,n,值所对应的,y,值在表格中的地址可按下面公式计算出来。,Y,地址,=,函数表首址,+ n3,因而可采用计算查表法。对每一,n,值,首先按上述公式计算出对应于,y,的地址,然后从该单元中取出,y,值。,设:,n,值存放在,TEM,单元,表的首址为,TABL,,用,MOVC A,,,A+DPTR,指令查表取出,Y,值存入,R2R1R0,中。,程序如下:,ORG2000H,TEM EQU 30H,CALN,:,MOV A,,,TEM,;取,n,值,MOV B,,,#3,MUL AB,;,n 3A,MOV B,,,A,;暂存,B,中,MOV DPTR,,,#TAB,;指向表首址,TABL,MOVC A,,,A+DPTR,;查表取低字节,MOV R0,,,A,;存入,R0,INC DPTR,;修正地址指针,MOV A,,,B,;恢复,n 3,MOVC A,,,A+DPTR,;查表取中间字节,MOV R1,,,A,;存入,R1,INC DPTR,;修正地址指针,MOV A,,,B,;恢复,n 3,MOVC A,,,A,十,DPTR,;查表取高字节,MOV R2,,,A,;存入,R2,RET,END,TABL,:,DB 00,,,00,,,00,;,0,的阶乘,DB 01,,,00,,,00,;,1,的阶乘,本 章 小 结,1,汇编语言的格式:,标号:操作码 操作数 ;注释,2,伪指令又称伪操作,它是在汇编程序对源程序汇编时,由汇编程序完成入数据定义、分配存储器、指示程序结束等操作。,3,汇编语言程序设计一般要经过分析问题、确定算法、画出程序流程图、编写源程序、汇编和调试等,5,个步骤。,4,汇编语言程序基本结构,顺序程序、分支程序、循环程序、子程序。一个真正的应用程序往往不是单纯的一种结构,而是几种结构混合使用。,