Assembly中按值传递和按引用传递

Pass by value and pass by reference in Assembly

我正在尝试解决这个问题:

创建一个PROC过程,将一个参数作为按值传递,并根据作为参数传递的数字。 打印前,请确保参数为正数,在程序结束时,您需要将已使用的寄存器更改回其最初的值

如果程序的输入是 5,那么控制台上的输出应该是:

XXXXX

这是我的代码:

var db 5 ;In the dataseg  
push [var] ;in the codeseg  

proc example  
    pop cx  
    cmp cx,0  
    ja print  
print:  
    mov dl, 'X'  
    mov ah, 2h  
    int 21h  
    loop print  
    ret  
endp example  

这段代码能按预期工作吗?如果不能,为什么?如何修复?

不,您的代码严重损坏。

函数使用 mov ecx, [esp+4] 之类的东西从堆栈访问它们的参数。或者在 16 位代码中,[sp+2] 不是可编码的寻址模式,所以你应该制作一个堆栈帧:
push bp / mov bp, sp / mov cx, [bp+4]。 (并在函数末尾撤消此操作)。 Google "stack frame" 了解更多信息,以及 x86 函数如何访问堆栈上的参数。 (或查看 标签 wiki)。

这是假设您需要在堆栈上传递参数。一些调用约定在寄存器中传递前几个参数,这尤其节省了指令。在小函数中。


你的函数以 ret 结尾,这意味着你假设你是用 call 调用的。 return 地址将在函数入口处位于 [sp](或 32 位代码中的 [esp])。此时的 pop 将加载 return 地址。如果return地址低于sp时有中断进来,它将被覆盖,所以pop不安全,然后在[=之前再次调整sp回来16=].

推送一个 arg 然后进入一个函数不是好的做法。


你的分支也错了:

    cmp cx, 0
    ja print  
print:         ; execution ends up here whether the branch is taken or not

使用 ja 意味着您将 arg 视为未签名。这很好,因为它仍然有可能因为为零而成为非正数。但是,我认为该作业是要您将 arg 视为已签名。试试像 jle nonpositive 这样的东西,然后把 nonpositive: 放在有用的地方。


标签 wiki 有一些指向教程和参考的链接 material。有了它 + 一个调试器 + google,你应该能够回答你自己的问题。

作为 Peter 回答的补充,您可以使用 MASM/TASM 生成序言和结尾代码来为您设置 BP,并允许您访问 procedure/function 标签参数。可以找到关于 MASM 和 TASM 使用的 PROC 子例程的相当不错的教程 here

我还将 var 更改为 WORD 而不是 BYTE。生成的代码如下所示:

.MODEL SMALL
.STACK 100H

.DATA
    var dw 5           ; Change to 16-bit WORD

.CODE

example proc C         ; C Calling convention - parameters on stack right to left
    ARG num:WORD       ; We take one argument called `num` that is a word

    mov cx, num        ; Move the 16-bit value in `num` to CX counter
                       ;     same as: mov cx, [bp+4]
                       ;     [bp+0] is saved copy of BP put on stack by MASM's prologue
                       ;     [bp+2] return address placed on stack by CALL
    cmp cx, 0
    jle negative       ; If we are less than or equal to 0, exit procedure
    mov dl, 'X'
    mov ah, 2h         ; ah and dl not destroyed by int 21h/ah=2 so set them once
                       ;     before loop
print:
    int 21h            ; Print an 'X'
    loop print         ; Continue until loop is 0
negative:
    ret
endp example

main proc
     mov ax, @data     ; initialize DS
     mov ds, ax

     push [var]        ; Push 2-byte value at `var` (pushing by value)
     call example
     add sp, 2         ; Remove parameter from stack
                       ;     Not necessary since we use int 21h to exit right after
     mov ah, 4ch       ; return control to DOS
     int 21h
main endp

end main               ; Entry point = label main

上面的代码会为过程 example:

生成这些指令
example proc
    push bp            ; Save BP on stack  \
    mov bp, sp         ; Set BP to SP      / Function prologue
                       ;     [bp+0] is saved copy of BP put on stack by prologue
                       ;     [bp+2] return address placed on stack by CALL
                       ;     [bp+4] first parameter (NUM)

    mov cx, [bp+4]     ; Move the value at BP+4 (NUM) to CX counter

    cmp cx, 0
    jle negative       ; If we are less than or equal to 0, exit procedure
    mov dl, 'X'
    mov ah, 2h         ; ah and dl not destroyed by int 21h/ah=2 so set them once
                       ;     before loop
print:
    int 21h            ; Print an 'X'
    loop print         ; Continue until loop is 0
negative:
    mov sp, bp         ; Restore stack pointer \
    pop bp             ; Restore BP register   / Function epilogue
    ret
endp example

我把它留作 reader 的练习以确定 example PROC 更改的所有寄存器,并且 saves/restores 它们作为在家庭作业中要求。提示:PUSH 它们在 ARG 指令之后 POP 它们在 [=] 之前以相反的顺序15=]