学习汇编记录Day5——子程序和库

以截图的方式记录了。

目录

1,子程序的定义

2,调用和返回指令

3,子程序的参数传递

4,寄存器的保护与恢复

5,子程序的完全定义

6,子程序库


了解:引入子程序的作用,制作用户子程序库的方法。

掌握:在子程序中,保护和恢复寄存器的原因和方法。

熟练:子程序定义的一般格式,

子程序的调用和返回指令,

调用子程序常用的参数传递方法,

模块间通信的方法。

 

1,子程序的定义

定义:某程序段在源程序中重复出现,即称该程序段为子程序。

作用:缩短源程序长度、节省目标程序的存储空间,提高程序的可维护性和共享性。

格式:

学习汇编记录Day5——子程序和库

规定:

1)“子程序名”必须合法,前后一致。

2)proc和endp必须是成对出现的关键字,分别表示定义的开始和结束。

3)子程序的类型,有near和far之分,缺省的类型是near。

4)如果一个子程序要被另一段程序调用,那么,其类型定义为far,否则其类型定义为near。

显然,near类型的子程序只能被与其同段的程序调用。

5)子程序至少要有一条返回指令。返回指令时子程序的出口语句,但不一定子程序的最后一条语句。

6)子程序名有3个属性:段值,偏移量和类型。段值和偏移量就是子程序的入口地址,类型就是子程序的类型。

 

2,调用和返回指令

这是一种特殊的转移操作,”有去有回“的转移。

调用指令  call   

格式:      call  子程序名/reg/mem

两种调用方式:near ,  far。

near方式:如果被调用子程序的属性是近的,那么,CALL指令将产生一个近调用,它把该指令之后地址的偏移量(用一个字来表示的)压栈,把被调用子程序入口地址的偏移量送给指令指针寄存器IP即可实现执行程序的转移。

far方式:如果被调用子程序的属性是远的,那么,CALL指令将产生一个远调用。这时,调用指令不仅要把该指令之后地址的偏移量压进栈,而且也要把段寄存器CS的值压进栈。在此之后,再把被调用子程序入口地址的偏移量和段值分别送给IPCS,这样完成了子程序的远调用操作。

子程序调用指令本身的执行不影响任何标志位,但子程序体中指令的执行会改变标志位,所以,如果希望子程序的执行不能改变调用指令前后的标志位,那么,就要在子程序的开始处保护标志位,在子程序的返回前恢复标志位。

返回指令   ret

格式:      ret/retn/retf  [imm]

学习汇编记录Day5——子程序和库

学习汇编记录Day5——子程序和库

3,子程序的参数传递

概念:当要给程序调用一个子程序时,通常都向子程序传递若干个数据让它来处理,当子程序处理完后,一般也向调用它的程序传递处理结果。这种在调用程序和子程序之间的信息传递叫参数传递。

常用的传递方法有3种:寄存器传递参数,约定存储单元传递参数,堆栈传递参数。

1)寄存器传递参数

一方面,由于CPU中的寄存器在任何程序中都是“可见”的,一个程序对某寄存器赋值后,在另一个程序中就能直接使用,所以,用寄存器来传递参数最直接、简便,也是最常用的参数传递方式。但另一方面,CPU中寄存器的个数和容量都是非常有限,所以,该方法适用于传递较少的参数信息。

 

学习汇编记录Day5——子程序和库

2)约定存储单元传递参数

在调用子程序时,当需要向子程序传递大量数据时,因受到寄存器容量的限制,就不能采用寄存器传递参数的方式,而要改用约定存储单元的传送方式。这种参数传递方式有点象情报人员和联络人员之间的传递信息方式,一个向指定地点放情报,另一个从指定地点取情报。

学习汇编记录Day5——子程序和库

学习汇编记录Day5——子程序和库

3)堆栈传递参数

堆栈是一个特殊的数据结构,它通常是用来保存程序的返回地址。当用它来传递参数时,势必会造成数据和返回地址混合在一起的局面,用起来要特别仔细。

(1)、当用堆栈传递入口参数时,要在调用子程序前把有关参数依次压栈,子程序从堆栈中取到入口参数;

(2)、当用堆栈传递出口参数时,要在子程序返回前,把有关参数依次压栈(这里还需要做点额外操作,要保证返回地址一定在栈顶),调用程序就可以从堆栈中取到出口参数。

            在通常情况下,我们用堆栈传入口参数,用寄存器传出口参数。

学习汇编记录Day5——子程序和库

学习汇编记录Day5——子程序和库

4,寄存器的保护与恢复

由于计算机的硬件资源只有一套,当子程序修改了寄存器的内容后,返回到调用它的程序时,这些寄存器的内容也就不会是调用子程序前的内容。这样,子程序修改寄存器内容就可能变成了调用它的副作用,这种副作用常常会导致调用程序的出错。为此,在编写子程序时,除了能对作为入口和出口参数的寄存器进行修改外,对其它寄存器的修改对调用程序来说都要是透明的,也就是说,在调用子程序指令的前后,除了作为入口和出口参数的寄存器内容可以不同外,其它寄存器的内容要保持不变。有时,也要求作为入口参数的寄存器内容保持不变。

在子程序中,保存和恢复寄存器内容的主要方法是:在子程序的开始把它所用到的寄存器压进栈,在返回前,再把它们弹出栈。这样编写的好处是该子程序可以被任何其它程序来调用。在调用指令前,不需要保存寄存器,在调用指令后,也无需恢复寄存器。

学习汇编记录Day5——子程序和库

5,子程序的完全定义

学习汇编记录Day5——子程序和库

定义子程序时,可使用参数表来直接指明其所要的参数,但程序员必须先用.MODEL伪指令,或使用<langtype>参数来说明本子程序所使用的程序设计语言类型。

程序员在定义子程序时,最好能象在高级语言(如:C/C++)定义过程那样,先说明该子程序的原型(用伪指令PROTO),这样,在调用时,系统可以自动进行类型检查,也可以使用更方便的调用伪指令INVOKE来调用该子程序。有关子程序的原型说明伪指令和调用伪指令在随后第7.5.87.5.9小节中加以介绍。

子程序通常用RET指令来结束其执行,也可用指令“RET n”来指明在结束子程序执行后从堆栈弹出n个字节。有关返回指令请参阅7.2.2节中的叙述。

汇编程序在处理子程序时能自动产生“起始”代码(PROLOGUE Code)和“结束”代码(EPILOGUE code)。这两段特殊的代码分别完成:在调用子程序时,能把传递给子程序的参数压栈,在子程序结束时能把先前压栈的参数弹出。有了这两段代码,程序员在调用子程序时就不用自行考虑子程序的参数传递问题。

若子程序用指令RETN、RETF或IRETF作为子程序的结束指令,那么,汇编程序将不生成“结束”代码。

程序员可以用自己定义宏来替代缺省的“起始”和“结束”的代码段。这种替代方法是使用伪指令:OPTION PROLOGUE和OPTION EPILOGUE。

若子程序没有参数、局部变量,没使用USES子句,也不会产生新的段或段组,那么,子程序是可以嵌套定义的。程序员也可以使用返回指令RETN和RETF来避免子程序的嵌套。

在子程序内部,可以在指令之前使用伪指令LOCAL来说明其局部变量。

1)子程序的位距

子程序的位距(Distance)有:Near、Far、Near16、Far16、Near32和Far32。

子程序位距描述符告诉汇编程序该子程序是在本段之内(Near),还是在本段之外(Far)。Near和Far描述符表示使用当前的段规模(Segment Size),Near16、Far16、Near32和Far32描述符是告诉汇编程序忽略当前的段规模,而使用指定16位或32位的段规模。

若选用类型Near或Far,那么,汇编程序将根据当前段的规模来决定选用16位,还是32位的Near或Far。

若程序员不指定该选项,那么汇编程序将根据当前的存储模式(由.MODEL来决定)和处理机类型来决定子程序类型。若不使用伪指令.MODEL,那么,Near是缺省的类型。

2)子程序的语言类型

学习汇编记录Day5——子程序和库

4)子程序的可见性

子程序的可见性(Visibility)决定该子程序对其它模块是否可用。它共有三个属性值:PRIVATE、PUBLIC和EXPORT。

PUBLIC属性是子程序标准的缺省属性,但该缺省属性可以用伪指令OPTION PROC来修改。EXPORT属性意味着该子程序是一个“远”的、具有PUBLIC属性的子程序,并要求连接程序在生成可执行文件时把其入口地址放入导出入口地址表中。

 

5)子程序的起始和结束操作

学习汇编记录Day5——子程序和库

6)寄存器的保护和恢复

学习汇编记录Day5——子程序和库

 

学习汇编记录Day5——子程序和库

 

学习汇编记录Day5——子程序和库

 

学习汇编记录Day5——子程序和库

 

6,子程序库

学习汇编记录Day5——子程序和库

学习汇编记录Day5——子程序和库

 

学习汇编记录Day5——子程序和库

 

学习汇编记录Day5——子程序和库

学习汇编记录Day5——子程序和库

学习汇编记录Day5——子程序和库