Program solving expression in assembly

12,245

Solution 1

Your program is almost good, you only have some issues with operand sizes, which is normal. So I took your code and made some little changes, those changes are commented and pointed by arrows (<========) and they are :

  • Fixed the operand size problem. I still use DB because I noticed you are capturing the numbers as single chars.
  • The result of (d-2*c) is stored in BX. This is because we need to divide (a+c*b) / (d-2*c), and you were popping (a+c*b) in BX, so, when you do div bx you were doing (d-2*c) / (a+c*b) .
  • Separated the display for quotient and remainder.
  • Added 13,10 line breaks to messages.
  • Fixed shl ax,2 by shl ax,1. One shl is x2, two shl are x2x2.
  • The remainder is obtained from dl because when div uses a word as divisor, the remainder is left in dx.

Here is your code with the little changes (tested on EMU8086):

; --------------------------------------------
; Equation=(a+c*b)/d-2*c,
; --------------------------------------------.model small
.stack 100h
.data
    a   db 0                
    b   db 0
    c   db 0
    d   db 0
    result1 db ?
    result2 db ?



    message1 db 13,10,"Equation: (a+c*b)/d-2*c",13,10,"a=$"
    message2 db 13,10,"b=$"         ;<================= 13,10 IS
    message3 db 13,10,"c=$"         ;<================= LINEBREAK.
    message4 db 13,10,"d=$"         ;<=================
    message5 db 13,10,"Quotient=$"  ;<=================
    message6 db 13,10,"Remainder=$" ;<=================
.code

start:  mov ax,@data
        mov ds,ax           



        mov ax, seg message1   ;get a and save to a variable
        mov ds,ax   
        mov dx,offset message1
        mov ah, 9h
        int 21h
        mov ah, 1h 
        int 21h
        sub al,30h    ;converting to real number
        mov a,al

        mov ax, seg message2 ;get b and save to a variable
        mov ds,ax   
        mov dx,offset message2
        mov ah, 9h
        int 21h
        mov ah, 1h
        int 21h
        sub al,30h    ;converting to real number
        mov b,al


        mov ax, seg message3    ;get c and save to a variable
        mov ds,ax   
        mov dx,offset message3
        mov ah, 9h
        int 21h
        mov ah, 1h 
        int 21h
        sub al,30h    ;converting to real number
        mov c,al


        mov ax, seg message4   ;get d and save to a variable
        mov ds,ax   
        mov dx,offset message4
        mov ah, 9h
        int 21h
        mov ah, 1h 
        int 21h
        sub al,30h   ;converting to real number
        mov d,al


        mov al,b          ; (a+c*b)
        mul c
        mov cl,A    ;<======== MOV A TO CX TO
        mov ch,0    ;<======== ADD IT TO AX.
        add ax,CX   ;<======== C*B + A.

       ;push ax     ;<======== NO LONGER NECESSARY BECAUSE
                    ;<======== IN NEXT BLOCK WE USE BX.

        mov bl,C    ;<======== MOV C TO BL AND CLEAR
        mov bh,0    ;<======== BH. NOW C IS IN BX.
        shl bx,1    ;<======== 2*c. ONE SHIFT IS x2, TWO SHIFTS ARE x2x2.
        sub d,bl          ;d - 2c
        mov bl,d    ;<======== MOV D TO BL AND CLEAR BH. NOW
        mov bh,0    ;<======== D IS IN BX. BX = (D-2C).

       ;pop ax      ;<======== NO LONGER NECESSARY. AX CONTAINS (A+C*B).

        mov dx,0    ;<======== CLEAR DX, BECAUSE DIVISOR IS A WORD.
                    ;<======== WHEN DIVISOR IS A WORD, DIV USES DX:AX. 
        div bx      ;<======== dx:ax / bx == DX:(A+C*B) / (D-2C). THIS
                    ;<======== DIVISION IS UNSIGNED, FOR SIGNED USE IDIV.

        mov result1,al ;<===== QUOTIENT.
        mov result2,dl ;<===== REMAINDER, BECAUSE DIVISOR IS WORD.

        add result1,30h   ;converting to string
        add result2,30h    ;converting to string

        mov al,result1
        mov bl,result2

      ;DISPLAY QUOTIENT  <=============
        mov ax, seg message5
        mov ds,ax   
        mov dx,offset message5
        mov ah, 9h
        int 21h
        mov al,result1
        mov dl, al
        mov ah , 2h
        int 21h       
      ;DISPLAY REMAINDER  <=============
        mov ax, seg message6
        mov ds,ax   
        mov dx,offset message6
        mov ah, 9h
        int 21h
        mov dl, bl
        mov ah , 2h
        int 21h

        mov ax,4C00h        
        int 21h

end     start

Next is your "to do" list:

  • Change the size of operands from DB to DW, to allow your program to handle bigger numbers.
  • Change DIV by IDIV, because DIV is unsigned while IDIV is signed. IDIV will let you handle negative results.
  • Capture numbers with int=21h ah=0Ah as strings (not as single chars). Later, you convert the strings into numbers. Next two links will take you to the procedures to convert from string to number :

Assembly x86 Date to Number - Breaking a string into smaller sections

32 bit Calculator in 8086 Assembly

Finally, the test data :

(a+c*b) / (d-2*c)

a=1
b=2
c=3
d=8

a+c*b = 1+3*2 = 7
d-2*c = 8-2*3 = 2

7 / 2 = quotient 3, remainder 1

Solution 2

Because this question is an big success at 9k views and because the accepted answer is essentially wrong and misguiding, I decided to post a correct version so people can finally find out how to calculate these simple expressions.


I have problem with program. Operand types do not match at line 76 78 80.

add  ax,a   ; line 76
push ax
mov  ax,c   ; line 78    
shl  ax,2
sub  d,ax   ; line 80

In most assembly instructions the size of the operands on both sides of the comma must match. Since you have defined your a, b, c, and d variables as bytes, you cannot legally use them with the word-sized register AX. That's why TASM gave your the error message.

When evaluating an expression like (a+c*b)/d-2*c, you have to respect the algebraic rules.

  • items that are parenthesised get calculated as a whole
  • for items that are not parenthesised you need to follow the normal precedence rules: * and / come before + and -

Redundantly parenthesizing everything we get: (a+c*b)/d-2*c <=> ((a+(c*b))/d)-(2*c)

  • when sets of parenthesis are nested the inner set has precedence over the outer set

Considering that a, b, and c are single digit numbers from 0 to 9, and that d is a single digit number from 1 to 9, the result can range from -18 to 72. Therefore we can calculate the whole expression using byte-sized operations. It's not necessary to use the signed division idiv since the dividend at that point will always be positive.

mov  al, c    ; AL = c
mul  b        ; AL = c * b                           AH is 0
add  al, a    ; AL = (c * b) + a                     AH is 0
div  d        ; AL = ((c * b) + a) / d               AH is remainder
sub  al, c    ; AL = (((c * b) + a) / d) - c         AH is remainder
sub  al, c    ; AL = ((((c * b) + a) / d) - c) - c   AH is remainder

Please notice that we used just one register (AX) to find the result. Would you have expected this?

Below is my implementation of it all. I only left out the part that displays the quotient and remainder, but I have provided a link to Displaying numbers with DOS that explains in great detail how you can output signed and unsigned numbers. It's the basic stuff that you simply must know, so it's never gonna be a waste of time if you read it whole.

; --------------------------------------------
; Expression=(a+c*b)/d-2*c,
; --------------------------------------------
  ORG  256              ; Use the .COM file format

; Make sure all inputs are valid single digit numbers                      
  cld
  mov  dx, offset msgA
  mov  ah, 09h          ; DOS.PrintString
  int  21h
  mov  di, offset a     ; Storage for the a, b, c, and d variables (adjacent in memory)
  mov  si, offset msgB  ; Offset of the incomplete message
  mov  bl, "a"          ; Character that completes the message
Again:
  mov  [si+2], bl       ; Completing the message
  inc  bl
  mov  dx, si
  mov  ah, 09h          ; DOS.PrintString
  int  21h
Redo:
  mov  ah, 01h          ; DOS.GetCharacter
  int  21h              ; -> AL
  sub  al, 30h          ; From character ["0","9"] to number [0,9]
  cmp  al, 10
  jnb  Redo
  stosb                 ; Is indeed in range [0,9]
  cmp  bl, "e"          ; Repeat for "b", "c", and "d"
  jb   Again
  dec  di
  cmp  al, 0
  je   Redo             ; Can't allow d=0 since it will be used as a divider

; The calculation
  mov  al, c            ; AL = c
  mul  b                ; AL = c * b                           AH is 0
  add  al, a            ; AL = (c * b) + a                     AH is 0
  div  d                ; AL = ((c * b) + a) / d               AH is remainder
  sub  al, c            ; AL = (((c * b) + a) / d) - c         AH is remainder
  sub  al, c            ; AL = ((((c * b) + a) / d) - c) - c   AH is remainder
  mov  Q, ax            ; Storage for the Q, and R variables (adjacent in memory)

; Displaying the quotient and remainder
  mov  dx, offset msgB  ; Offset of the incomplete message
  mov  ax, 0951h        ; DOS.PrintString
  mov  [msgB + 2], al   ; Completing the message with AL = "Q"
  int  21h
  mov  al, Q
  call DisplaySignedNumber8

  mov  dx, offset msgB  ; Offset of the incomplete message
  mov  ax, 0952h        ; DOS.PrintString
  mov  [msgB + 2], al   ; Completing the message with AL = "R"
  int  21h
  mov  al, R
  call DisplaySignedNumber8

  mov  ax, 4C00h        ; DOS.Terminate
  int  21h
; --------------------------------
a    db 0                           
b    db 0
c    db 0
d    db 0
Q    db 0
R    db 0
msgA db "Formula: (a+c*b)/d-2*c$"
msgB db 13, 10, "? = $"
Share:
12,245
Mack
Author by

Mack

Updated on June 28, 2022

Comments

  • Mack
    Mack almost 2 years

    I have a problem with my simple program in assembly. I'm using DOSBox and TASM. The problem is that the operand types don't match in lines 76, 78, and 80. This is after multiplication. I tried to make some changes by using a different variable size.

    ; --------------------------------------------
    ; Equation=(a+c*b)/d-2*c,
    ; --------------------------------------------
    .model small
    .stack 100h
    .data
            a       db 0                           
            b       db 0
            c       db 0
            d       db 0
            result1 db ?
            result2 db ?
           
     
           
            message1 db "Equation: (a+c*b)/d-2*c   a=$"
            message2 db "b=$"
            message3 db "c=$"
            message4 db "d=$"
            message5 db "Result=$"
    .code
     
    start:  mov ax,@data
                    mov ds,ax                      
    
                    mov ax, seg message1   ;get a and save to a variable
                    mov ds,ax      
                    mov dx,offset message1
                    mov ah, 9h
                    int 21h
                    mov ah, 1h
                    int 21h
                    sub al,30h    ;converting to real number
                    mov a,al
                   
                    mov ax, seg message2 ;get b and save to a variable
                    mov ds,ax      
                    mov dx,offset message2
                    mov ah, 9h
                    int 21h
                    mov ah, 1h
                    int 21h
                    sub al,30h    ;converting to real number
                    mov b,al
                   
                   
                    mov ax, seg message3    ;get c and save to a variable
                    mov ds,ax      
                    mov dx,offset message3
                    mov ah, 9h
                    int 21h
                    mov ah, 1h
                    int 21h
                    sub al,30h    ;converting to real number
                    mov c,al
                   
                
                    mov ax, seg message4   ;get d and save to a variable
                    mov ds,ax      
                    mov dx,offset message4
                    mov ah, 9h
                    int 21h
                    mov ah, 1h
                    int 21h
                    sub al,30h   ;converting to real number
                    mov d,al
                   
                   
                    mov al,b         ; (a+c*b) ------------------------error
                    mul c                          
                    add ax,a       ; ------------------------error
           
                    push ax     ;save current ax
           
                    mov ax,c     ;d-2*c------------------------error
                    shl ax,2
                    sub d,ax
                   
                   
                    pop bx     ;get previous ax  to bx
                   
                    div bx     ; div ax:bx
                   
                    mov result1,al
                    mov result2,ah
           
                    add result1,30h   ;converting to string
                    add result2,30h    ;converting to string
                   
                    mov al,result1
                    mov bl,result2
                   
                    mov ax, seg message5
                    mov ds,ax      
                    mov dx,offset message5
                    mov ah, 9h
                    int 21h
                    mov al,result1
                    mov bl,result2
                    mov dl, al
                    mov ah , 2h
                    int 21h
                    mov dl, bl
                    mov ah , 2h
                    int 21h
                   
                    mov ax,4C00h           
                    int 21h
     
    end             start
    
  • Mack
    Mack almost 9 years
    Thank You . Now i will make it more usefull like You said :>
  • Peter Cordes
    Peter Cordes over 3 years
    This correctly (I think) evaluates (a+c*b) / (d-2*c), not following the order of operations / operator precedence in the (a+c*b)/d -2*c formula the question asked for. It's weird to give the remainder from an intermediate part of an expression, but that's how formulas work. (Unless it was incorrectly transcribed from math notation like how $\frac{ (a+c*b }{ d-2*c } prints.
  • Sep Roland
    Sep Roland over 3 years
    @PeterCordes I did retag the question from equation to expression, but missed the same thing in my answer. Thanks for the edit.
  • Peter Cordes
    Peter Cordes over 3 years
    Cheers, glad you agree that people should use math terminology correctly, and that I'm not the only person bothered by people "solving" simple formulae / expressions and calling them equations. :)