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:
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:
and this is what creates the problem. The remainder stays > 100 when A1 = 161. So how can I fix this?
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.