一、子程序结构
1.子程序设计
- 子程序框架
//子程序框架
子程序名 proc;子程序开始
push ...1;保护寄存器
push ...2
...;子程序体
pop ...2;恢复寄存器
pop ...1
ret;子程序返回
子程序名 endp;子程序结束
RET指令返回主程序,CALL指令调用子程序
利用过程定义,获得子程序名和调用属性
压入和弹出操作要成对使用,保持堆栈平衡
子程序开始保护寄存器,返回前相应恢复
安排在代码段的主程序之外
子程序允许嵌套和递归
子程序返回指令RET
;ret指令的功能:
1.弹出返回地址;从当前堆栈顶部弹出内容作为返回地址
2.转移到返回地址
ret ;无参数返回:出栈返回地址
ret i16;有参数返回:出栈返回地址,ESP<-ESP+i16
ret;栈顶数据出栈到指令寄存器EIP
;EIP=SS:[ESP],ESP=ESP+4
;数据进入EIP,就作为下条要执行指令的地址
- 子程序调用
//子程序调用
call name
;call 指令的功能:
1.入栈返回地址;用在主程序中,实现子程序的调用,将下条指令的地址压入堆栈(顶部)
2.转移到目标地址
call label;调用标号制定的子程序
call reg32/reg16;调用寄存器指定地址的子程序
call mem48/mem32/mem16;调用存储单元指定地址的子程序
call label
next:...
;相当于
push next;入栈返回地址,ESP=ESP-4,SS:[ESP]=EIP(EIP->next)
jmp label;转移目标地址:EIP=EIP+偏移量
2.参数传递
入口参数:
主程序 —> 子程序
出口参数:
子程序 —>主程序
参数的具体内容:
数据本身(传递数值)
数据的存储地址(传递地址,传递引用)
参数传递的方式
- 通用寄存器
- 共享变量
- 堆栈
寄存器传递参数
最简单和常用的参数传递方法;
把参数存于约定的寄存器;
►少量数据直接传递数值
►大量数据只能传递地址
带有出口参数的寄存器不能保护和恢复;
带有入口参数的寄存器可以保护、也可以不保护,但最好能够保持一致
共享变量传递参数
子程序和主程序使用同一个变量名存取数据;
如果变量定义和使用不在同一个程序模块中,需要利用PUBLIC、EXTREN声明;
共享变量传递参数,子程序的通用性较差;
特别适合在多个程序段间、尤其在不同的程序模块间传递数据;
共享变量对应高级语言的全局变量
堆栈传递参数
主程序将入口参数压入堆栈
►子程序从堆栈中取出参数
采用堆栈传递参数常是程式化的
►子程序设置EBP等于当前ESP
►利用EBP相对寻址访问堆栈中的参数
出口参数通常不使用堆栈传递
高级语言函数调用的参数实质是堆栈传递参数
二、多模块程序结构
1.程序模块
MASM支持的多模块方法:1.源文件包含;2.模块连接;
3.子程序库;4.库文件包含
- 源文件包含
大型源程序可以合理地分放在若干个文本文件中
1.各种常量定义、声明语句等组织在包含文件(*.INC)
2.常用的或有价值的宏定义存放在宏定义文件(*.MAC)
3.常用的子程序形成汇编语言源文件(.ASM)
4.任何文本文件
使用源文件包含微指令inlcude
include 文件名
;将指定文件内容插入主体源程序
示例:
;文件名:eg0508.inc,程序的数据段内容
.data
...
;文件名:eg0508s.asm,子程序
rdhd proc;十六进制输入子程序
...
dphd proc;十六进制输出子程序
...
write proc;显示有符号十进制数字
...
;文件名: eg0508.asm,主程序
include io32.inc;包含软件I/O库声明
inclue eg0508.inc;包含数据段
.code;代码段,主程序
...
exit 0;主程序结束
include eg0508s.asm;包含子程序代码
end start
源文件包含的使用
被包含文件:
1.文件名要符合操作系统规范
2.只能是文本文件
3.内容被插入源文件包含include语句所在的位置
实质仍然是一个源程序:
1.只是分开在若干文件中
2.只需针对主体源程序文件进行汇编、连接
- 模块连接
子程序单独编写一个源程序文件:
使用共同伪指令PUBLIC和外部伪指令EXTERN声明
子程序在代码段,与主程序文件采用相同的存储模型
没有开始执行和结束执行点,但有汇编结束语句
处理好子程序与主程序之间的参数传递问题
;定义标识符的模块使用
public 标识符 [,标识符 …]
;调用标识符的模块使用
extern 标识符:类型 [,标识符:类型 …]
示例
;文件名:eg0508s.asm,子程序
public rdhd,dphd,write;子程序共用
extern temp:dword;外部变量
rdhd proc;十六进制输入子程序
...
end;汇编结束
模块连接的操作过程:
子程序单独编写一个源程序文件
子程序源文件汇编形成目标模块OBJ文件
连接时输入子程序目标模块文件名
- 子程序库
子程序库是子程序模块的集合,便于统一管理子程序
编写存入库文件的子程序:
1.遵循更加严格的子程序模块要求
2.应该遵循一致的规则(以免使用时造成混乱)
子程序库的使用
子程序单独编写一个源程序文件
子程序源文件汇编形成目标模块OBJ文件
利用库管理工具程序把子程序模块加入到子程序库
在连接主程序时提供子程序库文件名
- 库文件包含
要使用已存入库文件中的子程序
在主程序源文件中用库文件包含伪指令includelib的声明
使用库文件包含伪指令includelib
includelib 文件名
;使用库文件中的子程序
库文件包含的使用
将子程序源文件汇编、模块文件加入子程序库
源文件中用库文件包含伪指令includelib声明
正常对主程序汇编、连接,无需在连接时输入库文件名:
编写主程序、子程序更加独立
子程序使用更方便
2.宏汇编
;宏定义
宏名 macro [形参表]
...;宏定义体
endm
;宏定义
WriteString macro msg
push eax
lea eax,msg
call dispmsg
pop eax
endm
;宏调用(宏指令)
WriteString msg
;宏展开
push eax
lea eax,msg
call dispmsg
pop eax
宏汇编的特点
1.宏需要先定义后使用,且不必在任何段中:常常书写于源程序开始位置;常用的宏定义可以单独写成一个宏定义文件
2.宏定义中更改了寄存器内容,最好进行保护和恢复
3.宏定义的参数灵活,允许嵌套和递归调用
4.宏调用不需要控制的转移与返回:宏调用将相应的语句序列复制到宏指令的位置;宏展开将嵌入源程序,成为一体
宏与子程序的对比
宏 | 子程序 | |
---|---|---|
简化程序 | 宏仅是源程序级的简化:宏调用在汇编时进行程序语句的展开,不需要返回;不减小目标程序,执行速度没有改变 | 子程序不仅简化源程序,还是目标程序级的简化:子程序调用在执行时由call指令转向、ret指令返回;形成的目标代码较短,执行速度减慢 |
传递参数 | 宏通过形参、实参结合实现参数传递:使用软件方法,简洁直观、灵活多变;传递出错多体现为语法错误,易于发现 | 子程序利用寄存器、存储单元或堆栈等实现:运用硬件本身,规则严格、方法固定;传递出错常反映为逻辑或运行错误,较难排除 |
选用原则 | 当程序段较短或要求较快执行时,选用宏,宏常依附于源程序,适合进行全局性预处理 | 当程序段较长或为减小目标代码时,选用子程序,子程序更具有独立性,可以分别编写 |