【问题标题】:Pass by value and pass by reference in Assembly在 Assembly 中按值传递和按引用传递
【发布时间】:2016-03-29 19:43:51
【问题描述】:

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

创建一个PROC 过程,该过程将一个参数作为按值传递,并根据作为参数传递的数量打印“X”的数量。 在打印之前,请确保参数为正数,在程序结束时您需要将已使用的寄存器更改回它们的初始值

如果程序的输入是 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  

这段代码会按预期工作吗?如果没有,为什么?怎么解决?

【问题讨论】:

  • 没有PUSH r/m8,所以你应该使用dw(或dd)声明你的var。您还应该使用push word ptr [var](或dword ptr)明确说明要推送多少字节。
  • 是否需要在栈上传递参数?

标签: assembly parameters dos masm x86-16


【解决方案1】:

作为彼得答案的补充,您可以使用 MASM/TASM 生成序言和尾声代码来为您设置 BP,并允许您按标签访问过程/函数参数。可以找到关于 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

我把它作为一个练习留给读者来确定example PROC 更改的所有寄存器,并按照家庭作业中的要求保存/恢复它们。提示:在 ARG 指令之后 PUSH 并在 RET 之前以相反的顺序 POP 它们

【讨论】:

    【解决方案2】:

    不,您的代码已严重损坏。

    函数使用mov ecx, [esp+4] 之类的东西从堆栈中访问它们的参数。或者在 16 位代码中,[sp+2] 不是可编码的寻址模式,因此您应该创建一个堆栈帧:
    push bp / mov bp, sp / mov cx, [bp+4]。 (并在函数结束时撤消此操作)。谷歌“堆栈框架”了解更多信息,以及 x86 函数如何访问堆栈上的参数。 (或查看 标签维基)。

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


    您的函数以ret 结尾,这意味着您假设您被调用时使用了call。返回地址将在函数入口处的[sp](或[esp] 32 位代码)。此时的pop 将加载返回地址。如果在返回地址低于sp时出现中断,会被覆盖,所以pop不安全,然后在ret之前再次调整sp

    推入一个 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 有一些指向教程和参考资料的链接。有了那个 + 调试器 + google,您应该能够回答自己的问题。

    【讨论】:

    • OP 使用 16 位代码。您的某些答案可能会让人感到困惑,因为它讨论的是 32 位代码。即:[esp]mov ecx, [esp+4]。 OP 代码中的语法也是 MASM/TASM。
    • @MichaelPetch:我不想写[sp+4](或[sp+2]?),因为那不是合法有效的地址。我不想花更多的时间给出更好的答案,或者喋喋不休地谈论 16 位是多么愚蠢。我是否写了任何无效的 MASM 语法?不使用.local_labels吗?
    • 对于语法,您可以在 MASM/TASM 标签的开头添加句点,但它不被视为本地。您需要将LOCALS 指令添加到汇编程序文件的顶部,然后在标签前面加上@@
    • @MichaelPetch:我从来没有说过这个问题对 SO 来说是题外话,只是我对它的那一部分不感兴趣。不过,它迫切需要编辑,我确实想留下一些关于弹出访问 args 的回复。
    • @MichaelPetch:我更新了我的答案,因为你是对的,所以让低质量的答案坐在那里是不好的。
    猜你喜欢
    • 2014-08-23
    • 2015-05-23
    • 2012-07-29
    • 2014-04-04
    • 1970-01-01
    • 2011-05-29
    • 2012-01-16
    • 2012-10-14
    • 1970-01-01
    相关资源
    最近更新 更多