Emu8086中栈传递参数位置判断错误,如何正确访问参数?
问题分析与修正:Emu8086中栈参数的正确访问
你遇到的核心问题是对16位x86栈的生长方向和调用过程时的栈布局理解有误,导致参数偏移计算错误。我们先理清栈的实际结构,再给出正确的参数访问方式,最后结合你的需求补充代码示例。
一、调用filter时的栈布局详解
在16位模式下,栈是向下生长的(SP值越小,指向栈顶)。我们一步步拆解你的调用代码对应的栈状态:
你的调用代码:
push WORD PTR [reference+2] ; reference的高字(因为小端序,dd的高字节在[addr+2]和[addr+3]) push WORD PTR [reference] ; reference的低字 push offset address ; address数组的偏移地址 push offset mask ; mask的偏移地址 sub sp, 2 ; 预留2字节的返回值空间 call filter ; 压入返回地址(2字节),跳转到filter
进入filter过程后,你执行了push bp和mov bp, sp,此时栈的布局(以bp为基准,数值越大越靠近栈底)如下:
bp+0:旧的BP值(push bp压入)bp+2:返回地址(call指令自动压入的IP值)bp+4:预留的返回值存储位置(你用sub sp,2提前分配的空间)bp+6:mask的偏移地址(最后一个push的参数)bp+8:address数组的偏移地址(倒数第二个push的参数)bp+10:reference的低字(第三个push的参数)bp+12:reference的高字(第一个push的参数)
你之前的偏移假设把参数顺序搞反了(因为先push的参数在栈底,偏移更大),同时reference的高低字位置也颠倒了,这就是访问出错的原因。
二、正确的参数访问方式
在filter过程中,你可以通过以下方式获取各个参数:
proc filter push bp mov bp, sp ; 保存会被修改的寄存器(调用者保存寄存器,避免破坏上下文) push si push di push cx push dx ; 1. 获取mask的偏移地址 mov di, [bp+6] ; di = offset mask ; 2. 获取address数组的偏移地址 mov si, [bp+8] ; si = offset address ; 3. 获取reference的完整32位值(低字+高字) mov cx, [bp+10] ; cx = reference低字 mov dx, [bp+12] ; dx = reference高字 ; 4. 返回值的存储位置是[bp+4],最后把结果写入这里 ; 你的业务逻辑代码... ; 恢复寄存器 pop dx pop cx pop di pop si pop bp ret endp
三、业务逻辑实现示例(统计有效地址数量)
你的需求是检查address数组中的每个元素,当mask为1的位与reference完全匹配时,计数加1。核心逻辑是:(address_element XOR reference) AND mask == 0(如果结果为0,说明所有mask为1的位都匹配)。
以下是完整的filter实现示例:
proc filter push bp mov bp, sp push si push di push cx push dx mov ax, 0 ; 初始化有效计数器为0 ; 获取参数 mov di, [bp+6] ; di = offset mask mov si, [bp+8] ; si = offset address mov cx, [bp+10] ; cx = reference低字 mov dx, [bp+12] ; dx = reference高字 ; 检查第一个address元素(dd类型,占4字节,低字在[si],高字在[si+2]) push ax ; 暂存计数器 mov ax, [si] ; ax = address[0]低字 mov bx, [si+2] ; bx = address[0]高字 xor ax, cx ; 低字异或reference低字 xor bx, dx ; 高字异或reference高字 and ax, [di] ; 与mask低字做AND and bx, [di+2] ; 与mask高字做AND or ax, bx ; 组合高低字结果 jz count_up_0 ; 结果为0,说明有效 pop ax jmp check_second count_up_0: pop ax inc ax push ax ; 检查第二个address元素 check_second: mov ax, [si+4] ; ax = address[1]低字 mov bx, [si+6] ; bx = address[1]高字 xor ax, cx xor bx, dx and ax, [di] and bx, [di+2] or ax, bx jz count_up_1 pop ax jmp end_check count_up_1: pop ax inc ax ; 将结果写入返回值位置 end_check: mov [bp+4], ax ; 恢复寄存器 pop dx pop cx pop di pop si pop bp ret endp
四、额外注意事项
- 寄存器保存:在过程中如果使用了
si、di、bx等通用寄存器,一定要先push保存,退出前pop恢复——这是16位x86调用约定的基本要求,避免破坏调用者的寄存器状态。 - 小端序处理:你对
reference的push顺序是正确的(先高字后低字),这样栈中组合起来的32位值与dd类型的存储顺序一致。 - 数组遍历:如果
address数组长度可变,建议额外传递长度参数,这样代码更通用。
内容的提问来源于stack exchange,提问作者simondvt
相关产品推荐
相关产品推荐

