资源描述
,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,单击此处编辑母版标题样式,本文档所提供的信息仅供参考之用,不能作为科学依据,请勿模仿。文档如有不当之处,请联系本人或网站删除。,第,4,章 汇编语言程序设计,4.1,概 述,4.2,汇编语言伪指令,4.3,简单程序设计,4.4,循环程序设计,4.5,子程序设计,4.6,查表及散转程序设计,4.7,实用程序举例,4.1.1,程序设计语言简介,1.,机器语言,当指令和地址采用二进制代码表示时,机器能够直接识别,因此称为机器语言。,机器指令代码是,0,和,1,构成的二进制数信息,与机器的硬件操作一一对应。,使用机器语言可以充分发挥计算机硬件的功能。,但是,机器语言难写、难读、难交流,而且机器语言随计算机的型号不同而不同,因此移植困难。然而,无论人们使用什么语言编写程序,最终都必须翻译成机器语言,机器才能执行。,4.1,概 述,2.,汇编语言,汇编语言是采用易于人们记忆的助记符表示的程序设计语言,方便人们书写、阅读和检查。一般情况下,汇编语言与机器语言一一对应。,用汇编语言编写的程序称为汇编语言源程序(源程序)。,把汇编语言源程序翻译成机器语言程序的过程称为汇编,完成汇编过程的程序称为汇编程序,汇编产生的结果是机器语言程序,(,目标程序,).,汇编语言源程序从目标代码的长度和程序运行时间上看与机器语言程序是等效的。不同系列的机器有不同的汇编语言,因此汇编语言源程序在不同的机器之间不能通用。,3.,高级语言,高级语言是对计算机操作步骤进行描述的一整套标记符号、表达格式、结构及其使用的语法规则。,它是一种面向过程的语言,使用一些接近人们书写习惯的英语和数学表达式的语言去编写程序,使用方便,通用性强,不依赖于具体计算机。目前,世界上的高级语言有数百种。,用高级语言编写的源程序,同样需要翻译成用各种机器语言表示的目标程序,计算机才能解释执行,完成翻译过程的程序称为编译程序或解释程序。高级语言程序所对应的目标代码往往比机器语言要长的多,运行时间也更多。,4.1.2,汇编语言源程序的设计步骤 汇编,1.,分析任务,当我们要编写某个功能的应用程序时,首先应该详细分析给定的任务。明确哪些是任务所提供的基本条件,哪些是任务要解决的具体问题,哪些是任务所期望的最终目标。,2.,确定算法,任务明确之后,下一步就是确定解决问题的方法。,将给定的任务转换成计算机处理模式,即通常所说的算法。对于较复杂的任务,需要先用数学方法把问题抽象出来。往往同一个数学表达式可以用多种算法实现,我们应综合考虑寻找出其中的最佳方案,使程序所占内存小,运行时间短。,3.,画程序流程图,画流程图是把所采用的算法转换为汇编语言程序的准备阶段,选择合适的程序结构,把整个任务细化成若干个小的功能,使每个小功能只对应几条语句。,4.,分配资源,在用汇编语言进行程序设计时,我们直接面向的是计算机的最底层资源。在编写代码之前需要对内存区域进行分配,并确定程序和数据的存放地址。,5.,编写代码,在画好流程图并分配了相关资源后,就可以编写程序代码了。,6.,程序修改与调试,当一个汇编语言程序编好后难免有错误或需要进一步优化的地方,必须进行调试、修改。在源程序的汇编过程中用户很容易发现程序中存在的语法错误,但查找和修改程序中的逻辑错误就不那么简单,我们需要借助开发系统所提供的程序单步操作或设置断点等调试手段予以排除。,4.2,汇编语言伪指令,伪指令是用于告诉汇编程序如何进行汇编的指令,它不控制机器的操作也不能被汇编成机器码,只为汇编程序所识别并指导汇编如何进行。,MCS-51,系列单片机的常用伪指令如下,:,1.ORG,起始地址定义伪指,令格式,:ORG16,位地址,功能,:,规定目标程序在程序存储器中所占空间的起始地址。,例如,:ORG1000H,表示后面的数据或程序存放在从,1000H,开始的程序存储单元中。,2.END,汇编程序结束伪指令,格式,:END,功能,:,标志源程序的结束,即通知汇编程序不再继续向下汇编。,3.EQU,宏代换伪指令,格式,:,符号,EQU,字符串,功能,:,在程序中用,EQU,后面的字符串去替换,EQU,前面的符号。,EQU,后面的字符串可以是符号、数据地址、代码地址或位地址。,说明,:EQU,伪指令所定义的符号必须先定义后使用。,所以该语句一般放在程序开始。例如,:,BUFFER EQU 58H ;BUFFER,的值为,58H,MOV A,,,BUFFER ;,表示内部,RAM58H,单元中数据送给累加器,A,4.DATA,数值赋值伪指令,格式,:,符号名称,DATA,表达式,功能,:,将表达式指定的数据地址或代码地址赋予符号名称。,说明,:DATA,伪指令功能与,EQU,伪指令相似,但是,DATA,所定义的符号可以先使用后定义。该语句一般放在程序开始或结尾。,例如,:,BUFFER DATA58H ;BUFFER,的值为,58H,MOV A,,,BUFFER ;,表示内部,RAM58H,单元中数据送给累加器,A,5.DB,字节存储伪指令,格式,:,标号,:,DB8,位二进制数据表,功能,:,从指定的地址单元开始,定义若干个字节存储单元的内容。,【,例,4.1】,ORG 100H,FIRST:DB 01H,,,02H,SECO:DB 011B,,,A,,,12,以上伪指令经汇编后,程序存储器有关单元,如图,4-1,所示。,其中伪指令中的,011B,为二进制数,,A,为字,符,A,的,ASCII,码,41H,,,12,为十进制数。,另外,格式中的标号为可选项。,图,4-1,例,4.1,示意图,6.DW,字存储伪指令,格式,:,标号,:,DW16,位二进制数据表,功能,:,从指定的地址单元开始,定义若干个字存储单元的内容。,【,例,4.2】,ORG1 00H,FIRST:DW 01H,DW 1234H,,,AB,以上伪指令经汇编后,程序存储器有关单元,如图,4-2,所示。,其中,16,位数据的高,8,位存入低地址单元,低,8,位存入高地址单元。,格式中的标号为可选项。,图,4-2,例,4.2,示意图,7.DS,定义空间伪指令,格式,:,标号,:,DS,表达式,功能,:,从指定的地址单元开始,保留由表达式指定的若干字节空间作为备用空间。,例如,:,ORG 1000H,DS 0AH,DB 12H,,,B,伪指令汇编后从,1000H,单元开始,保留,10,个字节,从,100AH,开始连续存放,12H,、,42H,。,8.BIT,位地址符号伪指令,格式,:,字符名称,BIT,位地址,功能,:,用规定的字符名称表示位地址。,例如,:,X0 BIT P1.0,X1 BIT 30H,经汇编后,,P1,口的第,0,位地址赋给,X0,,位地址,30H,赋给,X1,。,在程序中可以分别用,X0,、,X1,代替,P1.0,和位地址,30H,。,4.3,简单程序设计,4.3.1,顺序程序设计,顺序结构的程序,是指程序按指令的排列顺序依次执行直至程序结束。这种结构是程序结构中最简单的一种,用程序流程图表示的顺序结构程序,是一个处理框紧接一个处理框。,【,例,4.3】,【,例,4.4】【,例,4.5】,(,见教材,P80-81,页,),4.3.2,分支程序设计,分支程序是按照给定的条件进行判断,根据不同的情况使程序发生转移,选择不同的程序入口。,通常用条件转移指令形成简单分支结构。例如,判断结果是否为,0,(,JZ,、,JNZ,),、,是否有进位或借位,(JC,、,JNC),、,指定位是否为,1(JB,、,JNB),、,比较指令,CJNE,等都可作为分支依据。,【,例,4.6】,【,例,4.7】,(,见教材,P82-83,页,),4.4,循环程序设计,4.4.1,循环结构,顺序程序中每条指令只执行一次,分支程序则依据条件不同会跳过一些指令,执行另一部分指令。,这两种程序的特点是每条指令最多只执行一次。,在处理实际问题时,常常要求某些程序段重复执行,此时应采用循环结构实现。,典型的循环结构如图,4-4,所示。,一般包含程序初始化、循环处理、循环控制和循环结束四部分。,(,1,)初始化部分,为实现程序循环做准备,如建立循环计数器、设地址指针以及为变量赋初值等。,(,2,)循环处理部分,该部分是循环程序的主体,在这里对数据进行实际的处理,是重复执行部分,所以这段程序的设计非常关键,应充分考虑程序的效率。,(,3,)循环控制部分,为下一次数据处理而修改计数器和地址指针,并判断循环是否结束。,(,4,)结束部分,分析、处理或存放结果。,4.4.2,单重循环程序设计,1.,循环次数已知的循环程序,【,例,4.8】,从,60H,单元开始的连续单元中有,一个无符号数的数据块,其长度在,5FH,中,编程,求数据块的最大值,存入,5EH,单元。,分析,:,假设累加器,A,中有最大值,00H,,然后逐个取出,数据块中的数据与之进行比较。,如果当前数据大于累加器,A,的数据,则把数,据块中的数据送给累加器,A;,图,4-4,例,4.8,程序流程图,否则累加器,A,的内容不变,即保证累加器,A,中存放的是每次比较出的较大数。,如此循环,直至比较完最后一个数。累加器,A,中存放的便是所有数据中的最大值。程序流程如图,4-4,所示。,源程序,:,ORG 1000H,CLR A,MOV B,,,5FH,MOV R0,,,#60H,LOOP:CLR C,SUBB A,,,R0,JC L1,ADD A,,,R0,SJMP L2,L1:XCH A,,,R0,L2:INC R0,DJNZ B,,,LOOP,MOV 5EH,,,A,SJMP,END,【,例,4.9】,编程确定一个数据块中负元素的个数。假设数据块的长度存放在内部,RAM51H,单元,数据块从内部,RAM52H,单元开始存放,要求将负元素的个数存放在,50H,单元中。,分析,:,判断有符号数值的正负可以通过判断该数值的最高位来完成。,如果最高位为,1,,则该数为负,;,否则该数为正。,另外,统计负数个数,需要在程序初始化时将存,放负元素个数的单元清零,以便在循环中进行个,数累计,流程图如图,4-5,所示。,源程序,:,ORG 1000H,MOV R0,,,#52H,MOV R2,,,#00H,MOV B,,,51H,LOOP:MOV A,,,R0,JNB Acc.7,,,NEXT,INC R2,图,4-5,例,4.9,程序流程图,NEXT:INC R0,DJNZ B,,,LOOP,MOV 50H,,,R2,SJMP,END,2.,循环次数未知的循环程序,【,例,4.10】,从,60H,单元开始的连续单元中有一个无符号数,0FH,,编程求该数据的地址并存入,5FH,单元。,分析,:,先取出,60H,单元的数据与立即数,0FH,比较。,如果不相等,再取下一个单元数据进行比较,;,如此循环,直到两个数据相等时,记录数据地址,退出循环。,判断两数是否相等有多种方法,在此我们选择两数异或指令。如果两数相等,则异或结果为,0;,否则结果为,1,。程序流程图如图,4-6,所示。,源程序,:,ORG 1000H,MOV R0,,,#5FH,LOOP:INC R0,MOV A,,,R0,XRL A,,,#0FH,JZ NEXT,SJMP LOOP,NEXT:MOV 5FH,,,R0,SJMP,END,图,4-6,例,4.10,程序流程图,【,例,4.11】,用,80C51,单片机的,P1,口作输出,经驱动电路接,8,只发光二极管,如图,4-7,所示。,当输出位是“,1”,时,发光二极管被点亮,;,输出位是“,0”,时二极管熄灭。,编制单灯循环亮程序,即按,P1.0P1.1P1.2P1.6P1.7P1.0P1.1,顺序,每次只有一只二极管亮。,分析,:,要使每个灯轮流被点亮,必须在,P1,口的各,位按指定顺序轮流输出“,1”,其余位输出,0,,可,以采用循环移位指令完成。,8,个灯一轮的渐次点亮由循环体完成,然,后利用无条件转移指令反复执行循环体。,图,4-7,单灯循环电路图,程序流程图如图,4-8,所示。,源程序,:,ORG 1000H,MOV A,,,#01H,LOOP:MOV P1,,,A,RL A ;,累加器,A,左循环一位,LCALL DELAY ;,调用延时子程序,SJMP LOOP,DELAY:MOV R2,,,#0FAH ;,延时子程序,L1:MOV R3,,,#0FAH,L2:DJNZ R3,,,L2,DJNZ R2,,,L1,END,图,4-8,例,4.11,程序流程图,4.4.3,多重循环程序设计,多重循环又称为循环嵌套,是指一个循环程序的循环体中包含另一个循环程序。理论上对循环嵌套的层数没有明确的规定,但由于受硬件资源的限制,实际可嵌套层数不能太多。,需要注意的是循环嵌套只允许一个循环程序完全包含另一个循环程序,不允许两个循环程序之间相互交叉嵌套。,【,例,4.12】,编程将内部,RAM70H,79H,中,10,个无符号数按由大到小的顺序排序。排序后仍存放在,70H,79H,中。,分析,:,排序方法有多种,本例题采用“冒泡”法完成。,具体思路是,:,从低地址到高地址将相邻两个单元进行比较。,若低地址的内容大于相邻高地址单元的内容,则保持原状,;,若低地址的内容小于相邻高地址单元的内容,则两单元内容互换。,图,4-9,给出设标志位冒泡法排序流程图。,设标志位冒泡法排序源程序,:,ORG 1000H,LOOP:MOV R0,,,#70H,MOV B,,,#09H,CLR 10H,LOOP1:MOV A,,,R0,MOV 20H,,,A,INC R0,MOV 21H,,,R0,CJNE A,,,21H,,,LOOP2,图,4-9,冒泡法排序流程图,LOOP2:JNC LOOP3,MOV A,,,R0,MOV R0,,,20H,DEC R0,MOV R0,,,A,INC R0,SETB 10H,LOOP3:DJNZ B,,,LOOP1,JB 10H,,,LOOP,SJMP,END,【,例,4.13】,编写软件延时,80ms,程序。,分析,:,设单片机的时钟频率为,6MHz,,则其机器周期为,2s,。,由于,DJNZ,指令需要,2,个机器周期,因此它的指令周期即指令执行时间为,4s,。,MOV,和,NOP,指令分别为,2,个机器周期和,1,个机器周期。,为了延时,80ms,,我们可以通过控制循环次数的方法来解决。,执行两条,NOP,指令和,1,条,DJNZ,指令的时间是,8s,,循环,250,次用时,2000s,,即程序中第,2,条到第,5,条指令完成的功能。,若想达到延时,80ms,的目的,只要再使用一个循环,40,次的,DJNZ,指令即可。精确计算程序运行时间的公式为,:,2+,2+,(,1+1+2,)*,250+2,*,40,*,2s=80324s,如果单片机的时钟频率为,12MHz,,则其机器周期为,1s,。为了延时,80ms,,我们只要把循环次数由,40,修改为,80,就可以了。,延时程序,:,MOV 20H,,,#40 ;2,机器周期指令,BBB1:MOV 21H,,,#250,BBB2:NOP ;1,机器周期指令,NOP,DJNZ 21H,,,BBB2 ;2,机器周期指令,DJNZ 20H,,,BBB1,SJMP,4.5,子程序设计,在程序设计过程中,经常会遇到在不同的程序中或同一个程序的不同地方执行同一个操作的情况,例如软件延时、代码转换等。,为了缩短程序设计周期及程序长度,可以将这些程序段从源程序中分离出来单独组成一个程序模块,我们称之为子程序。,在需要使用这些模块的地方可以“调用子程序”。,那些调用子程序的程序被称为主程序。主程序对子程序的调用是通过,ACALL,或,LCALL,指令完成的。一个主程序可以多次调用同一个子程序,也可以调用多个子程序。子程序也可调用其他子程序,(,称为子程序嵌套,).,4.5.1,关于子程序的几点说明,1.,每个子程序的起始指令前必须定义一个标号,作为该子程序的名称,以便主程序正确的调用它。子程序通常以,RET,指令结束,以便正确的返回主程序。,2.,子程序应具有通用性。一般,子程序的操作对象通常采用寄存器或寄存器间接寻址等寻址方式,尽量避免采用立即寻址。,3.,子程序应保证放在存储器的任何空间都能正确运行,即具有浮动性。例如,子程序中应使用相对转移指令,避免使用绝对转移或长转移。,4.,进入子程序时需要把在主程序中使用并在子程序中也要使用的寄存器进行保存,并在返回主程序之前恢复原来状态。,5.,子程序的调用和返回指令,以及保护现场等操作均需用到堆栈,因此在程序初始化时应设置堆栈指针,SP,,开辟堆栈保护区。,6.,设计子程序时应首先确定子程序名称,确定子程序的入口参数和出口参数,确定子程序需要使用的寄存器和存储单元,确定子程序的算法,再编写源程序。,4.5.2,子程序的应用举例,(,略,),4.5.3,子程序的嵌套调用,子程序的嵌套调用是指在一个子程序中又调用另一个子程序。,对于,MCS-51,单片机,子程序嵌套次数一般不受限制。,子程序的嵌套调用过程如图,4-10,所示。,当主程序执行到,LCALL SB01,指令时,它会将断点地址,M02,压入堆栈,并转去执行,SB01,子程序。,在,SB01,子程序中执行到,LCALL SB02,指令时,它会将断点地址,SB12,压入堆栈,并转去执行,SB02,子程序。,SB02,子程序执行到最后的,RET,指令时,它会从堆栈中取出断点地址,SB12,送给指令计数器,PC,,程序返回,SB01,子程序。,SB01,子程序执行到最后的,RET,指令时,它会从堆栈中取出断点地址,M02,送给指令计数器,PC,,程序返回主程序,继续执行。,图,4-10,子程序嵌套示意图,4.6.1,查表程序设计,查表程序是指适当的组织一些表格,跟控制程序一起事先输入到单片机的程序存储器中。使用查表指令迅速获取结果数据。该类程序主要用于代码转换、算术运算等。,【,例,4.16】【,例,4.17】,(,见教材,P91,页,),说明,:,在例,4.16,和例,4.17,中分别使用了指令,MOVC A,,,A+PC,和指令,MOVC A,,,A+DPTR,实现查表。,第一条指令是以,PC,作为基址寄存器,,A,中存放偏移量(偏移量为当前的,PC,值到表格首地址之间的距离),两者的和为结果所在程序存储单元的地址。,4.6,查表及散转程序设计,该指令执行后,PC,仍指向下一条指令。,用,PC,的内容作为基地址来查表,通常分为三步,:,(,1,)将所查表格的项数(即在表格中的位置)送入累加器,A,中,;,(,2,)计算偏移量,data,,并在,MOVC A,,,A+PC,指令前加上指令,ADD A,,,#data,。计算公式如下,:,偏移量,=,表格首地址,-,(,MOVC,指令所在的地址,+1,),(,3,)执行查表指令,MOVC A,,,A+PC,,结果存入累加器,A,。,第二条指令是以,DPTR,作为基址寄存器,,A,与,DPTR,两者的和为结果所在程序存储单元的地址。,用,DPTR,内容作为基地址来查表,通常也分为三步,:,(,1,)将表格的项数(即在表格中的位置)送入累加器,A,中,;,(,2,)将表格的首地址送入,DPTR,中,;,(,3,)执行查表指令,MOVC A,,,A+DPTR,,结果存入累加器,A,。,4.6.2,散转程序设计,在设计单片机应用程序时,经常遇到根据不同的输入或运算结果决定程序流向的问题。这就是散转程序,实际就是一种多分支程序。,散转程序也需要一个表,但表中所列的不是普通数据,而是某些功能程序的入口地址、偏移量或转向这些功能程序的转移指令。,程序的散转功能,主要依靠间接转移指令,JMPA+DPTR,完成。,1.,利用转移指令表进行散转,假设有一个标志单元,它的可能取值为,0,,,1,,,2,的自然数,每个值对应一个处理程序。我们可以利用转移指令表,使程序根据标志单元的值转向各自的处理程序。,选择与处理程序相同数目的无条件转移指令,由这些指令组成一张指令表,且第,1,条转移指令转向“,0”,标志所对应的处理程序,第,2,条转移指令转向“,1”,标志所对应的处理程序,依此类推。,然后把指令表的起始地址和标志单元的值分别送数据指针,DPTR,和累加器,A,中,最后使用散转指令完成。,【,例,4.18】,(,见教材,P93,页,),2.,利用转向地址表进行散转,当转向范围比较大时,可直接使用转向地址表法,即把每个处理程序的入口地址直接放到地址表内。,用查表指令从表中查找与变量值对应的处理程序的入口地址,再通过间接转移指令,使程序转向该地址指向的功能程序。,【,例,4.19】,(,见教材,P93,页,),3.,利用“,RET”,指令进行散转,此方法与转向地址表法基本相同。,两者的惟一区别是,:,转向地址表法,将表中取出功能程序的入口地址直接送给数据指针,;,而返回指令法,则把入口地址压入堆栈进行保存,并随后便执行一条,RET,指令,使程序转向功能程序。,【,例,4.20】,(,见教材,P94,页,),4.7,实用程序举例,设计单片机的应用程序,不仅要掌握几种基本结构程序的设计步骤、设计方法,还要熟悉一些常用子程序的基本功能和使用方法。,4.7.1,代码转换类子程序,(,略,),4.7.2,运算类子程序,(,略,),4.7.3,数据比较类子程序,(,略,),4.7.4,数据块处理类子程序,(,略,),
展开阅读全文