1

I am trying to create a calculator program using Assembly Language but it is required for us to show 4 digit number operations.

In this code, I can add, subtract and divide numbers with 4 digits each, but when multiplying 4 digit by 4 digit numbers, the answer is different and wrong

Example: 9999x9999= 37601 (which is wrong and the answer should be 99980001)

Here is the part of the code for Multiplication:

mult: 
pop ax
mul bx      
push ax     
lea dx,Mulseu   
mov ah,09h
int 21h 
pop ax
mov cx,0
mov dx,0
mov bx,10d
jmp wrong

Here is the FULL code:

.model small
.stack 100h

.data
msg1    db 13, 10, 13, 10,"MENU "
        db 10, 13,"1. Add   "
        db 10, 13,"2. Subtract "
        db 10, 13,"3. Multiply "
        db 10, 13,"4. Divide "
        db 10, 13,"5. Exit "
        db 13,10,13,10, "Enter 1st Number : $"
msg2 db 13,10, "Enter 2nd Number : $"
msgEr db 13,10, "Error $"
choiceseu db 13,10, "Enter choice: $ "
sumseu db 13,10,13,10, "***Sum is : $"
Diffseu db 13,10,13,10, "***Difference is : $"
Divseu db 13,10,13,10, "***Quotient is : $"
Mulseu db 13,10,13,10, "***Product is : $"
temp     db ?


.code

start:
mov ax, @data
mov ds, ax

lea dx, msg1
mov ah, 09h
int 21h
mov bx, 0

ph1:
mov ah, 01h
int 21h
cmp al,0dh      
je input1
mov ah,0        
sub al,30h      
push ax         
mov ax,10d      
mul bx          
pop bx          
add bx,ax       
jmp ph1      




input1:
push bx
lea dx,msg2
mov ah,09h
int 21h

mov bx,0


ph2:
mov ah,01h
int 21h
cmp al,0dh
je choice
mov ah,0
sub al,30h
push ax
mov ax,10d
mul bx
pop bx
add bx,ax 
jmp ph2


choice:
lea dx, choiceseu
mov ah, 09h
int 21h

mov ah, 01h
int 21h


cmp al,'4'
je divd

cmp al,'1'  
je addz

cmp al,'2'
je subt

cmp al,'3'
je mult

cmp al,'5'
mov ah, 4ch
int 21h

error:
lea dx,msgEr
mov ah,09h
int 21h 
jmp start


divd: 
pop ax
mov dx, 0
div bx
push ax
lea dx,Divseu
mov ah,09h
int 21h 
pop ax
mov cx,0
mov dx,0
mov bx,10d
jmp wrong

addz:     
pop ax
add ax,bx   
push ax
lea dx,sumseu   
mov ah,09h
int 21h 
pop ax
mov cx,0
mov dx,0
mov bx,10d
jmp wrong   

mult: 
pop ax
mul bx      
push ax     
lea dx,Mulseu   
mov ah,09h
int 21h 
pop ax
mov cx,0
mov dx,0
mov bx,10d
jmp wrong


subt: 
pop ax
sub ax,bx 
push ax
lea dx,Diffseu
mov ah,09h
int 21h 
pop ax
mov cx,0
mov dx,0
mov bx,10d

wrong:
mov dx, 0
div bx
push dx
mov dx,0
inc cx
or ax,ax 
jne wrong 

ans:        
pop dx
add dl,30h
mov ah,02h
int 21h
loop ans

jmp start



end start
Xer007
  • 35
  • 8
  • Are you sure your assignment requires you to support 8-digit results? The sensible alternative would be producing the low 4 digits (i.e. correct result modulo 10k), but that still means you can't just let a 16-bit result wrap modulo 2^16. – Peter Cordes Oct 20 '17 at 07:42
  • 1
    If you do need the full multiply result, the high half of the 16b x 16x => 32b multiply result is in DX. i.e. `mul bx` produces a 32-bit result in `dx:ax`. – Peter Cordes Oct 20 '17 at 07:43
  • 99980001 can't fit a 16-bit register. In hex it's 5F5 92E1 so it takes 28 bits. You just print the lower 16 bit (AX), i.e. 92E1 (or 37601 in dec). If you adapt the code at the `wrong` label to not clear DX you can print 32-bit numbers. – Margaret Bloom Oct 20 '17 at 07:44
  • @MargaretBloom I tried not to clear DX at the `wrong` label. The answer doesn't show up anymore and the program is stuck. – Xer007 Oct 20 '17 at 07:54
  • @PeterCordes yes. My professor wants the full multiply result, but my code prints the lower 16 bits. I'm new to assembly so I'm sorry for not understanding some terms.I also tried not to clear `dx` but the program hangs. – Xer007 Oct 20 '17 at 08:05
  • 1
    right after `mul` instruction the pair of registers `dx` and `ax` hold the total result. You need to store both, then restore them before print, and adjust your output code at `wrong:` to output 32 bit value stored in two registers, which is not trivial (dividing `0x05F592E1` by `10` with 16b division like `div bx` will cause division overflow, because the result will not fit into `ax`). Either figure out whether you can use the 32b registers (then convert all your arithmetic to 32b regs, like `eax`), or search SO how to print 32b value in x86-16. – Ped7g Oct 20 '17 at 08:13
  • You need to adapt the whole function, not clearing DX is necessary but not sufficient, unfortunately. Check what Ped7g said out. – Margaret Bloom Oct 20 '17 at 08:17
  • 1
    Other option is to not use binary values for calculations, but keep them as separate digits (BCD unpacked is one of options to store values) and do all your arithmetic with separate digits like on paper. Also make sure you understand why we are tossing around those 16/32 bits remarks, and why your result in question *is* sort of *correct* from 16b calculation perspective. – Ped7g Oct 20 '17 at 08:17
  • That said, I just realized there's no really simple way to do division per digits on paper, so you will hit some problems even with that approach, still solvable by doing some division-by-subtraction loop, eventually optimizing it by using 10^i*divisor values for subtraction (as doing 10^i*divisor is very simple when you have it split per digit, just adding *i* zero digits at end of it), so the total amount of looping will be less than result value, but even full subtraction loop for reasonable small numbers is bearable with modern HW ("12345678/2" is about 50mil of loops = well under 0.1s) – Ped7g Oct 20 '17 at 11:58
  • @Ped7g making the code longer? I'm still unable to print 32b values despite your advice. I don't know why, the program doesn't display errors but hangs up the moment it solves 32b values. Please help me. – Xer007 Oct 20 '17 at 13:04
  • use debugger of course, to identify the exact cause. – Ped7g Oct 20 '17 at 13:37
  • 1
    To learn how to display your product in `DX:AX` read this excellent Q/A [Displaying numbers with DOS](https://stackoverflow.com/questions/45904075/displaying-numbers-with-dos?s=1|97.2003). – Fifoernik Oct 21 '17 at 12:16

1 Answers1

0
mult: 
pop ax
mul bx      
push ax     
lea dx,Mulseu   
mov ah,09h
int 21h 
pop ax
mov cx,0
mov dx,0
mov bx,10d
jmp wrong

Since you want to display the full 32-bit result (in DX:AX) from the multiplication, you can't let the DX register go to waste! You need to preserve it just like you did with AX.
Because your current conversion/displaying routine (Why o why is this named wrong ?) only knows about 16-bit numbers, you'll need another routine that I'll present below. This routine comes from another answer that I wrote some time ago. You should definitely read it. It explains in great detail how these things work and so I shall not repeat that explanation here.

mult: 
    pop     ax
    mul     bx      
    push    ax
    push    dx    
    lea     dx, Mulseu   
    mov     ah, 09h
    int     21h 
    pop     dx
    pop     ax
    jmp     DisplayNumber32

    ...

DisplayNumber32:
    mov     bx,10          ;CONST
    push    bx             ;Sentinel
.a: mov     cx,ax          ;Temporarily store LowDividend in CX
    mov     ax,dx          ;First divide the HighDividend
    xor     dx,dx          ;Setup for division DX:AX / BX
    div     bx             ; -> AX is HighQuotient, Remainder is re-used
    xchg    ax,cx          ;Temporarily move it to CX restoring LowDividend
    div     bx             ; -> AX is LowQuotient, Remainder DX=[0,9]
    push    dx             ;(1) Save remainder for now
    mov     dx,cx          ;Build true 32-bit quotient in DX:AX
    or      cx,ax          ;Is the true 32-bit quotient zero?
    jnz     .a             ;No, use as next dividend
    pop     dx             ;(1a) First pop (Is digit for sure)
.b: add     dl,"0"         ;Turn into character [0,9] -> ["0","9"]
    mov     ah,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    pop     dx             ;(1b) All remaining pops
    cmp     dx,bx          ;Was it the sentinel?
    jb      .b             ;Not yet

For all the remaining operations (addz, subt, divd) you can also use this new DisplayNumber32 routine. Just make sure to zero the DX register beforehand.

subt: 
    pop     ax
    sub     ax, bx 
    push    ax
    lea     dx, Diffseu
    mov     ah, 09h
    int     21h 
    pop     ax
    xor     dx, dx              ;Add this for the 32-bit version!
    jmp     DisplayNumber32
Sep Roland
  • 33,889
  • 7
  • 43
  • 76