汇编语言子程序结构
Posted BkbK-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了汇编语言子程序结构相关的知识,希望对你有一定的参考价值。
子程序结构
文章目录
一、子程序指令
(1)子程序调用指令CALL
CALL
指令用在主程序中,实现子程序的调用子程序和主程序可以在同一个代码段内,也可以在不同段内。因而,与无条件转移指令 JMP 类似,子程序调用指令 CALL 也可以分成段内调用(近调用)和段间调用(远调用),同时, CALL指令的目标地址也可以采用相对寻址、直接寻址或间接寻址。
1.子程序调用指令CALL的功能
CALL指令用在主程序中,实现子程序的调用
- 功能1:将下条指令的地址压入堆栈(顶部)
- 功能2:转移到目标地址
CALL 指令实际上就是入栈指令 PUSH 和转移指令 JMP 的组合
2.子程序调用指令CALL的使用方法
-
调用标号指定的子程序
CALL label ;调用标号指定的子程序
-
调用寄存器指定地址的子程序
CALL reg32/reg16 ;调用寄存器指定地址的子程序
-
调用存储单元指定地址的子程序
CALL mem48/mem32/mem16 ;调用存储单元指定地址的子程序
3.子程序调用指令CALL的分类
CALL分成段内调用(近调用)和段间调用(远调用)目标地址支持相对寻址、直接寻址或间接寻址
(2)子程序返回指令RET
RET指令用在子程序结束,实现返回主程序
- 功能1:从当前堆栈顶部弹出内容作为返回地址
- 功能2:转移到返回地址
RET
无参数返回:出栈返回地址
RET i16
有参数返回:出栈返回地址,ESP←ESP+i16
(3)过程定义伪指令
过程名为符合语法的标识符
过程名 proc ;过程定义
... ;过程体
过程名 endp ;过程结束
MASM会根据存储模型等信息确定子程序的远近调用,并相应产生调用、返回指令
-
子程序框架
标识符 proc ;过程定义(子程序开始) push ...1 ;保护寄存器 push ...2 … ;子程序体 pop ...2 ;恢复寄存器 pop ...1 ret ;子程序返回 标识符 endp ;过程(子程序)结束
- RET指令返回主程序,CALL指令调用子程序
- 利用过程定义,获得子程序名和调用属性
- 压入和弹出操作要成对使用,保持堆栈平衡
- 子程序开始保护寄存器,返回前相应恢复
- 安排在代码段的主程序之外
- 子程序允许嵌套和递归
二、参数传递
- 主程序与子程序间通过参数传递建立联系
►入口参数(输入参数):主程序→子程序
►出口参数(输出参数):子程序→主程序- 参数的具体内容
►数据本身(传递数值)
►数据的存储地址(传递地址,传递引用)
(1)寄存器传递参数
- 最简单和常用的参数传递方法
- 把参数存于约定的寄存器
►少量数据直接传递数值
►大量数据只能传递地址 - 带有出口参数的寄存器不能保护和恢复
- 带有入口参数的寄存器可以保护、也可以不保护,但最好能够保持一致
十六进制显示:
INCLUDE io32.inc
.data;数据段
regd byte 'EAX=',8 dup(0),'H',0
.code
start:;代码段,主程序
mov eax,1234abcdh ;假设一个数据
xor ebx,ebx ;相对寻址访问字符串
mov ecx,8 ;8位十六进制数
again: rol eax,4 ;高4位循环移位进入低4位
push eax;也可以用mov edx,eax
call htoasc ;调用子程序htoasc
mov regd+4[ebx],al ;保存转换后的ASCII码
pop eax ;也可以用moveax,edx
inc ebx
loop again
mov eax,offset regd
call dispmsg ;显示
exit 0
;代码段,子程序
htoasc proc
and al,0fh ;只取AL的低4位
or al,30h ;AL高4位变成3
cmp al,39h ;是0~9,还是A~F
jbe htoend
add al,7 ;是A~F,ASCII码再加上7
htoend: ret ;子程序返回
htoasc endp
end start
(2)共享变量传递参数
共享变量对应高级语言的全局变量
- 子程序和主程序使用同一个变量名存取数据
- 如果变量定义和使用不在同一个程序模块中,需要利用
PUBLIC
、EXTREN
声明 - 共享变量传递参数,子程序的通用性较差
- 特别适合在多个程序段间、尤其在不同的程序模块间传递数据
二进制输入子程序:
;代码段,子程序:输入32位二进制数
rdbd proc ;出口参数:共享变量TEMP
push eax ;寄存器保护
push ebx
push ecx
rdbd1: xor ebx,ebx ;EBX用于存放二进制结果
mov ecx,32 ;限制输入字符的个数
rdbd2: call readc ;输入一个字符
cmp al,'0' ;检测键入字符是否合法
jb rderr ;不合法则转到出错处理
cmp al,'1'
ja rderr
sub al,'0' ;对输入的字符进行转化
shl ebx,1 ;EBX的值乘以2(左移1位)
or bl,al ;BL和AL相加(或)
loop rdbd2 ;循环输入字符
mov temp,ebx ;把二进制结果存放TEMP返回
call dispcrlf ;分行
pop ecx
pop ebx
pop eax
ret
rderr: mov eax,offset errmsg ;显示错误信息
call dispmsg
jmp rdbd1 ;重新输入
errmsg byte 0dh,0ah,'Input error, enter again: ',0
rdbd endp
(3)堆栈传递参数
- 主程序将入口参数压入堆栈
►子程序从堆栈中取出参数 - 采用堆栈传递参数常是程式化的
►子程序设置EBP等于当前ESP
►利用EBP相对寻址访问堆栈中的参数 - 出口参数通常不使用堆栈传递
采用堆栈传递参数可以程式化
• 子程序设置EBP等于当前ESP
• 利用EBP相对寻址访问堆栈中的参数
计算有符号数平均值子程序:
INCLUDE io32.inc
.data
;数据段
array dword 675,354,-34,198,267,0,9,2371,-67,4257
.code
;代码段,主程序
push lengthof array ;压入数据个数(4个字节)
push offset array ;压入数组地址(4个字节)
call mean ;调用求平均值子程序
add esp,8 ;主程序平衡堆栈(弹出8个字节)
call dispsid ;显示平均值EAX(出口参数)
;代码段,子程序:计算32位有符号数平均值
mean proc ;入口参数:顺序压入个数和地址
push ebp ;出口参数:EAX=平均值
mov ebp,esp
push ebx ;保护寄存器
push ecx
push edx
mov ebx,[ebp+8] ;EBX=取出的数组地址
mov ecx,[ebp+12] ;ECX=取出的数据个数
xor eax,eax ;EAX保存和值
xor edx,edx ;EDX=指向数组元素
mean1: add eax,[ebx+edx*4] ;求和
add edx,1 ;指向下一个数据
cmp edx,ecx ;比较个数
jb mean1 ;增量计数循环
cdq ;将累加和EAX符号扩展到EDX
;将EAX最高位填入EDX所有位
idiv ecx ;有符号数除法,EAX=平均值
pop edx ;恢复寄存器
pop ecx
pop ebx
pop ebp
ret 8;子程序平衡堆栈
mean endp
以上是关于汇编语言子程序结构的主要内容,如果未能解决你的问题,请参考以下文章