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 函数如何访问堆栈上的参数。 (或查看 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:
放在有用的地方。
x86 标签 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=]
我正在尝试解决这个问题:
创建一个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 函数如何访问堆栈上的参数。 (或查看 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:
放在有用的地方。
x86 标签 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=]