《程序设计方法》PPT课件.ppt
第5章 程序设计方法,教学目的及要求:精确掌握各种转移指令的功能、使用限制、标志位使用、在程序设计中的通常用法等2.能够实际应用所学到的转移指令,在编写完整程序的基础上使用分支、循环等复杂的流程结构教学重点与难点:1.在理解各标志位含义的基础上,着重理解各条件转移指令对标志位的解释、条件判断原理2.结合实例,理解转移指令在分支程序设计中的通常用途3.结合实例,理解条件转移指令的使用限制,理解并逐步建立由框图到程序的设计思路,并由此建立使用条件转移指令所需的良好习惯,开 始,第5章 程序设计方法,5.1 概述5.2 顺序程序设计5.3 分支程序设计5.4 循环程序设计5.5 子程序设计,开 始,5.1 概述,5.1.1 汇编语言程序设计的一般步骤5.1.2 流程图5.1.3 三种基本结构,返回本章首页,5.1.1 汇编语言程序设计的一般步骤,汇编语言程序设计一般有以下几个步骤:1分析问题,确定算法2绘制流程图3根据流程图编写程序4调试程序,返回本节,5.1.2 流程图,1流程图的概念流程图是由特定的几何图形、指向线、文字说明来表示数据处理的步骤,形象描述逻辑控制结构以及数据流程的示意图。流程图具有简洁、明了、直观的特点。,2流程图符号表示(1)起止框:表示程序的开始和结束。,起止框,(2)判断框,(3)处理框,(4)调用框,(5)指向线,(6)连接框,返回本节,5.1.3 三种基本结构,(1)顺序结构,A,B,C,(2)分支结构,测试条件,A,B,Y,N,(3)循环结构,测试条件,A,N,Y,5.2 顺序程序设计,下面举例说明顺序程序的设计。【例4.1】试编写一程序计算以下表达式的值。=(v-(*+-540)/x式中x、v均为有符号字数据。设、的值存放在字变量、V中,结果存放在双字变量之中,程序的流程图如图4.1所示。,返回本章首页,图4.1 顺序运算程序流程图,源程序如下:DATASEGMENT XDW200 YDW100 ZDW3000 VDW10000 WDW2 DUP(?)DATAENDSCODESEGMENTASSUME DS:DATA,CS:CODE,START:MOVAX,DATA MOVDS,AX;DATADS MOVAX,X IMULY;(X)*(Y)DX:AX MOVCX,AX MOVBX,DX;(DX:AX)(BX:CX)MOVAX,Z CWD;(Z)符号扩展 ADDCX,AX ADCBX,DX;(BX:CX)+(DX:AX)(BX:CX)SUBCX,540 SBBBX,0;(BX:CX)-540(BX:CX)MOVAX,V,CWD;(V)符号扩展SUB AX,CXSBB DX,BX;(DX:AX)-(BX:CX)(DX:AX)IDIV X;(DX:AX)/XMOV W,AX;商WMOV W+2,DX;余数DXW+2MOV AH,4CHINT 21HCODEENDS;退出DOS 状态END START,【例4.2】,【例4.2】已知某班学生的英语成绩按学号(从1开始)从小到大的顺序排列在TAB表中,要查的学生的学号放在变量NUM中,查表结果放在变量ENGLISH中。编写程序如下:,STACK SEGMENT STACKDB 200 DUP(0)STACK ENDSDATASEGMENTTAB DB 80,85,86,71,79,96 DB 83,56,32,66,78。84NUM DB 10ENGLISH DB?DATA ENDSCODE SEGMENT,ASSUME DS:DATA,SS:STACK,CS:CODEBEGIN:MOVAX,DATA MOVDS,AX LEABX,TAB MOVAL,NUM DECAL XLAT TAB MOVENGLISH,AL MOVAH,4CH INT21HCODEENDS ENDBEGIN,5.3 分支程序设计,5.3.1 用条件转移指令实现程序分支5.3.2 用跳转表实现多路分支,返回本章首页,5.3.1 用条件转移指令实现程序分支,【例4.3】编写计算下面函数值的程序:1X0Y=0X=0-1X0设输入数据为X、输出数据Y,且皆为字节变量。程序如下:,DATASEGMENTX DB-10Y DB?DATAENDSSTACK SEGMENT STACK DB 200 DUP(0)STACK ENDSCODESEGMENTASSUME DS:DATA,SS:STACK,CS:CODESTART:MOVAX,DATA,MOVDS,AX CMPX,0;与0进行比较 JGEA1;X0转A1 MOVY,-1;X 0时,-1Y JMPEXITA1:JGA2;X0转A2 MOVY,0;X=0时,0Y JMPEXITA2:MOVY,1;X0,1Y EXIT:MOVAH,4CH INT21H CODE ENDS ENDSTART,图4.2 分支运算程序流程图,【例4.4】,【例4.4】试编一程序,求三个带符号字数据中的最大值,并将最大值存入MAX字单元中。设三个带符号数分别在三个字变量X、Y、Z中存储。程序流程图如图4.3所示,图4.3例4.4程序流程图,程序如下:STACK SEGMENT STACK DB 200 DUP(0)STACK ENDSDATA SEGMENTX DW 00ABHY DW 5Z DW 200MAX DW?DATA ENDSCODE SEGMENT,ASSUME DS:DATA,SS:STACK,CS:CODE START:MOVAX,DATA MOVDS,AX MOVAX,X CMPAX,Y;XY?JGL1 MOVAX,Y;YZ?CMPAX,Z JGEXITL2:MOVAX,Z JMPEXIT,L1:CMPAX,Z;XZ?JLEL2EXIT:MOVMAX,AX MOVAH,4CH INT21HCODEENDS ENDSTART,返回本节,5.3.2 用跳转表实现多路分支,【例4.5】设某程序有8路分支,试根据给定的N值(18),将程序的执行转移到其中的一路分支。程序流程如图4.4所示。,程序如下:DATASEGMENTTABDW P1,P2,P3,P4,P5,P6,P7,P8N DB 5DATAENDSSTACK SEGMENT DB 200 DUP(0)STACK ENDSCODESEGMENT ASSUME DS:DATA,SS:STACK,CS:CODE,START:MOV AX,DATAMOVDS,AXMOVAL,NDECALADDAL,ALMOVBL,ALMOVBH,0JMPTABBX,P1:JMP EXITP2:JMP EXITP2:JMP EXITP3:,JMPEXITP8:EXIT:MOVAH,4CH INT21HCODEENDS ENDSTART上述程序中的无条件转移指令的转移地址采用的是变址寻址。同理,转移地址也可以用寄存器间接寻址或基址加变址寻址,读者可自行考虑。,返回本节,5.4 循环程序设计,5.4.1 循环程序的结构5.4.2 单重循环程序设计5.4.3 多重循环程序设计,返回本章首页,5.4.1 循环程序的结构,1初始化部分2循环体部分3循环控制部分,循环程序的常见结构形式如图4.5(a)、(b)所示。,返回本节,5.4.2 单重循环程序设计,1计数控制2条件控制,1计数控制,【例4.7】已知有几个元素存放在以BUF为首址的字节存贮区中,试统计其中正元素的个数。显然,每个元素为一个8位有符号二进制数,统计其中正元素的个数可用循环程序实现。其程序流程图如图4.6所示。,源程序如下:DATA SEGMENT BUF DB 10,12,-3,6,-7,8,10 LENGTH EQU$-BUF NUM DW?DATA ENDSCODE SEGMENT ASSUME CS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX LEA BX,BUF;初始化部分 MOV CX,LENGTH MOV AX,0,LOP:CMP BYTE PTR BX,0;循环体部分 JBE NEXT INC AXNEXT:INC BX LOOP LOP;控制部分 MOV NUM,AX;保存结果 MOV AH,4CH INT 21HCODE ENDS END START,【例4.8】,【例4.8】试编写一程序,要求比较两个字符串STR1和STR所含字符是否相同,若相同则显示MATCH!,若不相同则显示NO MATCH!。(程序略)其流程图如图4.7所示。,图4.7 程序流程图,【例4.9】试编一个程序将字单元BUF中所含1的个数存入COUNT单元中。要测出BUF字单元所含1的个数,首先将BUF中的数送给寄存器AX,然后将AX寄存器逻辑左移一次,如果CF=1,则表明AX中的最高位为1,则计数器CL计数1次,如果CF=0,表明AX最高位为0,这样依次将最高位移入CF中去测试。移位之后,判断AX的值是否为0,如果为0则结束循环,不为0,则继续循环。其流程图如图4.8所示。,2条件控制,程序如下:STACK SEGMENT STACK DB 200 DUP(0)STACK ENDSDATASEGMENTCOUNT DB?DATAENDSCODESEGMENT ASSUME DS:DATA,CS:CODE,SS:STACK,START:MOVAX,DATAMOVDS,AXMOVAX,BUFMOVCL,0;计数器为0LOPA:ANDAX,AXJEEXIT;(AX)=0,结束循环SHLAX,1 JNCLOPA INCCL;产生进位,(CL)+1CLJMPLOPAEXIT:MOVCOUNT,CLMOVAH,4CH;源程序结束INT21HCODEENDSENDSTART,返回本节,5.4.3 多重循环程序设计,多重循环程序设计的原则与单重循环类似,但也有其特殊性。在多重循环程序设计中,要注意内层循环和外层循环之间的参数协调。当考虑外层参数时,要兼顾内层循环的需要,当修改内层循环参数时,也要注意对外层循环的影响。尤其注意,从外层循环程序再次进入内层循环时,初始条件必须重新设置。【例4.10】在以BUFFER为首址的存储区中存放有10个单字节无符号整数,现需将它们按小到大的顺序排列在BUFFER存储区中,试编写其程序。为了方便说明二重循环,这里采用“简单选择”法进行排序。,“简单选择”法进行排序的总体思路:对含有n个数的无序数列按照从小到大的顺序排序。在每一趟排序过程中,从n-i+1(i=1,2,n-1)个数中选出最小的数,并把它放在第i个位置上.,程序如下:;数据段DSEG SEGMENTBUFFER DB 23,12,45,127,3,58N EQU$-BUFFERDSEG ENDS;代码段CSEG SEGMENT ASSUME CS:CSEG,DS:DSEG,START:MOVAX,DSEG MOVDS,AX MOVBX,OFFSET BUFFER DEC BX MOV SI,1;I=1FORI:MOVDI,SI INC DI;J=I+1 MOVAL,BX+SIFORJ:CMPAL,BX+DI;AI与AJ比较 JBENEXTJ;AI小于等于AJ转 XCHGAL,BX+DI;AI与AJ交换 MOVBX+SI,AL,NEXTJ:INC DI;J=J+1 CMP DI,N JBE FORJ;J=N时转NEXTI:INC SI;I=I+1 CMP SI,N-1 JBE FORI;I=N-1时转 MOV AH,4CH INT 21HCSEG ENDSENDSTART,程序运行后,BUFFER区中的内容如下:3,12,23,45,58,127,返回本节,5.5 子程序设计,5.5.1 子程序的概念5.5.2 子程序的定义5.5.3 子程序设计方法5.5.4 子程序应用举例5.5.5 子程序的嵌套与递归调用,返回本章首页,5.5.1 子程序的概念,一。子程序和主程序 在程序设计中如果反复用到某个具有独特功能的程序片段,例如输入输出处理程序、代码转换程序等,在程序设计时,通常把这些功能相对独立的部分编写成一个独立的程序段,称为子程序(Subroutine)。如果某个程序片段具有通用性,可供许多程序共享,可以把它设计成子程序,例如标准函数程序。这样就能有效缩短程序长度、节约存储空间、便于程序的阅读和调试,也大大减轻了程序设计者的工作量。子程序由主程序(也称调用程序)根据需要进行调用,执行完成后又返回主程序继续执行。子程序在汇编语言中又称为过程(Procedure),其执行过程如图所示。,返回本节,。CALL 子程序下一条指令。,调用程序,第一条指令。RET,子程序,二。子程序调用与返回指令 子程序调用与返回的过程,实际上也就是程序流程无条件转移的过程。只不过子程序调用指令要求程序流程无条件转向一个特定地址,即子程序第一条指令(也称子程序入口地址),而子程序返回指令RET要求程序流程无条件转向主程序中CALL指令的下一条指令继续执行。1。子程序调用指令CALL 子程序和调用程序可能在同一代码段中,也可能处在不同的代码段中。因此子程序的调用又可分为段内调用和段间调用两大类。,(1)段内直接调用 当调用程序和被调用的子程序存放在同一代码段中时,两段程序的段地址相同,因此只需修改指令指针寄存器IP的内容,就可以使程序转到相应的子程序的入口地址继续执行。格式:CALL DST功能:SPSP-2(SP+1,SP)IP IP IP+D16执行操作:该指令首先移动栈顶指针,并把子程序的返回地址(即调用程序中CALL指令的下一条指令的地址,也就是当前 IP寄存器内容)压入堆栈,供子程序返回主程序时使用。然后,再将子程序入口地址IP+D16装入IP寄存器,以便使程序跳转到子程序的第一条指令继续执行。,(2)段内间接调用格式:CALL DST功能;SPSP-2(SP+1,SP)IP IPEA 在该指令格式中,DST可以用除立即数以外的其他方式进行寻址,并把找到的偏移地址EA直接装入IP寄存器作为子程序入口地址。该指令的其他操作与段内直接调用相同。,(3)段间直接调用格式:CALL DST功能:SPSP-2(SP+1,SP)CS SP SP-2(SP+1,SP)IP IP 偏移地址 CS 段地址由于是段间调用,因此,必须同时修改指令指针寄存器IP和段寄存器CS。此指令格式中的操作数DST,在汇编源程序中可直接写为程序符号地址,而在机器指令中转换为四字节地址,其中,两低字节中为偏移地址,两高字节中为段地址。,(4)段间间接调用格式:CALL DST功能:SPSP-2(SP+1,SP)CS SP SP-2(SP+1,SP)IP IP(EA)CS(EA+2)其中DST只能使用各种存储器寻址方式。当指令找到有效地址EA后,就把EA相邻的4个字节单元中的低位字装入IP,高位字装入CS。,2.子程序返回指令RET,子程序执行完后,必须用RET返回主程序并继续执行。由于子程序的返回地址在子程序调用时,已经由 CALL指令压入堆栈保存,RET指令的任务就是从堆栈中正确地取得返回地址,传送给IP或CS、IP。与调用指令CALL相对应,返回指令RET也可分为段内返回和段间返回指令。,(1)段内返回,格式:RET功能:IP(SP+1,SP)SPSP+2 该指令与段内调用指令(直接调用或间接调用)配合使用。执行RET指令后,可把由CALL指令压入堆栈的返回地址取回寄存器IP,以保证子程序正确返回。,(2)段内带立即数返回,格式:RET 表达式功能:IP(SP+1,SP)SPSP+2 SP SP+D16 这种指令格式允许在子程序的返回地址出栈后,再将栈顶指针寄存器SP按表达式计算出的常数D16移动一段位移,以抛弃多余的数据。这种功能在利用堆栈进行参数传递的子程序设计中十分有用。,(3)段间返回,格式:RET功能:IP(SP+1,SP)SPSP+2 CS(SP+1,SP)SPSP+2 该指令与段间调用指令(直接调用或间接调用)配合使用,可以把CALL指令执行中压入堆栈的4个字节弹出,分别装入CS和IP寄存器以保证程序的正确返回。,(4)段间带立即数返回,格式:RET 表达式功能:IP(SP+1,SP)SPSP+2 CS(SP+1,SP)SPSP+2 SPSP+D16 该指令除了因为是段间返回而需要同时弹出CS和IP寄存器的内容外,其他特性与段内带立即数返回格式相同。注意:CALL指令和RET指令都不影响标志寄存器(PSW)中的标志位。,5.5.2 子程序的定义,子程序的定义是由过程定义伪指令PROC和ENDP来完成的。其格式如下:过程名 PROC NEAR/FAR RET 过程名 ENDP说明:其中PROC表示过程定义开始,ENDP表示过程定义结束。过程名是过程 入口地址的符号表示。过程名称的命名与普通标号、变量的命名方法相同。一般过程名同标号一样,具有三种属性,即段属性、偏移地址属性以及类型属性。过程的类型分为近(NEAR)类型和远(FAR)类型。如果不指定,则默认为近类型。,返回本节,5.5.3 子程序的设计要求,子程序应该是一个结构清晰、功能明确、独立性好、通用性强的程序段。为了实现上述的目的,在编写子程序时应满足以下基本要求:1.具有一定的通用性2.注意信息的保护与恢复3.正确的使用堆栈4.选用适当的方法实现主程序与子程序间的参数传递5.清晰的子程序文本,2.寄存器的保护和恢复,汇编语言程序的主要操作对象是CPU中的寄存器。由于子程序和主程序在运行中很可能会用到相同的一些寄存器,如果不进行寄存器保护,则主程序存放在寄存器中的有用数据可能会被子程序修改,从而造成返回后主程序无法正常运行。为此,必须在子程序修改寄存器内容之前对寄存器进行保护,称为寄存器保护。,寄存器保护既可以在主程序中进行,也可以在子程序中进行。一般在主程序中保护的寄存器在主程序中恢复,在子程序中保护的寄存器在子程序中恢复。大多数程序都是在子程序中进行寄存器保护的。有关说明:(1)保护寄存器内容最方便的方法是使用堆栈。在利用堆栈进行寄存器的保护和恢复时,要注意堆栈的后进先出特性。(2)在子程序当中可能会破坏标志寄存器中的部分标志,可用PUSHF指令和POPF指令保护和恢复标志寄存器。但一般不在子程序中保护和恢复标志寄存器。(3)如果子程序中被修改的寄存器,在主程序中并没有使用或不再使用,该寄存器可以不保护。,例如:若子程序SUB1中改变了寄存器AX,BX,CX,DX的值,则可采用如下方法保护和恢复现场。SUB1PROCPUSHAXPUSHBX PUSHCX;保护现场PUSHDX,POP DX POP CX POP BX;恢复现场 POP AX RET;返回断点处SUB1ENDP,4主程序与子程序间的参数传递方式,(1)寄存器法(2)约定单元法(3)堆栈法,返回本节,有关概念:主程序在调用子程序时,往往要向子程序传递一些参数;同样,子程序运行后也经常要把一些结果参数传回给主程序。主程序与子程序之间的这种信息传递称为参数传递。我们把由主程序传给子程序的参数称为子程序的入口参数。把由子程序传给主程序的参数称为子程序的出口参数。一般子程序既有入口参数,又有出口参数。,5.子程序的说明信息,为了能正确地使用子程序,在给出子程序代码时还要给出子程序的说明信息。子程序说明信息一般由如下几个部分组成:(1)子程序名(2)功能描述(3)入口和出口参数(4)其他说明,子程序说明信息至少应该包含前三部分内容。例如:;子程序名:AHTOASC;功能:把8位二进制数转换为2位十六进制数的ASCII码;入口参数:AL=欲转换的8位二进制数;出口参数:AH=十六进制数高位的ASCII码 AL=十六进制数低位的ASCII码;其他说明:(1)近过程(2)除AX寄存器外,不影响其他寄存器(3)调用了HTOASC实现十六进制数到ASCII 码的转换,5.5.4 子程序设计举例,【例4.12】将一个给定的二进制数按位转换成相应的ASCII码字符串,送到指定的存储单元并显示。如二进制数10010011转换成字符串为10010011。要求将转换过程写成子程序,且子程序应具有较好的通用性,而必须能实现对8位和16位二进制数的转换。,入口参数:DX 存放待转换的二进制数 CX 存放待转换数的位数(8位或16位)DI 存放ASCII码首地址出口参数:转换后的字符串存放在以DI作指针的字节存贮区中程序如下:DATASEGMENTNUM8 DB93HNUM16 DW0ABCDHASCBUF DB20H DUP(?)DATAENDS,CODESEGMENTASSUME DS:DATA,CS:CODESTART:MOVAX,DATAMOVDS,AXMOVDX,0MOVDL,NUM8;转换二进制数送DXMOVCX,8;置位数8LEADI,ASCBUF;字符串首址DICALLBTASC;调用子程序BTASCMOV BYTE PTR DI,0DHMOV BYTE PTR DI+1,0AH,MOV BYTE PTR DI+2,$LEA DX,ASCBUFMOV AH,9INT 21HMOV DX,NUM16MOV CX,16;置位数16LEA DI,ASCBUFCALL BTASC MOV BYTE PTR DI,0DHMOV BYTE PTR DI+1,0AHMOV BYTE PTR DI+2,;LEA DX,ASCBUF 显示转换后的字符串MOV AH,9 INT 21H MOV AH,4CH INT 21H,BTASCPROCPUSHAX;保存AXMOVAL,0CMPCX,8;比较8位数JNEL1;直接转换16位数MOVDH,DL;8位数转换送DHL1:ROLDX,,1;DX最高位移入CFRCLAL,1;CF移入AL最低位ADDAL,30HMOVDI,AL,INCDI LOOPL1 POPAX RETBTASCENDPCODEENDSENDSTART,返回本节,5.5.5 子程序的嵌套与递归调用,1子程序的嵌套 子程序不但可以被主程序调用,而且也可以被其他子程序调用。我们把一个子程序调用另一个子程序称为子程序的嵌套调用。2子程序的递归调用 子程序的递归调用是指一个子程序直接或间接地调用自己。递归子程序一般对应于数学上对函数的递归定义,它往往能设计出效率较高的程序,完成相当复杂的计算,因而是很有用的。,【例4.15】,【例4.15】试编制计算N!(N0)的程序。N!=N*(N-1)*(N-2)*1其递归定义如下:0!=1(N=0)N!=N*(N-1)!(N1)计算N!的子程序FACT的流程图如图4.10所示。,【例4.16】计算5!的程序示例,RESULT是保存阶乘的存储单元。程序如下:STACKSEGMENT STACKDB 200 DUP(0)STACKENDSDATASEGMENTNDW 5RESULTDW?DATAENDSCODESEGMENTASSUME CS:CODE,SS:STACK,DS:DATA,START:MOVAX,DATAMOVDS,AXMOVAX,NCALLFACTMOVAX,RESULTMOVAH,4CHINT21HFACTPROC;子程序FACTCMPAX,0JNEL1,MOVRESULT,1JMPEXITL1:PUSHAXDECAXCALLFACTPOPAX MUL RESULT MOVRESULT,AXEXIT:RETFACTENDPCODEENDSENDSTART,返回本节,Thank you very much!,本章到此结束,谢谢您的光临!,返回本章首页,结 束,