在8086汇编中,如何在子程序中使用堆栈?

时间:2017-07-22 06:21:01

标签: assembly stack x86-16 integer-division

我的任务是设计一个dword division子程序( divdw ),它不会在8086程序集中溢出(我使用masm5.0 / masm.exe和masm5.0 / link.exe并调试MS-DOS中的.exe)。 AXCXDX用于保存 divdw 的结果。以下代码无法运行。

这是任务:

输入:

  

(ax)=低位红利16位   (dx)=双字红利的高16位
  (cx)= 16位除数

输出:

  

(ax)=结果的低16位
  (dx)=结果的高16位
  (CX)=剩余

我用来计算 divdw 的公式:

  

dividend / divisor = quot(高16位的被除数/除数)+
  (rem(高16位被除数/除数)* 2 ^ 16 +低16位被除数)/除数

这是我的代码:

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:SP已被提供,但push无法正常工作。)
如果没有,我应该在哪里定义堆栈以及如何使用它?

如果我在主过程中定义了一个堆栈段,似乎我需要在过程的开头保存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

2 个答案:

答案 0 :(得分:3)

只要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]

有点令人困惑。你怎么知道那个地址是什么?

答案 1 :(得分:3)

  

如果没有,我应该在哪里定义堆栈以及如何使用它?

如何定义堆栈段取决于汇编程序。 排序为JoinClause和/或->leftjoin('log_simple_calory as LC', function($join)use($tz_lccreated_date,$dateRange){ $join->on('LC.user_id_fk','=','UA.user_id_fk'); $join->on('LC.is_active','=',DB::raw('1')) })->whereRaw('date('.$tz_lccreated_date.')'. $dateRange); 。除segment stack / assume ss:stack个程序外,无需直接在启动时修改SS:SP

  

似乎我需要在程序开始时保存ss和sp值,但是我应该在哪里保存它们?

典型程序仅使用单个堆栈段,因此无需保存/修改.com寄存器。关于model tiny,不,你显然不能(不应该)将它保存到堆栈中。通常的诀窍是:

SS

但是,如果您真的需要在过程中使用基于堆栈的args,那么它才有用。否则,只需根据需要使用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