Assembly 在程序集中按值传递和按引用传递

Assembly 在程序集中按值传递和按引用传递,assembly,parameters,dos,masm,x86-16,x86,Assembly,Parameters,Dos,Masm,X86 16,X86,我试图解决这个问题: 创建一个PROC过程,该过程将一个参数作为传递值,并根据作为参数传递的编号打印“X”的编号打印前,确保参数为正数,在程序结束时,您需要将已使用的寄存器更改回其第一个值 如果程序的输入为5,则控制台上的输出应为: XXXXX 这是我的代码: var db 5 ;In the dataseg push [var] ;in the codeseg proc example pop cx cmp cx,0 ja print prin

我试图解决这个问题:

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

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

不,您的代码严重损坏

函数通过
mov ecx、[esp+4]
之类的东西从堆栈访问其参数。或者在16位代码中,
[sp+2]
不是可编码的寻址模式,因此您应该创建一个堆栈帧:
推送bp
/
mov bp,sp
/
mov cx,[bp+4]
。(并在函数结束时撤消此操作)。Google“stack frame”了解更多信息,以及x86函数如何访问堆栈上的参数。(或查看标记wiki)

这是假设您需要在堆栈上传递参数。一些调用约定在寄存器中传递前两个参数,这将指令保存在小函数中


您的函数以
ret
结尾,这意味着您假设您是通过
调用被调用的。返回地址将位于函数条目上的
[sp]
(或32位代码中的
[esp]
)。此时的
pop
将加载返回地址。如果在返回地址低于
sp
时出现中断,它将被覆盖,因此在
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:
放在有用的地方



TagWiki有一些指向教程和参考资料的链接。有了这个+调试器+谷歌,你应该能够回答你自己的问题。

作为Peter答案的补充,你可以使用MASM/TASM生成序言和尾声代码,为你设置BP,并允许你通过标签访问过程/函数参数。可以找到关于MASM和TASM使用的PROC子程序的相当好的教程

我还将
var
更改为一个单词而不是一个字节。生成的代码如下所示:

.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 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

我把它作为一个练习留给读者,让他们确定
示例
过程中更改的所有寄存器,并按照家庭作业中的要求保存/恢复它们。提示:在ARG指令之后推送它们,并在
RET

之前按相反顺序弹出它们。没有
PUSH r/m8
,因此您应该使用
dw
(或
dd
)声明您的
var
)。您还应该通过使用
push word ptr[var]
(或
dword ptr
)明确要推送多少字节。是否需要在堆栈上传递参数?OP使用的是16位代码。您的一些回答可能会让人困惑,因为它讨论的是32位代码。ie:
[esp]
mov ecx,[esp+4]
。OP代码中的语法也是MASM/TASM。@MichaelPetch:我不想写
[sp+4]
(或
[sp+2]
?),因为那不是合法有效的地址。我不想花更多的时间给出更好的答案,也不想喋喋不休地说16位是多么愚蠢。我是否编写了任何无效的MASM语法?它不使用
.local\u label
s吗?至于语法,您可以在MASM/TASM标签的开头添加句点,但它不被视为local。您需要将
LOCALS
指令添加到汇编文件的顶部,然后在标签前面加上
@
@MichaelPetch:我从来没有说过这个问题是离题的,只是我对其中的那部分不感兴趣。不过,它急需编辑,我确实想留下一些关于弹出访问args的回复。@MichaelPetch:我更新了我的答案,因为你是对的,所以有低质量的答案是不好的。