I am writing an ASM program that divides two numbers and calculates 20 decimal places. My strategy was to calculate the next digits with a long division sort of process and push them to the stack. Then get the stack pointer, subtract 20 digits * 8 bits from it and write the digit from that address, then add 8 to that adress, write that digit etc. But when I try that it writes out only zeros. The digits are on the stack because I tried just "popping" them and that works fine. I just need them to be popped in the same order that they were pushed, not reversed.
my code:
dane SEGMENT ;segment danych
text db 0dh, 0ah,"Give b (a/b)", 0dh, 0ah, "$"
text2 db 0dh, 0ah,"Give a (a/b)", 0dh, 0ah, "$"
text3 db 0dh, 0ah,"a/b:", 0dh, 0ah, "$"
quot db 0h
rem db 0h
counter db 0h
dane ENDS
; C:\ml /Fl /Zm /Zi /c lab3.asm
; E:\link /CODEVIEW lab3.obj
rozkazy SEGMENT 'CODE' use16 ;segment rozkazu
ASSUME cs:rozkazy, ds:dane
start:
mov ax, SEG dane
mov ds, ax
mov cl, 03h
mov ch, 0Ah
mov dx, offset text
mov ah, 09h
int 21h ; "input divisor"
mov ah, 01h
int 21h ; input into al
mov cl, al
sub cl, 30h ; move to cl and subtract 30h to get value instead of ascii
mov dx, offset text2
mov ah, 09h
int 21h ; "input dividend"
mov ah, 01h
int 21h
sub al, 30h ; input into al and get number instead of ascii
jmp divide
divide:
cbw
div cl ; convert al to ax and divide by cl
mov dl, ah
cbw ; convert al -> ax
push ax ; push ax to stack
mov al, dl ; move remainder from division to al
xor ah, ah
cbw
mul ch ; clear ah and al - > ax, multiply remainder times 10
inc counter ; increase counter by 1
cmp counter, 14h ; if the division was preformed 20 times, jump to show
jz show
jmp divide
show:
mov dx, offset text3
mov ah, 09h
int 21h ; "your number"
mov bx, sp
sub bx, 160 ; get stack pointer address and subtract by 8*20 (to get address of the number that is 20 positions down from sp)
jmp show2
show2:
mov dx, [bx] ; move value to dx from address that is stored in bx
add dl, 30h ; add 30h to get ascii
mov ah, 02h
int 21h ; write dl out
dec counter ; decrease counter
add bx, 8h ; move our memory pointer 8 up (to next number)
cmp counter, 0h
jz finish ; if counter = 0 jump to finish
jmp show2
finish:
mov ah, 4CH
int 21H
rozkazy ENDS
stosik SEGMENT stack
dw 128 dup(?)
stosik ENDS
END start
The push ax
stores 2 bytes into memory and you do that 20 times = 40 bytes. I don't see how you managed to find 160 for sub bx
.
Also the stack grows downward, i.e. if ahead of your loop the sp
is 1234
, then after first push ax
it will be adjusted to 1232
(-2), and the al
value is right there at [1232]
and ah
at [1233]
.
So you should do instead add bx,19*2
to calculate address of the first stored digit, and then sub bx,2
to move to the next one.
BTW, in this case it would be more efficient to not even store the values, but just output them immediately, so I guess you are exercising stack memory manipulation.
Also use some debugger to check the memory and addresses yourself, your mistake should be obvious if you mark down the sp
after each push ax
, where did you store particular digit. There was some kind of "cv.exe" (code view) from Microsoft DOS, or you may try to search for other popular DOS debuggers, although most of them are probably difficult to obtain in legal way.
You may also consider to use some modern tools like DOS emulators with built-in debuggers (some dosbox builds have it, or BOCHS, etc...), which are usually more powerful than the SW debugger running in DOS.
Oh, now I see...
subtract 20 digits * 8 bits from it
Memory is addressable by bytes, not bits, so that would suggest you may want to adjust by 20 (20 digits * 1 byte). But you are doing push ax
, storing 16 bits, not just 8 (because there is no push 8 bits
instruction on x86-16 CPUs), and 16 bits are 2 bytes, so 20 * 2 = 40 is needed and in opposite direction.