按值传递并在Assembly中通过引用传递

时间:2016-03-29 19:43:51

标签: assembly parameters masm x86-16

我试图解决这个问题:

创建一个 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  

此代码是否按预期工作,如果没有,为什么?如何解决?

2 个答案:

答案 0 :(得分:1)

不,你的代码严重破坏了。

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

这假设您需要在堆栈上传递args。一些调用约定将寄存器中的前几个args传递,这样可以保存指令esp。在小功能。

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

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

答案 1 :(得分: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 指令之后, POP 它们在RET

之前以相反的顺序排列