汇编第5章-子程序设计.ppt
《汇编第5章-子程序设计.ppt》由会员分享,可在线阅读,更多相关《汇编第5章-子程序设计.ppt(184页珍藏版)》请在三一办公上搜索。
1、子程序设计,第五章,在汇编语言中,子程序又称为过程,是程序设计中的一种重要方法。在实际的程序中,常常会遇到在同一程序或多个程序中多次遇到同一任务的处理过程。如果每一次处理都编写一次程序,不仅加大了编程人员的工作量,也浪费存储空间。,为了避免重复编程,人们将经常遇到的处理任务编写成一些独立的程序段,以供其他程序调用,这就是子程序设计。为了能更好地把握子程序的编写和使用方法,必须先掌握堆栈的有关概念。,5.1 堆栈,5.2 子程序的调用与返回,5.3 子程序设计,5.4 子程序设计案例,习 题 5,5.1 堆栈,在汇编语言和机器语言中,堆栈在物理结构上是一段存放数据的连续的内存区域,以及一个称为栈
2、顶指针的专用存储单元。,堆栈中只能存入16位的字型数据,存入数据的操作称为“进栈”或“压栈”,已存入的数据也可以取出,称为“出栈”或“弹出”,数据的存取操作由专用指令完成。从逻辑上说,堆栈是一种按“先进后出”或“后进先出”原则进行操作的数据结构,栈顶指针用于指出入栈操作和出栈操作的位置。,5.1.1 建立堆栈,图5-1是堆栈的物理结构示意图.图中标出的SS和SP是与堆栈密切相关的寄存器,SS存放堆栈所占用内存区域的段地址,SP所指向的位置称为栈顶。,图5-1 堆栈的逻辑结构图,一个程序如果要使用堆栈,首先要建立堆栈。建立堆栈实际上就是在程序中定义一个堆栈段,并给SS和SP赋值即可。定义堆栈段的
3、格式如下:段名 SEGMENT STACK DW n DUP(?)段名 ENDS,【说明】(1)保留字STACK是堆栈段的专用符号,SEGMENT后面的保留字STACK表明这个段专供堆栈使用。(2)段定义中用“DW n DUP(?)”说明堆栈所用内存区的大小为2n字节,其中n是一个常量。可根据程序需要,调节堆栈段的大小。因为堆栈只能存放字型数据,所以习惯上都是用DW伪指令来定义栈的大小。这不并是说用其它伪指令不行。,(3)按基本格式定义的栈是一个空栈,栈中没有存放有效数据。(4)为了使SS和SP在程序执行时取得正确的值,必须在源程序中写一条伪指令:ASSUME SS:堆栈段段名,但不需要像DS
4、和ES一样在程序中用指令进行赋值。对SS和SP的赋值是由操作系统在把执行程序调入内存时由DOS本身完成的,DOS将把SS赋值为堆栈段的段地址,把SP赋值为2n,这时用户使用的就是系统堆栈。当然,用户也可以根据程序的需要按自己的要求定义自己的堆栈,这时,用户不仅要定义堆栈段,而且还要用指令给SS和SP赋值。,5.1.2 堆栈操作指令,栈操作指令以它特有的方式存取数据,属于数据传送类指令,但又与MOV等指令有很大的区别。1.进栈指令【指令格式】PUSH S【功能】先把SP的值减去2,然后把操作数S指明的字型数据放入以SS为段地址、SP为偏移地址所对应的字内存单元中。,【说明】(1)这是单操作数指令
5、,操作数S可以是包括段寄存器在内的任何字型寄存器,或者内存型寻址方式,但不能是立即寻址。(2)PUSH指令的功能包括移动栈顶和存入数据两部分,两部分连续完成,密不可分。,(3)操作数S进栈是以减2以后的SP的值作为偏移地址,但程序中不允许出现SP的写法。不要与基地址寄存器或变址寄存器用作偏移地址时的写法相混淆,也就是说,把PUSH指令理解成下面两条指令的组合是不正确的:SUB SP,2 MOV SP,S 因为指令“MOV SP,S”存在语法错误。,(4)PUSH指令会导致栈顶指针的移动,如果用PUSH指令把很多数据进栈,使SP不断减2,就有可能超出栈的有效范围。在一些高级语言中这种现象会导致堆
6、栈溢出错误,但8086对此并不做任何检测和警告。因此要求编程人员自己注意控制堆栈的大小,估计可能进栈的数据量,以免由于栈溢出导致一些不可预测的后果。,假设:SS=3000H,SP=0200H,AX=1234H,则CPU执行PUSH AX指令后:SS的内容不变,SP=01FEH,栈顶字单元(301FEH)=1234H,AX的内容不变。,2出栈指令【指令格式】POP D【功能】从SS为段地址、SP为偏移地址的栈顶单元中取出一个字型数据,送到操作数D指定的位置,然后把SP的值加2。对操作数D的寻址方式要求与PUSH指令相同。,堆栈通常用于临时保存数据。一般做法是先用PUSH指令把需要保存的数据入栈,
7、然后完成一定的指令序列,再用POP指令把原先保存的数据出栈。用堆栈保存数据的特点是不用定义变量,不必关心被保存的数据到底在栈的什么位置,只要保证出栈和进栈的对应关系即可。当CPU中的寄存器不够使用时经常用堆栈临时保存数据。,假设:SS=3000H,SP=0200H,栈顶单元的数据为1234H,即(30200H)=1234H,则CPU执行POP AX指令后,SS的内容不变,SP=0202H,AX=1234H。栈顶所指位置以上的部分是堆栈的空闲区,以下部分是已入栈的数据存放区(见图5-1),例5-1用来说明PUSH指令和POP指令对堆栈的影响。,【例5-1】设AX1234H,BX56578H,SP
8、1000H,分别逐条执行下列指令,用内存图的形式画出堆栈的变化情况,并分析程序段执行完后AX和BX寄存器的值。PUSH AX PUSH BX POP AX POP BX,【解】堆栈变化见图5-2,程序段执行完后AX5678H,BX1234H。,(a)执行前,(b)执行PUSH AX 后,(c)执行 PUSH BX 后,(d)执行 POP AX 后,(e)执行 POP BX 后,图5-2 执行PUSH 和POP 指令前后堆栈的变化情况,3.标志寄存器入出栈指令(1)标志寄存器入栈指令【指令格式】PUSHF【功能】把SP的值减2,并把16位的标志寄存器的内容送入SS:SP所指向的内存字单元,即把标
9、志寄存器的值入栈。,(2)标志寄存器出栈指令【指令格式】POPF【功能】把栈顶的一个16位的字型数据从堆栈中弹出,然后送入标志寄存器,并把SP的值加2。,这两条指令相互配合可以设置标志寄存器中的任意一个标志位,一般的做法是:PUSHF POP AX;按标志位的分布情况和实际需要,修改AX中的值 PUSH AX POPF,5.2 子程序的调用与返回,把可以多次调用、能够完成特定处理任务的程序段编写成独立的程序模块,称为子程序。子程序可以被其他程序调用,调用这些子程序的程序称为主程序。,在主程序中,如果调用子程序,就把控制转移到子程序,这个过程称为转子;子程序执行完后,再把控制返回到主程序,这个过
10、程称为返主。主程序与子程序之间的转换关系如图5-3所示。,图5-3 主程序和子程序间的转换关系,为了实现主程序调用子程序以及子程序执行完后能返回主程序,在8086/8088指令系统中提供了一组调用指令CALL和返回指令RET,5.2.1 子程序调用指令CALL,主程序调用子程序需要通过调用指令来实现。调用指令的基本功能是将返回地址,即调用指令的下一条指令的偏移地址或下一条指令的段地址和偏移地址(简称为断点)压入堆栈,并按照某种寻址方式转向子程序的入口。,主程序和子程序可以在同一个代码段中,也可以在不同的代码段中。前者称为段内调用,只涉及偏移地址,后者称为段间调用,将涉及段地址和偏移地址两部分。
11、,段内调用指令的基本功能是:将断点地址,即当前的IP值压入堆栈,段寄存器CS的内容保持不变。然后将子程序的入口地址(即偏移地址)装入IP中。段间调用指令执行时,其断点地址及子程序的入口地址包括段地址和偏移地址。,子程序入口地址直接出现在调用指令中(用子程序名表示),称为直接调用;子程序入口地址存放在寄存器或存储单元中,而寄存器名或存储单元的地址出现在调用指令中,称为间接调用。,1.段内直接调用【指令格式】CALL DST 或:CALL NEAR PTR DST;DST为子程序名或语句标号,执行操作:(SP)(SP)2;即修改堆栈指针;(SP+1),(SP)(IP);IP(断点)入栈;(IP)(
12、IP)+16 位偏移量;转向子程序入口。,说明:该指令的第一步是把子程序的返回地址(即CALL指令的下一条指令地址)压入堆栈中,第二步是转移到子程序的入口地址,执行该子程序。,指令中的DST为子程序的入口地址,16位偏移量是子程序入口地址与CALL指令的下一条指令地址之间的差值,所以这种调用也称为相对调用。,【例5-2】指令CALL SUB1SUB1为子程序名,即过程名或语句标号,且子程序SUB1被定义为段内标号或近过程名,则CALL SUB1就是段内直接调用。,若假设指令CALL SUB1的第一个字节的地址为2000H:1000H,段内直接调用指令为三字节,因此其返回地址是2000H:100
13、3H。再设子程序SUB1的入口地址为2000H:3000H,那么,该指令执行后,堆栈、IP和CS的内容如图5-4所示。,图5-4 段内直接调用示意图,2.段内间接调用【指令格式】CALL DST 或:CALL WORD PTR DST;DST为通用字寄存器或字存储单元,执行操作:1.(SP)(SP)2;修改堆栈指针2.(SP+1),(SP)(IP);IP入栈3.(IP)(DST);转向子程序,说明:该指令与段内直接调用类似,区别是将字寄存器或存储单元内容作为子程序入口地址送入IP。,【例5-3】下面三种情况均是段内间接调用。CALL BX CALL WORD PTR BX CALL ES:WO
14、RD PTR SUB2,3.段间直接调用【指令格式】CALL FAR PTR DST;DST为子程序名或语句标号,执行操作:1.段点入栈:(SP)(SP)2(SP+1),(SP)(CS);(CS)入栈;(SP)(SP)2(SP+1),(SP)(IP);(IP)入栈;,2.子程序入口地址送IP和CS,转入子程序:(IP)偏移地址;IP由指令中的偏移地址取代;(CS)段地址;CS由指令中的段地址取代.,说明:该指令的操作与段内直接调用指令基本类似,不同的只是:其一,调用时不仅要保护偏移地址,而且要保护段地址,它们的顺序是CS先压栈,然后再把IP压栈;其二,CS和IP的值,是直接将子程序入口的段地址
15、和偏移地址送入CS和IP中。,【例5-4】指令CALL FAR PTR SUB2SUB2是子程序名,且过程SUB2与调用指令不在同一个代码段,即段间直接调用若设该指令的第一个字节的地址为2000H:0200H,段间直接调用指令为五字节,因此,其返回地址为2000H:0205H。再设子程序SUB2的入口地址为4000H:0100H,那么,该指令执行后,堆栈、IP和CS的内容如图5-5所示。,图5-5 段间直接调用示意图,4.段间间接调用【指令格式】CALL DWORD PTR DST;DST为双字存储器单元操作数,执行操作:1.段点入栈:(SP)(SP)2(SP+1),(SP)(CS);CS入栈
16、(SP)(SP)2(SP+1),(SP)(IP);IP入栈,2.子程序入口地址送IP和CS,转入子程序:(IP)(EA);指令中的有效地址所指字内容送入IP;(S)(EA+2);指令中的有效地址+2所指字内容送入CS.,说明:该指令的操作与段间直接调用指令基本类似。不同的是根据寻址方式求出EA后,把指定存储器单元的字内容送到IP寄存器,再把下一个字的内容送到CS寄存器。,【例5-5】下面指令都是段间间接调用。CALL DWORD PTR BX CALL DWORD PTR SADR上面两条指令的操作数均为双字存储器类型。其中第一指令是由BX的内容指定数据段中的一个双字存储单元地址,而第二条指令
17、是由SADR指定数据段中的一个双字存储单元地址。它们所指的字单元存放着子程序的入口偏移地址,下一个字单元存放着子程序的入口段地址。,注意:调用的类型与子程序/过程定义时的类型必须一致。,5.2.2 子程序返回指令RET,子程序执行完后需要返回主程序,这个功能由子程序返回指令RET来实现。返回指令总是与调用指令配合使用,且返回指令通常放在子程序的末尾(即出口处),使子程序执行完毕能够返回主程序,继续执行原来的程序。,与调用指令相对应,返回指令也有段内返回和段间返回两种形式。其功能是从堆栈的栈顶弹出返回地址。段内返回是从栈顶弹出一个字数据送入IP;段间返回是从栈顶弹出两个字数据分别送入IP和CS,
18、从而控制程序返回到主程序。,1、段内返回指令【指令格式】RET执行操作:(IP)(SP+1),(SP);栈顶字内容弹出送入IP;(SP)(SP)+2,说明:当子程序被定义为近过程时,RET指令执行的操作是段内返回,它把堆栈顶部的一个字内容送入IP,作为返回的偏移地址。,2、段间返回指令【指令格式】RET执行操作:(IP)(SP+1),(SP);栈顶内容送入IP(SP)(SP)+2;修改堆栈指针(CS)(SP+1),(SP);栈顶内容送入CS(SP)(SP)+2;修改堆栈指针,说明:当子程序被定义为远过程时,RET指令执行的操作是段间返回,它把堆栈顶部的两个字内容依次送入IP和CS,分别作为返回
19、的偏移地址和段地址。,3、带参返回指令指令格式:RET n;n为立即数或数值表达式,说明:1、带立即数返回指令分为带立即数的段内返回和带立即数的段间返回两种形式。它们都是在完成基本返回功能后,再执行:(SP)(SP)+n的操作,即修改堆栈指针。2、n 一 般是偶数,主要用以废弃堆栈中的一些数据。,【例5-6】用带参返回指令RET 4,跳过子程序调用前传入的参数。MOV AX,M1 PUSH AX;参数M1被压栈 MOV AX,M2 PUSH AX;参数M2被压栈 CALL SUBPROC,SUBPROC:RET 4;带参返回在上面的程序段中,调用SUBPROC子程序前,通过两次压栈将两个参数M
20、1和M2传递给子程序使用,而在子程序执行完后,这两个参数也就没用了,所以必须废弃它们。为此,采用带参返回指令,使栈顶指向第一条PUSH指令执行以前的位置。,5.3 子程序设计,子程序是供主程序调用的。为了使不同的程序在不需要了解子程序的内部结构及其算法情况下,方便地调用子程序,一个完整的子程序应当包括:子程序调用方法说明、保护现场和恢复现场、子程序定义、参数传递方法等部分。,本小节主要介绍子程序调用方法说明与保护现场和恢复现场,对于子程序定义、参数传递方法将放在后面小节作专题介绍。,1、子程序调用方法说明为了使子程序便于阅读、维护和使用,以及明确子程序的功能和主程序与子程序之间的联系,让使用者
21、完全不必关心所用子程序的算法及处理过程,一般应提供子程序调用方法说明。主要包含下述几项内容:,1.子程序名:供调用子程序时使用;2.子程序功能:用来指明该子程序完成什么样的操作或实现何钟功能,供选择子程序时参考;3.入口参数:说明调用子程序前应该把什么样的数据放在什么地方;,4.出口参数:说明调用后从什么地方取得处理结果;5.保护的寄存器:指出子程序中哪些寄存器受到保护,哪些寄存器的内容被破坏。,【例5-7】编写一个子程序,对一个无符号的字型数组的各元素求和。在调用子程序之前,已把数组的段地址放在DS中,起始偏移地址放在寄存器SI中,数组元素个数(0)放在CX中。要求子程序把计算结果以双字的形
22、式存放,高位放在DX中,低位放在AX中。并为该子程序编写调用方法说明。,【解】;子程序名:Sum;子程序功能:对字型数组各元素求和,结果是双字;入口参数:DS:SI:数组的逻辑地址,CX:数组元素个数;出口参数:DX,AX:各数组元素的和,DX为高16位,AX为低16位;保护的寄存器:BX;破坏的寄存器:CX,sum PROC NEAR PUSH BX;保护寄存器BX XOR AX,AX MOV DX,AX;DX,AX清0 MOV BX,AXs1:ADD AX,BX+SI;累加 ADC DX,0;加进位 INC BX;修改数组地址,INC BX;修改数组地址 LOOP s1;循环控制 POP
23、BX;恢复寄存器BX RET;返回sum ENDP子程序调用方法说明一般提行注释,并放在子程序的前面。,2、保护现场和恢复现场保护现场和恢复现场是子程序设计时必须考虑的问题。子程序中需要使用的寄存器,有可能在调用子程序前主程序正在使用,其值在从子程序返回主程序后还要继续使用,我们把这些寄存器的值和状态寄存器的状态均称之为现场。显然,子程序执行前需要保护现场,返回时要恢复现场。,保护和恢复现场的工作可以在主程序中完成,也可以在子程序中完成。一般情况下,是在子程序的开始安排一串保护现场语句,子程序返回前恢复现场。这样处理,主程序在转子前后均不必考虑保护和恢复现场的工作,其处理流程显得清晰。,保护和
24、恢复现场,更简洁的方法是利用压栈指令,将寄存器内容及状态标志寄存器的内容压入堆栈,恢复时再从堆栈中弹出。尤其在嵌套子程序设计中,由于压栈和出栈指令会自动修改堆栈指针,保护和恢复现场的工作层次清晰,只要注意堆栈操作的先进后出原则,就不会造成混乱和错误。,【例5-8】使用堆栈保护和恢复现场,程序如下:SUBR PROC PUSH AX;保护现场 PUSH BX PUSH CX PUSH DX;子程序处理部分语句,POP DX;恢复现场 POP CX POP BX POP AX RET;返回SUBR ENDP注意:恢复现场时寄存器出现的顺序应与保护现场时寄存器出现的顺序相反。,5.3.2 子程序的定
25、义,汇编语言中,子程序也叫过程。其定义格式为:过程名 PROC NEAR/FAR RET过程名 ENDP,说明:(1)过程名用以标识不同的过程。过程名是提供给其他程序调用时使用的,因而不能省略。过程名具有与语句标号相同的属性,即具有段地址、偏移地址和类型三个属性。过程名的段地址和偏移地址是指过程中第一条可执行指令的段地址和偏移地址,过程名的类型由格式中的NEAR/FAR指定。,(2)PROC与ENDP相当于一对语句括号,将子程序的处理部分(也称过程体)括在其内。过程体为一段相对独立的程序,是完成子程序功能的程序主体。(3)NEAR或FAR是过程的类型说明参数。NEAR类型的过程只允许段内调用;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 汇编 子程序 设计
链接地址:https://www.31ppt.com/p-6586366.html