IA-32:过程调用
TITLE Integer Summation Program
INCLUDE Irvine32.inc
INTEGER_COUNT = 3 ; 定义常量:整数个数为3
.data
str1 BYTE "Enter a signed integer:",0
str2 BYTE "The sum of the integers is:",0
array DWORD INTEGER_COUNT DUP(?) ; 定义DWORD类型数组,预留3个整数的存储空间
.code
main PROC
call Clrscr ; 清空控制台屏幕
mov esi,OFFSET array ; ESI指向数组首地址(数组操作常用ESI作为源变址寄存器)
mov ecx,INTEGER_COUNT ; ECX赋值为3(LOOP指令的循环计数器)
call PromptForIntegers ; 调用子程序:提示并读取用户输入的整数
call ArraySum ; 调用子程序:计算数组元素和(结果存EAX)
call DisplaySum ; 调用子程序:显示求和结果
exit ; 程序退出(Irvine32库的退出指令)
main ENDP
;--------------------------------------------
PromptForIntegers PROC USES ecx edx esi
mov edx,OFFSET STR1 ; EDX指向输入提示字符串(WriteString要求字符串地址存EDX)
L1:
call WriteString ; 输出提示字符串
call ReadInt ; 读取用户输入的有符号整数(结果存EAX)
call Crlf ; 换行
mov [esi],eax ; 将输入的整数存入数组当前位置
add esi,TYPE DWORD ; ESI偏移4字节(DWORD占4字节),指向下一个数组元素
loop L1
RET
PromptForIntegers ENDP
;---------------------------------------------
ArraySum PROC USES esi ecx
mov eax,0
L1:
add eax,[esi]
add esi,TYPE DWORD
loop L1
ret
ArraySum ENDP
;------------------------------------------------
DisplaySum PROC USES edx
mov edx,OFFSET STR2
call WriteString
call WriteInt
call Crlf
ret
DisplaySum ENDP
END main
关键知识点解析:
TYPE指令
在 x86 汇编(尤其是 MASM/TASM/Irvine32 环境)中,TYPE 是一个汇编器伪指令(运算符),用于返回单个数据类型 / 变量 占用的字节数,核心作用是简化内存偏移计算、提高代码可读性。
一、TYPE 核心定义
TYPE 的计算规则:返回指定标识符(数据类型 / 变量 / 数组元素)的基本存储单元字节数,本质是汇编器在编译阶段计算的常量值(非运行时计算)。
语法格式
TYPE 标识符 ; 标识符可以是:数据类型(如DWORD、BYTE)、变量、数组名、数组元素
二、TYPE DWORD 具体含义
DWORD(Double Word,双字)是 x86 汇编中最常用的 32 位数据类型,固定占用 4 字节,因此:
TYPE DWORD ; 汇编器直接解析为常量 4
在你的代码中:
add esi,TYPE DWORD ; 等价于 add esi,4
作用是让 ESI 寄存器(数组指针)偏移 4 字节,指向数组的下一个 DWORD 类型元素(因为每个数组元素是 4 字节)。
三、TYPE 对不同数据类型的返回值
| 数据类型 | 含义 | TYPE 返回值(字节) |
|---|---|---|
| BYTE | 字节(8 位) | 1 |
| WORD | 字(16 位) | 2 |
| DWORD | 双字(32 位) | 4 |
| QWORD | 四字(64 位) | 8 |
| FWORD | 远字(48 位) | 6 |
| TBYTE | 十字节(80 位浮点数) | 10 |
| REAL4 | 单精度浮点数 | 4 |
| REAL8 | 双精度浮点数 | 8 |
| REAL10 | 扩展精度浮点数 | 10 |
四、TYPE 对变量 / 数组的使用场景
1. 对普通变量使用
如果定义变量:
var1 BYTE 10h ; 字节变量
var2 WORD 2000h ; 字变量
var3 DWORD 30000000h ; 双字变量
则:
TYPE var1 ; 返回 1(BYTE占1字节)
TYPE var2 ; 返回 2(WORD占2字节)
TYPE var3 ; 返回 4(DWORD占4字节)
2. 对数组使用
数组的本质是 “相同类型数据的连续存储”,TYPE 对数组名的返回值 = 数组单个元素的字节数:
array DWORD 3 DUP(?) ; 定义3个DWORD元素的数组
TYPE array ; 返回 4(单个DWORD元素占4字节)
这也是你的代码中 add esi,TYPE array 等价于 add esi,4 的原因(和 TYPE DWORD 效果一致)。
USES指令
一、USES 指令核心定义
USES 是 MASM/TASM(Microsoft/Turbo Assembler) 专有的伪指令(非 x86 硬件指令),用于简化 IA-32 汇编过程中非易失性寄存器的保存与恢复,本质是编译器级别的语法糖,无需手动编写 push/pop 指令。
❗ 重要限制:
- 仅支持 MASM/TASM,NASM/GAS 无此伪指令;
- 仅能在
PROC定义行使用(PROC与USES之间空格分隔);- 作用域仅限当前过程,过程结束时自动恢复寄存器。
二、USES 语法格式
过程名 PROC [NEAR/FAR] USES 寄存器1 寄存器2 ... 寄存器n
; 过程体
过程名 ENDP
NEAR/FAR:可选,指定过程调用类型(IA-32 平展内存模型下默认NEAR);- 寄存器列表:空格分隔,支持 IA-32 通用寄存器(
eax/ebx/ecx/edx/esi/edi/ebp/ebx等); - 无逗号分隔(常见错误:
USES ecx,edx❌ →USES ecx edx✔️)。
三、USES 底层实现原理
USES 会在过程入口处自动插入 push 指令,在过程返回前自动插入 pop 指令,且 pop 顺序与 push 相反(栈 “后进先出” 特性)。
示例 1:基础用法
MyProc PROC USES ecx edx esi
; 过程体
MyProc ENDP
等价于手动编写:
MyProc PROC
; 入口处自动插入(保存寄存器)
push ecx
push edx
push esi
; 过程体(你的代码)
; 返回前自动插入(恢复寄存器)
pop esi
pop edx
pop ecx
ret
MyProc ENDP
示例 2:带栈帧的完整等价
MyProc PROC USES ecx edx esi
push ebp
mov ebp, esp
; 过程体
pop ebp
ret
MyProc ENDP
等价于:
MyProc PROC
; USES 自动插入(先保存寄存器)
push ecx
push edx
push esi
; 手动栈帧
push ebp
mov ebp, esp
; 过程体
; 手动销毁栈帧
pop ebp
; USES 自动插入(后恢复寄存器)
pop esi
pop edx
pop ecx
ret
MyProc ENDP
四、USES 核心使用规则
1. 寄存器选择原则
| 寄存器类型 | 是否需要用 USES 保存 | 原因(IA-32 调用约定,如 CDECL/stdcall) |
|---|---|---|
| 易失性寄存器 | 可选(通常无需) | 调用者不依赖其值(eax/ecx/edx) |
| 非易失性寄存器 | 必须(推荐用 USES) | 调用者期望值不变(esi/edi/ebx/ebp) |
示例:若过程中修改
esi(非易失性),必须保存 →USES esi;若仅修改eax(易失性),无需USES。
2. 禁止操作的寄存器
ebp:若过程中手动建立栈帧(push ebp; mov ebp, esp),不要放入USES(会导致栈帧错乱);esp:栈指针寄存器,USES不支持,也绝对不能手动push esp(破坏栈结构)。
3. 与 LOOP 指令的兼容
LOOP 指令依赖 ecx 作为计数器,若过程中使用 LOOP 且 ecx 需保存,USES ecx 仍有效(push ecx 会保存初始值,pop ecx 恢复):
asm
CountProc PROC USES ecx
mov ecx, 10 ; 初始化计数器
LoopStart:
; 循环体
loop LoopStart ; ecx--,直到0退出
ret
CountProc ENDP
→ 过程结束后 ecx 会恢复为调用前的值(而非 0)。

浙公网安备 33010602011771号