《第5章汇编语言程序设计.ppt》由会员分享,可在线阅读,更多相关《第5章汇编语言程序设计.ppt(53页珍藏版)》请在课桌文档上搜索。
1、第5章 汇编语言程序设计,主要内容,顺序、分支、循环结构程序设计子程序设计转移指令、循环指令和子程序伪指令,教学要求,掌握:(1)分支程序的概念、结构和设计(2)循环程序的概念、结构和设计(3)子程序的概念、结构和设计了解:转移表法和地址表法多分支程序的设计原理;多重循环程序的结构形式,内层循环与外层循环遵守的层次结构规则,参数修改对各层的相互影响;汇编递归程序方法。,编制一个汇编语言程序的步骤,(1)分析题意,确定算法。(2)根据算法,确定程序流程或画出程序框图。(3)根据流程或框图编写程序。(4)上机调试程序。,5.1 顺序程序设计,顺序结构是最简单的程序结构,程序的执行顺序就是指令的编写
2、顺序,所以,安排指令的先后次序就显得至关重要。,【例】设置光标到屏幕左上角,SET_CUR PROCMOV AH,2;设置光标位置功能MOV BH,0;页号MOV DX,0;行DH,列DLINT 10H;BIOS输出字符中断 RETSET_CUR ENDP,这个程序段是顺序执行的,一条指令执行后顺序执行紧接其后的另一条指令。,【例】设在X单元中存放一个07之间的整数,用查表法求出其平方值,并将结果存入Y单元。,分析:根据题意,首先将07所对应的平方值存入连续的8个单元中,构成一张平方值表,其首地址为SQTAB。由表的存放规律可知:表首址SQTAB与X单元中的数i之和,正是i2所在单元的地址。,
3、DATA SEGMENT;数据段定义SQTAB DB 0,1,4,9,16,25,36,49;平方值表X DB 5Y DB?DATA ENDSSTACK SEGMENT PARA STACK STACKTAPN DB 100 DUP(?)TOP EQU LENGTH TAPNSTACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACK,SQRTSUB PROC FARPUSH DSSUB AX,AXPUSH AXMOV AX,DATAMOV DS,AXMOV AX,STACKMOV SS,AXMOV AL,X;取数iMOV AH,0MOV BX,O
4、FFSET SQTAB;BX表首址ADD BX,AXMOV AL,BX;取i2并保存MOV Y,ALRETSQRTSUB ENDPCODE ENDSEND SQRTSUB,5.2 分支程序设计,分支结构是一种非常重要的程序结构,也是实现程序功能选择所必要的程序结构。由于汇编语言需要用转移指令来实现分支结构,而转移指令肯定会破坏程序的结构,所以,编写清晰的分支结构是掌握该结构的重点。计算机可根据不同条件进行逻辑判断,从而选择不同的程序流向。程序的流向是由CS和IP值决定的,当程序的转移仅在同一段内进行时,只需修改偏移地址IP的值;如果程序的转移是在不同段之间进行的,则段基址CS和偏移地址IP的值
5、均需要修改。,5.2.1 转移指令,转移指令是汇编程序员经常要用到的一组指令。在高级语言中,时常有“尽量不要使用转移指令语句”的劝告。但是,在汇编语言程序中,不但要使用转移指令,而且还要灵活运用,因为指令系统中有大量的转移指令。转移指令分为无条件转移指令和有条件转移指令。,无条件转移指令JMP,无条件转移指令JMP指令是从程序当前执行的地方无条件地转移到另一个地方执行。,无条件转移指令JMP,JMP指令转移可以是短(short)转移(偏移量在-128B,127B之内)、近(near)转移(偏移量在-32KB,32KB之内)、远(far)转移(在不同的代码段之间转移)。短转移和近转移都是段内转移
6、,JMP指令只将目标指令位置处的偏移量赋值给指令指针寄存器IP,从而实现转移功能远转移属于段间转移,JMP指令不仅会把目标指令位置处的偏移量赋值给指令指针寄存器,同时还会把目标指令所处的代码段的段地址赋值给当前代码段寄存器CS。,有条件转移指令,有条件转移指令是一组及其重要的转移指令,它根据标志寄存器中的一个(或多个)标志位来决定是否需要转移,这就为实现多功能程序提供了必要的手段,有条件转移指令的格式和类型,5.2.2 分支程序的结构,分支程序结构有两种形式:双分支结构和多分支结构。,分支程序设计要点,(1)首先根据处理的问题用比较、测试、算术运算、逻辑运算等方式,使标志寄存器产生相应的标志位
7、。例如,比较两个单元地址的高低、两个数的大小,测试某个数据是正还是负,测试数据的某位是“0”还是“1”等,将处理的结果反映在标志寄存器的CF、ZF、SF、DF和OF位上。(2)根据转移条件选择适当的转移指令。通常一条条件转移指令只能产生两路分支,因此要产生n路分支需n-1条条件转移指令。(3)各分支之间不能产生干扰,如果产生干扰,可用无条件转移语句进行隔离。,【例】设有单字节无符号数X、Y、Z,若X+Y255,求X+Z;否则求X-Z,运算结果放在F1中(X、Y、Z、F1均为字节变量名)。,分析:这是一个双分支结构。因为X、Y均为无符号数,当X+Y255时会产生进位即CF=1,所以可以用进位标志
8、来判断,MOV AL,XMOV BL,ALADD AL,YJNC LET;若无进位 则转LETADD BL,Z DONE:MOV F1,BLHLT LET:SUB BL,Z JMP DONE,【例】已知符号函数,假设任意给定x值,存放在内存RS1单元中,求出函数y的值,存放在内存RS2单元中。,DATA SEGMENTRS1 DB X;存放自变量XRS2 DB?;函数Y值的存储单元DATA ENDSCODE SEGMENTASSUME CS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX MOV AL,RS1;ALX CMP AL,0;将X与0比较 JGE BI
9、G;若X0BIG MOV RS2,0FFH;若X0,(RS2)-1补0FFH JMP DONEBIG:JE EQUL;若X0EQUL MOV RS2,1;若X0,(RS2)1 JMP DONEEQUL:MOV RS2,0;若X0,(RS2)0DONE:MOV AH,4CH INT 21HCODE ENDSEND START,5.3 循环程序设计,在实际工作中,有时要求对某一问题进行多次重复处理,而仅仅只是初始条件不同,这种计算过程称为具有循环特征的,而循环程序设计是解决这类问题的一种行之有效的方法。循环程序是采用重复执行某一段程序来实现要求完成计算的编程方法。,5.3.1 循环指令,循环语句当
10、然可以用条件转移指令来实现,除此之外,在80 x86系统中还有专门的循环控制指令来简化循环程序的设计。循环控制指令包括重复控制指令和串操作指令。,重复循环控制指令,注意:在执行此类重复控制指令前必须把重复次数送入寄存器CX中。,串操作指令,串操作指令能对存储区中一块(串)字节或字进行操作,其块的长度可达64KB。这些指令分别是:串复制指令MOVS、串取出指令LODS、串存储指令STOS、串比较指令CMPS和串搜索(扫描)指令SCAS。,循环程序的结构,循环程序一般包括以下5个部分:(1)初始化部分:为循环做准备工作,如设置地址指针、计数器及其他变量的初值等。(2)循环工作部分:它是循环程序的主
11、体,用来完成循环的基本操作。(3)修改部分:为循环参数做必要的修改,如修改操作数地址、计数器,为下一次执行循环体做好准备。(4)控制部分:根据循环条件来判断、控制循环的继续和终止。(5)结束部分:主要是对循环的结果进行必要的处理,如将结果送入某一寄存器或内存区域。,循环程序的结构,常见的循环程序结构有两种形式:“先处理后判断”和“先判断后处理”,【例】计算。假设这10个已知数为字类型,已连续存放在内存中以AA为首址的存储区域中,其相加的和仍为字数据,存放在BB字单元。,分析:求a1+a2+a10的和,要用10条加法指令来完成,这样程序太长,书写麻烦。由于数据是有规律存放的,并且每加一项所用的指
12、令都一样,只是数据的地址不同,所以可用间接寻址的方法,将数据地址放在寄存器中,用寄存器加1指令修改地址来取得每个待加的数据,将相加的程序作为一个公共执行的程序段,重复执行10次来实现本题的累加过程,DATA SEGMENT;定义数据AA DW 100H,200H,300H,400H,500H,600H,700H,800H,900H,1000HBB DB 4 DUP(?)MES1 DB AA:100H,200H,300H,400H,500H,600H,700H,800H,900H,1000H,0DH,0AH,$MES2 DB BB:,0DH,0AH,$DATA ENDS,CODE SEGMENT
13、ASSUME CS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX MOV DX,OFFSET MES1 MOV AH,09H INT 21H MOV DX,OFFSET MES2 MOV AH,09H INT 21H MOV AX,0;累加器AX清0 MOV CX,10;设置计数器初始值 MOV BX,OFFSET AA;BX数据首地址 MOV DI,OFFSET BB;DI存放结果地址LOP:ADD AX,BX;累加一个数据 INC BX;修改地址指针指向下一个数据 INC BX LOOP LOP;CX-10,继续循环累加MOV BX,AX LEA DI,B
14、B CALL DISP MOV AH,4CH INT 21H,DISP PROC NEAR;输出 MOV CH,4CONV:MOV CL,4 ROL BX,CL MOV AL,BL AND AL,0FH CMP AL,09H JLE ASCI ADD AL,37H JMP DONEASCI:ADD AL,30HDONE:MOV DI,AL INC DI DEC CH JNZ CONV MOV CX,4 LEA DI,BBDIS:MOV DL,DI MOV AH,02H INT 21H INC DI LOOP DIS RETDISP ENDP HLTCODE ENDS END START,【例】
15、设有两个字数组XX和YY,各包含10个元素x0 x9及y0y9,试编制程序计算:z0 x0y0 z5x5y5z1x1y1 z6x6y6z2x2y2 z7x7y7z3x3y3 z8x8y8z4x4y4 z9x9y9并将运算结果存入ZZ数组。,分析:这是个循环次数已知的过程,每循环一次,由XX和YY数组各取一个数据进行相应的运算,并将运算结果存入ZZ数组。由于每次循环所做的运算不同,有加法也有减法,就必须建立一个标志位,若为“0”做加法,为“1”则做减法,这样一来,只要判断标志位就可确定本次循环该做什么操作了。另外,题目要求进行10次运算,应该建立10个标志位,并存放在一个存储单元中,称为逻辑尺。
16、根据z0z9的运算类型,设定逻辑尺如下:0000000011011100 其中,最末位的状态对应z0的操作,应做加法,其余位依次类推,最高的6位无意义。,5.4 子程序设计,为了程序共享或模块化设计的需要,把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序。子程序可以实现源程序的模块化,简化源程序结构,提高编程效率。,5.4.1 子程序概念,子程序又称过程,相当于高级语言中的过程和函数。在一个程序的不同部分,往往要用到“类似”的程序段,即这些程序段的功能和结构形式都相同,只是某些变量赋值不同。此时就可以把这些程序段写成子程序形式,以便需要时调用它。,子程序伪指
17、令,子程序的相关伪指令包括子程序的定义指令、调用指令和返回指令等,子程序定义指令,子程序就是过程。过程定义伪指令的格式如下:PROC 类型属性 RET ENDP,过程和主程序在同一代码段时,过程定义和调用格式如下:CODE SEGMENT SUBT PROC NEAR RET SUBT ENDP CALL SUBT CODE ENDS,当过程和主程序不在同一代码段时,过程定义和调用格式如下:CODE1 SEGMENT SUBT PROC FAR RETSUBT ENDP CALL SUBTCODE1 ENDSCODE2 SEGMENT CALL SUBT CODE2 ENDS,子程序调用指令C
18、ALL,CALL指令用在主程序中,实现子程序的调用。子程序和主程序可以在同一个代码段内,也可以在不同段内。类似无条件转移指令JMP,子程序调用指令CALL也可以分成段内调用(近调用)和段间调用(远调用)CALL目标地址也可以采用直接寻址或间接寻址方式。但是子程序执行结束后是要返回的,所以,CALL指令不仅要同JMP指令一样改变CS:IP以实现转移,而且还要保留下一条要执行指令的地址,以便返回时重新获取它。保护CS:IP值的方法是压入堆栈,获取CS:IP值的方法就是弹出堆栈。,CALL 指令的4种格式,(1)CALL LABEL。段内调用,直接寻址:SPSP-2,SS:SPIP,IPIP+16位
19、位移量。(2)CALL Reg16/Mem16。段内调用,间接寻址:SPSP-2,SS:SPIP,IPReg16/Mem16。(3)CALL FAR PTR LABEL。段间调用,直接寻址:SPSP-2,SS:SPCS,SPSP-2,SS:SPIP,IPLABEL偏移地址,CSLABEL段地址。(4)CALL FAR PTR MEM。段间调用,间接寻址:SPSP-2,SS:SPCS,SPSP-2,SS:SPIP,IPMEM,CSMEM+2。,子程序返回指令RET,子程序执行完后,应返回主程序中继续执行,这一功能由RET指令完成。要回到主程序,只需获得离开主程序时,由CALL指令保存于堆栈的指令
20、地址即可。根据子程序与主程序是否同处于一个段内,返回指令分为段内返回和段间返回。,RET指令的4种格式,(1)RET。无参数段内返回:IPSS:SP,SPSP+2。(2)RET Imm16。有参数段内返回:IPSS:SP,SPSP+2,SPSP+Imm16。(3)RET。无参数段间返回:IPSS:SP,SPSP+2,CSSS:SP,SPSP+2。(4)RET Imm16。有参数段间返回:IPSS:SP,SPSP+2,CSSS:SP,SPSP+2,SPSP+Imm16。,子程序的调用,对于一个子程序,应该注意它的入口参数和出口参数。入口参数是由主程序传给子程序的参数,而出口参数是子程序运算完传给
21、主程序的结果。另外,子程序所使用的寄存器和存储单元往往需要保护,以免影响返回后主程序的运行。,保护寄存器,由于调用程序(即主程序)和子程序经常是分别编制的,所以它们所使用的寄存器往往会发生冲突。如果主程序在调用子程序之前的某个寄存器内容在从子程序返回后还有用,而子程序又恰好使用了同一个寄存器,这就破坏了该寄存器的原有内容,因而会造成程序运行错误,这是不允许的。为避免这种错误的发生,在一进入子程序后,就应该把子程序所需要使用的寄存器内容保存在堆栈中,而在退出子程序前把寄存器内容恢复原状。,寄存器保护实例SUBT PROC NEARPUSH AXPUSH BXPUSH CXPUSH DX POP
22、DXPOP CXPOP BXPOP AXRETSUBT ENDP,一般说来,子程序中用到的寄存器是应该保存的。但是,如果使用寄存器在主程序和子程序之间传送参数的话,则这种寄存器就不一定需要保存,特别是用来向主程序回送结果的寄存器,就更不应该因保存和恢复寄存器而破坏了应该向主程序传送的信息。,子程序的参数传递,调用程序在调用子程序时,经常需要传送一些参数给子程序;子程序运行完后也经常要回送一些信息给调用程序。这种调用程序和子程序之间的信息传送称为参数传送(或称变量传送或过程通信)。参数传递一般有3种方法,利用寄存器,这是一种最常见方法,把所需传递的参数直接放在主程序的寄存器中传递给子程序。这种方
23、法的特点是编程方便,速度快,节省存储单元,但只适合参数较少的情况,因为寄存器是有限的。,利用存储单元,这种参数传递方法是把所需传递的参数直接放在子程序调用指令代码之后。这种方法的特点是每个子程序都有独立的工作单元,工作时不易引起紊乱,但它占用了存储空间。这种方法不适合递归子程序。,利用堆栈,这种方法将参数压入堆栈,在子程序运行时从堆栈中取参数。由于堆栈操作不占用寄存器,并且堆栈单元使用后可自动释放,反复使用,便于实现数据隔离和模块化设计。使用这种方法时,当子程序返回后,这些参数就不再有用了,应当丢弃。这时可以利用带立即数的返回指令修改指针,使其指向参数入栈以前的值。,子程序的嵌套,子程序作为调
24、用程序又调用其他子程序,称为子程序嵌套。一般来说,只要堆栈空间允许,嵌套的层数不限。但嵌套层数较多时应特别注意寄存器内容的保护和恢复,以免数据发生冲突。,子程序递归调用,在子程序嵌套的情况下,如果一个子程序调用的子程序就是它本身时,称为子程序递归调用。递归子程序对应于数学上对函数的递归定义,它往往能设计出效率较高的程序,可完成相当复杂的计算。递归调用时必须保证不破坏前面调用所用到的参数及产生的结果,否则,就不能求出最后结果。此外,被递归调用的子程序还必须具有递归结束的条件,以便在递归调用一定次数后能够退出,否则,递归调用将无限嵌套下去。,【例】两个6字节数相加,分析:将一个字节相加的程序段设计
25、为子程序。主程序分6次调用该子程序,但每次调用的参数不同。,主程序:,DATASEGMENTADD1DB0FEH,86H,7CH,35H,68H,77HADD2DB45H,0BCH,7DH,6AH,87H,90HSUMDB 6 DUP(0)COUNTDB6DATAENDSSTACKSEGMENTDB100 DUP(?)STACKENDSCODESEGMENTASSUMECS:CODE,DS:DATA,SS:STACKMADD:MOVAX,DATAMOVDS,AXMOVAX,STACKMOVSS,AXMOVSI,OFFSETADD1MOVDI,OFFSETADD2MOVBX,OFFSET SUMMOVCX,COUNT;循环初值为6CLCAGAIN:CALL SUBADD;调用子程序LOOP AGAIN;循环调用6次MOVAX,4C00HINT21H,子程序:,SUBADDPROC;完成一个字节相加PUSHAX;保护AX的值MOVAL,SI;SI是一个源操作数指针ADCAL,DI;DI是另一个源操作数指针MOVBX,AL;BX是结果操作数指针INCSIINCDIINCBXPOPAX;恢复AX的值RETSUBADDENDPCODEENDSEND MADD,