You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:58:33