8086汇编中,如何在子程序中使用堆栈?
In 8086 assembly, how to use stack in a child procedure?
我的任务是设计一个在8086汇编中不会溢出的dword除法子程序(divdw)(我用masm5.0/masm.exe和masm5. 0/link.exe 和 debug.exe(在 MS-DOS 中)。 AX
、CX
、DX
用于保存divdw的结果。下面的代码还不能运行。
这是任务:
输入:
(ax)=low 16 bit of a dword dividend
(dx)=high 16 bit of a dword dividend
(cx)=16 bit divisor
输出:
(ax)=low 16 bit of the result
(dx)=high 16 bit of the result
(cx)=remainder
我用来计算的公式divdw:
dividend/divisor = quot(high 16 bit of dividend / divisor) +
( rem(high 16 bit of dividend / divisor) * 2^16 + low 16 bit of dividend ) / divisor
这是我的代码:
assume cs:code
code segment
start: mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: push bx
push dx ;ss:[0ch]
mov ax,dx
mov dx,0
div cx
mov ax,0 ;rem, only need dx
mov bx,ss:[0ch]
div bx
;; finish 1
push dx
push ax
;; get dx, into ax
mov ax,ss:[0ch]
div cx
;; finish 2
push ax
pop dx ;ax after 2
pop ax ;ax after 1
pop cx ;dx after 1
;; recover bx
pop bx
pop bx
ret
code ends
end start
在这段代码中,我尝试使用 pop
和 push
但没有定义堆栈段。这是允许的吗? (在调试器中我发现给出了 SS:SP
但 push
无法正常工作。)
如果没有,我应该在哪里定义堆栈以及如何使用它?
如果我在主程序中定义一个堆栈段,似乎我需要在程序开始时保存SS
和SP
值,但我应该把它们保存在哪里?我可以将它们保存在堆栈中,还是必须将它们保存在内存中的某个位置?
给出start主程序是为了测试。
谢谢!
编辑:
谢谢你们!在你的帮助下,我完成了这个任务。现在代码如下:
assume cs:code,ss:stack
stack segment
dw 8 dup (0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: push bx
push dx ;ss:[0ah]
push ax ;ss:[08h]
mov ax,dx ;ax=0fh
mov dx,0 ;dx=0
div cx ;ax=1,dx=5
push ax ;1, quot, should be dx when ret, as the high 16 bit of result
;; use dx=5 and 4240h to do div
mov ax,ss:[08h] ;ax=4240h. rem, only need dx of last result, use low 16bit of dividend, ie. ax, as ax
div cx ;ax=86a0h,dx=0h
;ax already is low 16bits of quot
mov cx,dx ;rem, store in cx
pop dx ;1, high 16 bits of quot
pop bx ;discard original ax
pop bx ;discard original dx
pop bx ;recover original bx
ret
code ends
end start
编辑于 20170724
又一个版本,去掉ss:[08h],用另外3个寄存器来存储。我不确定这是否更好,但它确实有效。
assume cs:code,ss:stack
stack segment
db 16 dup (0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: push bp
mov bp,sp
push si
push bx
push dx
push ax
mov si,sp
mov ax,dx ;ax=0fh
mov dx,0 ;dx=0
div cx ;ax=1,dx=5
push ax ;1, quot, should be dx when ret, as the high 16 bit of result
;; use dx=5 and 4240h to do div
mov ax,ss:[si] ;ax=4240h. rem, only need dx of last result, use low 16bit of dividend, ie. ax, as ax
div cx ;ax=86a0h,dx=0h
;ax already is low 16bits of quot
mov cx,dx ;rem, store in cx
pop dx ;1, high 16 bits of quot
pop bx ;discard original ax
pop bx ;discard original dx
pop bx ;recover original bx
pop si
mov sp,bp
pop bp
ret
code ends
end start
只要 PUSH
的数量等于 POP
的数量就可以不用保存。
这并不总是很方便,但根据您的情况应该没问题。
除了,您在最后将保存的 DX
值弹出到 BX
,所以这可能是个问题。
编辑:
我看不出你在这种情况下需要它,但通常你可以这样做来恢复 BP
和 SP
:
push bp ; first statement of subroutine
mov bp, sp
...
mov sp, bp
pop bp
ret
在您的子例程中,您可以 BP
用作在调用之前压入堆栈的任何参数的基地址。同样,您的示例没有显示,但您似乎也没有使用您在 AX
寄存器中输入的值和语句
mov bx, ss:[0ch]
有点混乱。你怎么知道那个地址是什么?
If not, where should I define the stack and how to use it?
如何定义堆栈段取决于汇编程序。
类似于 segment stack
and/or assume ss:stack
。除了.com
/model tiny
程序外,不需要直接在启动时修改SS:SP
。
it seems I need to save the ss and sp values in the beginning of the procedure, but where should I save them?
典型程序仅使用单个堆栈段,因此无需 save/modify SS
注册。关于 SP
,不,你显然不能(不应该)将它保存到堆栈中。通常的技巧是:
push bp
mov bp, sp
... ; use [bp+4] to address the first argument on the stack
.... ; ss segment is assumed by default when [bp] used
mov sp, bp ; if sp was modified by some push/sub instructions
pop bp
ret
然而,它只有在您确实需要在过程中使用基于堆栈的参数时才有用。否则只需根据需要使用 push
/ pop
。
我的任务是设计一个在8086汇编中不会溢出的dword除法子程序(divdw)(我用masm5.0/masm.exe和masm5. 0/link.exe 和 debug.exe(在 MS-DOS 中)。 AX
、CX
、DX
用于保存divdw的结果。下面的代码还不能运行。
这是任务:
输入:
(ax)=low 16 bit of a dword dividend
(dx)=high 16 bit of a dword dividend
(cx)=16 bit divisor
输出:
(ax)=low 16 bit of the result
(dx)=high 16 bit of the result
(cx)=remainder
我用来计算的公式divdw:
dividend/divisor = quot(high 16 bit of dividend / divisor) +
( rem(high 16 bit of dividend / divisor) * 2^16 + low 16 bit of dividend ) / divisor
这是我的代码:
assume cs:code
code segment
start: mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: push bx
push dx ;ss:[0ch]
mov ax,dx
mov dx,0
div cx
mov ax,0 ;rem, only need dx
mov bx,ss:[0ch]
div bx
;; finish 1
push dx
push ax
;; get dx, into ax
mov ax,ss:[0ch]
div cx
;; finish 2
push ax
pop dx ;ax after 2
pop ax ;ax after 1
pop cx ;dx after 1
;; recover bx
pop bx
pop bx
ret
code ends
end start
在这段代码中,我尝试使用 pop
和 push
但没有定义堆栈段。这是允许的吗? (在调试器中我发现给出了 SS:SP
但 push
无法正常工作。)
如果没有,我应该在哪里定义堆栈以及如何使用它?
如果我在主程序中定义一个堆栈段,似乎我需要在程序开始时保存SS
和SP
值,但我应该把它们保存在哪里?我可以将它们保存在堆栈中,还是必须将它们保存在内存中的某个位置?
给出start主程序是为了测试。
谢谢!
编辑:
谢谢你们!在你的帮助下,我完成了这个任务。现在代码如下:
assume cs:code,ss:stack
stack segment
dw 8 dup (0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: push bx
push dx ;ss:[0ah]
push ax ;ss:[08h]
mov ax,dx ;ax=0fh
mov dx,0 ;dx=0
div cx ;ax=1,dx=5
push ax ;1, quot, should be dx when ret, as the high 16 bit of result
;; use dx=5 and 4240h to do div
mov ax,ss:[08h] ;ax=4240h. rem, only need dx of last result, use low 16bit of dividend, ie. ax, as ax
div cx ;ax=86a0h,dx=0h
;ax already is low 16bits of quot
mov cx,dx ;rem, store in cx
pop dx ;1, high 16 bits of quot
pop bx ;discard original ax
pop bx ;discard original dx
pop bx ;recover original bx
ret
code ends
end start
编辑于 20170724
又一个版本,去掉ss:[08h],用另外3个寄存器来存储。我不确定这是否更好,但它确实有效。
assume cs:code,ss:stack
stack segment
db 16 dup (0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: push bp
mov bp,sp
push si
push bx
push dx
push ax
mov si,sp
mov ax,dx ;ax=0fh
mov dx,0 ;dx=0
div cx ;ax=1,dx=5
push ax ;1, quot, should be dx when ret, as the high 16 bit of result
;; use dx=5 and 4240h to do div
mov ax,ss:[si] ;ax=4240h. rem, only need dx of last result, use low 16bit of dividend, ie. ax, as ax
div cx ;ax=86a0h,dx=0h
;ax already is low 16bits of quot
mov cx,dx ;rem, store in cx
pop dx ;1, high 16 bits of quot
pop bx ;discard original ax
pop bx ;discard original dx
pop bx ;recover original bx
pop si
mov sp,bp
pop bp
ret
code ends
end start
只要 PUSH
的数量等于 POP
的数量就可以不用保存。
这并不总是很方便,但根据您的情况应该没问题。
除了,您在最后将保存的 DX
值弹出到 BX
,所以这可能是个问题。
编辑:
我看不出你在这种情况下需要它,但通常你可以这样做来恢复 BP
和 SP
:
push bp ; first statement of subroutine
mov bp, sp
...
mov sp, bp
pop bp
ret
在您的子例程中,您可以 BP
用作在调用之前压入堆栈的任何参数的基地址。同样,您的示例没有显示,但您似乎也没有使用您在 AX
寄存器中输入的值和语句
mov bx, ss:[0ch]
有点混乱。你怎么知道那个地址是什么?
If not, where should I define the stack and how to use it?
如何定义堆栈段取决于汇编程序。
类似于 segment stack
and/or assume ss:stack
。除了.com
/model tiny
程序外,不需要直接在启动时修改SS:SP
。
it seems I need to save the ss and sp values in the beginning of the procedure, but where should I save them?
典型程序仅使用单个堆栈段,因此无需 save/modify SS
注册。关于 SP
,不,你显然不能(不应该)将它保存到堆栈中。通常的技巧是:
push bp
mov bp, sp
... ; use [bp+4] to address the first argument on the stack
.... ; ss segment is assumed by default when [bp] used
mov sp, bp ; if sp was modified by some push/sub instructions
pop bp
ret
然而,它只有在您确实需要在过程中使用基于堆栈的参数时才有用。否则只需根据需要使用 push
/ pop
。