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 中)。 AXCXDX用于保存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

在这段代码中,我尝试使用 poppush 但没有定义堆栈段。这是允许的吗? (在调试器中我发现给出了 SS:SPpush 无法正常工作。)
如果没有,我应该在哪里定义堆栈以及如何使用它?

如果我在主程序中定义一个堆栈段,似乎我需要在程序开始时保存SSSP值,但我应该把它们保存在哪里?我可以将它们保存在堆栈中,还是必须将它们保存在内存中的某个位置?

给出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,所以这可能是个问题。

编辑:

我看不出你在这种情况下需要它,但通常你可以这样做来恢复 BPSP:

 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