1
votes

My task is to design a dword division child procedure (divdw) that will not overflow in 8086 assembly (I use masm5.0/masm.exe and masm5.0/link.exe and debug.exe in MS-DOS). AX, CX, and DX are used to save the result of divdw. The following code cannot run yet.

Here is the task:

input:

(ax)=low 16 bit of a dword dividend
(dx)=high 16 bit of a dword dividend
(cx)=16 bit divisor

output:

(ax)=low 16 bit of the result
(dx)=high 16 bit of the result
(cx)=remainder

The formula I used to calc 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

Here is my code:

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

In this code, I am trying to use pop and push but did not define a stack segment. Is this permitted? (In the debugger I found the SS:SP is given but the push cannot work correctly.)
If not, where should I define the stack and how to use it?

If I define a stack segment in the main procedure, it seems I need to save the SS and SP values in the beginning of the procedure, but where should I save them? Can I save them in the stack, or must I save them somewhere in memory?

The start main procedure is given for the purpose of testing.

Thanks!


Edit:

Thank you both! With your help, I finished this task. Code is now as follows:

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

Edited on 20170724

Yet another version, get rid of the ss:[08h], and used another 3 registers to store. I am not sure whether this is better, but it works.

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
how is this loaded/linked? The stack should be prepared by loaderGarr Godfrey
I use masm5.0/masm.exe and masm5.0/link.exe and debug.exe in MS-DOS .cmal
A stack of only 16 bytes? You must be joking!Fifoernik
Please don't use mov ax,ss:[08h]. That's not good programming practise. Why don't you use the stack frame pointer BP like both answerers told you?Fifoernik
Sorry for that. I just not quite understand that for that moment. I will try out another version soon. Thanks @Fifoernikcmal

2 Answers

3
votes

You don't have to save them as long as the number of PUSH is equal to the number of POP.

That isn't always convenient, but it your case, it should be fine.

Except, you are popping the saved DX value into BX at the end, so that could be a problem.

EDIT:

I don't see that you need it in this case, but in general you can do this to restore BP and SP:

 push bp   ; first statement of subroutine
 mov bp, sp

 ...

 mov sp, bp
 pop bp
 ret

Within your subroutine, you then have BP to use as a base address for any parameters pushed onto the stack before the call. Again, your example doesn't show that, but you also don't seem to use the value you put in AX register and the statement

mov bx, ss:[0ch]

is a bit confusing. How do you know what is at that address?

3
votes

If not, where should I define the stack and how to use it?

How you define the stack segment depends on the assembler program. Sort of segment stack and/or assume ss:stack. There's no need to modify SS:SP directly on the startup, except .com/model tiny programs.

it seems I need to save the ss and sp values in the beginning of the procedure, but where should I save them?

Typical program uses only single stack segment, so there's no need to save/modify SS register. Concerning SP, no, you obviously can't (shouldn't) save it onto the stack. The usual trick is:

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

However it's only useful if you really need to work with the stack-based args inside of the procedure. Otherwise just use push / pop as needed.