Reputation: 2222
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
Upvotes: 1
Views: 728
Reputation: 15091
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.
Upvotes: 3
Reputation: 9005
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?
Upvotes: 3