Search code examples
assemblyx86-16emu8086integer-division

Convert HEX to decimal in assembly 8086


I am trying to create a code which would take as input 1 8-bit number in HEX form and produce the decimal equivalent.

Here is my code:

data segment 
    
    msg1 db 10,13, "Give me a valid hex number:$"
    hex1 db 0
    hex2 db 0
    hundred db 0
    hundredrem db 0
    decade db 0 
    decaderem db 0
    unit db 0 
    unitrem db 0
    res db 0
    hundredval db 100
    decadeval db 10
    unitval db 1
ends

stack segment
    dw   128  dup(0)
ends

code segment
start: 

      L1:

      mov ax,data
      mov ds,ax
      
      lea dx,msg1
      mov ah,09h
      int 21h
      
      mov ah,01h
      int 21h
      cmp al,48
      jb L1
      cmp al,70
      ja L1
      cmp al,57
      jbe sethex1
      cmp al,65
      jae sethex1 
      lea dx,msg1
      mov ah,09h
      int 21h
      
      
      
      sethex1:
      
      mov hex1,al
      jmp L2
      
      L2:
      
      mov ah,01h
      int 21h
      cmp al,48
      jb L1
      cmp al,70
      ja L1
      cmp al,57
      jbe sethex2
      cmp al,65
      jae sethex2
      lea dx,msg1
      mov ah,09h
      int 21h
      
      
      sethex2:
      
      mov hex2,al
      jmp maincalc
      
      
      maincalc:
      
      cmp hex1,60
      ja calclettsHex1
      jb calcnumsHex1
      
      calclettsHex1:
      
      mov bl,hex1
      sub bl,55
      jmp movetoHex2
      
      calcnumsHex1:
      
      mov bl,hex1
      sub bl,48
      jmp movetoHex2
      
      movetoHex2:
      
      cmp hex2,60
      ja calclettsHex2
      jb calcnumsHex2
      
      calclettsHex2:
      
      mov cl,hex2
      sub cl,55
      jmp conv2dec
      
      calcnumsHex2:
      
      mov cl,hex2
      sub cl,48
      jmp conv2dec
      
      conv2dec:
      
      mov al,16
      mul cl
      add al,bl
      mov res,al
      
      mov al,res
      
      div hundredval
      mov hundredrem,ah
      mov hundred,al
      mov al,hundredrem
      div decadeval
      mov decaderem,ah
      mov decade,al
      mov al,decaderem
      div unitval
      mov unit,ah
      
      mov dl,hundred
      add dl,30h
      mov ah,02h
      int 21h
      mov dl,decade
      add dl,30h
      mov ah,02h
      int 21h
      mov dl,unit
      add dl,30h
      mov ah,02h
      int 21h
        

I was expecting the code to work but then it shows me an error dialog "Division overflow". So I decided to look at the values of the registers.

Until line mov al,res the state of the registers is this:enter image description here

and due to x86 processors use big endian , the result is expected. However after the next instruction:

div hundredval the state of the registers becomes this:

enter image description here

and this is what creates the problem. The remainder stays > 100 when A1 = 161. So how can I fix this?


Solution

  • and due to x86 processors use big endian ,

    Incorrect! x86 uses little endian: in memory, the low byte is stored before the high byte.


      cmp al,57
      jbe sethex1
      cmp al,65
      jae sethex1 
      lea dx,msg1
      mov ah,09h
      int 21h
    
    sethex1:
    

    Your current program erroneously allows the characters ":;<=>?@" to pass! For now, you just (re)display the prompt and then happily continue as if the character were a correct one ("0123456789ABCDEF").
    The quick fix would be:

      cmp al, 57
      jbe sethex1
      cmp al, 65
      jae sethex1 
      jmp L1
    sethex1:
    

    but the better fix is:

      cmp al, '9'
      jbe sethex1
      cmp al, 'A'
      jb  L1
    sethex1:
    

    The latter allows falling-through in the label sethex1.


    conv2dec:
    
      mov al,16
      mul cl
      add al,bl
    

    The first hexadecimal digit that you input and that you stored in the hex1 variable and afterwards converted into its [0,15] value in the BL register, is the most significant digit. Therefore, that is the digit that you must multiply by 16.

    conv2dec:
    
      mov  al, 16
      mul  bl
      add  al, cl
    

      div hundredval
      mov hundredrem,ah
      mov hundred,al
      mov al,hundredrem
      div decadeval
      mov decaderem,ah
      mov decade,al
      mov al,decaderem
      div unitval
      mov unit,ah
    

    All of these div operations are byte-sized because the divisors hundredval, decadeval, and unitval have been defined using DB. Therefore, the division will use the whole AX register as its dividend.
    In case of div hundredval, AX contains a value in the range [0,255], so that's fine. However, in case of div decadeval, you load AL but allow AH to retain its last value. You must zero AH beforehand.
    The same problem exists for the div unitval instruction but here the solution is not to zero AH but rather to not execute this redundant (but some might call it silly) operation of dividing by 1. Your decaderem is equal to your unit.

    div  hundredval
    mov  hundred, al  ; [0,2]
    mov  al, ah
    mov  ah, 0
    div  decadeval
    mov  decade, al   ; [0,9]
    mov  unit, ah     ; [0,9]
    

    The above should cover the mistakes, but there's more that can be said about the code in this program. Be sure to read Displaying numbers with DOS and once you get the program working you could consider posting it on our companion site https://codereview.stackexchange.com/questions/tagged/assembly.