Assembly: How to convert hex input to decimal?

2019-07-25 03:38发布

I have found many questions on this issue, however, I have not been able to get my code to run. My program should take a hex value, check to see if it is a valid hex character, then display the hex value as a decimal value. If it is a lower case hex character then it needs to be converted to upper case. All this needs to be in a loop.

I have all of this done except for converting the hex to decimal. I have the code in the program I think should convert it, but it will not compile. I will list the compiler errors below the code. If the code in the label convert: is commented out(except for the last line, "jmp display") then the program will run as it should, but obviously will not show the value as a decimal.

Compiling with nasme: "nasm -fbin getChar.asm -o getChar.com -l getChar.lst"

I am running the program in dosbox.

; This program gets a char from user and prints it out as a decimal 

    org 100h        ; program start point
section .data
    msgIn:  DB  13, 10, "Enter a Hex Digit: $"
    msgOut: DB  13, 10, "Decimal Value: $"
    msgOpt: DB  13, 10, "Enter another?(y or n): $"
    errMsg: DB  13, 10, "Illegal character, Enter 0..9 or A..F: $"
    HNUM:   DB  19H
    NUM:    DB  0
    D:      DB  10h
    H:      DB  16
    CNT:    DB  0

section .text

continue:               ; start of loop
        mov dx, msgIn   ; offset address of message to display
        mov ah, 9       ; print string function
        int 21h

        mov ah, 1       ; keyboard input sub-program
        int 21h         ; read character into al
        mov cl, al

legal:                  ; compare input to see if valid
        cmp cl, 48      ; cl < 0
        jl  end_if      ; yes, error msg
        cmp cl, 70      ; cl > F
        jg  check_case  ; yes, error msg

        jmp prntMsg2    ; print value of input

check_case:             ; check case of input
        cmp cl, 97      ; cl < a
        jl  end_if      ; yes, error msg
        cmp cl, 102     ; cl > f
        jg  end_if      ; yes, error msg

        jmp to_upper    ; need to send to function to convert to upper case
                        ; then pass to prntMsg2

to_upper:
        and al, 223 ; convert to upper case(0DFh)

        jmp prntMsg2

end_if:                 ; error message if invalid input
        mov ah, 9
        mov dx, errMsg  ; print error message
        int 21h

        jmp continue    ; get a new value

prntMsg2:               ; print second message*****
        mov dx, msgOut  ; offset of second message
        mov ah, 9       ; print string function
        int 21h         ; display message

convert:
        mov cx, 00
        mov dx, 00

    L6: mov ax, 00
        mov al, [HNUM]
        div word [D]
        mov [HNUM], al

        mov bx, ax
        mov cl, 0
        mov ax, 1
    L5: 
        cmp cl, 00
        je L7
        mul word [H]
        sub cl, 1
        jmp L5
    L7: 
        mul bh
        add dx, ax
        add word [CNT], 1
        cmp word [HNUM], 0
        jg L6
        mov [NUM], dl

        jmp display

display:                ; display character
        mov dl, al
        mov ah, 2       ; print char function
        int 21h

        mov ah, 9       ; see if user wants to do it again
        mov dx, msgOpt
        int 21h

        mov ah, 1
        int 21h
        mov bl, al
        cmp bl, 'y'     ; bl = y
        jne exitPrg     ; no, end

        jmp continue    ; get a new value

exitPrg:                ; exit program
        mov ah, 4ch     ; exit to DOS function
        int 21h         ; see you later!

The code above has been edited and now compiles and runs. However, it is still not doing the conversion from hex to decimal correctly. It simply doesn't display a value, just blank. It will also hang up if a letter is entered, even a letter a-f. A number does not hang it up, but still a value is not displayed.

Now that I have it running at least I can work on fixing it, however, any guidance is appreciated. Thanks Gene for getting me up and running.

2条回答
Luminary・发光体
2楼-- · 2019-07-25 04:35

NASM memory operands use square brackets to denote dereferencing. So for example you will want:

    mov al, [HNUM]
    div byte [D]
    mov [HNUM], al

The NASM Manual explains this. RTFM!

Without the brackets, the labels are treated as immediate operands equal to the address of the memory location. The first line is no syntax error, but causes al to be loaded with the low byte of the address of HNUM. Not what you wanted. The div is an error because 8086 has no instruction to divide by an immediate quantity. And the mov is nonsense because you can't write to an immediate value.

So the error messages are telling you what's wrong. In the lines referenced, the operands do not comport with their instructions.

Addition

I went ahead and installed dosbox and NASM. Indeed NASM is not quite as smart as MASM about inferring operand types. So for the div instruction you need byte, (not word) as now reflected above. I could not grok your algorithm. It's more complicated than necessary. Here is my version:

; This program gets a hex digit from user and prints it out as a decimal 

        org 100h        ; program start point

section .data

msgIn:  DB      13, 10, "Enter a hex digit or q to quit: $"
msgErr: DB      " isn't hex. Must be 0-9, A-F, or a-f.$"
msgOut: DB      " has decimal value $"
buffer: DB      "xxxxx"
endBuf: DB      ".$"
ten:    DB      10

section .text

continue:               ; start user interaction
        mov dx, msgIn   ; offset address of message to display
        mov ah, 9       ; print string function
        int 21h

get_hex_digit:
        mov ah, 1
        int 21h         ; read character into al

check_for_quit:
        cmp al, 'q'     ; handle quit character
        je exit
        cmp al, 'Q'
        je exit

check_for_digit:
        cmp al, '0'     ; handle 0-9
        jl check_for_upper
        cmp al, '9'
        jg check_for_upper
        sub al, '0'     ; convert to numeric value
        jmp print_decimal

check_for_upper:
        cmp al, 'A'     ; handle A-F
        jl check_for_lower
        cmp al, 'F'
        jg check_for_lower
        sub al, 'A'-10  ; convert to numeric value
        jmp print_decimal

check_for_lower:
        cmp al, 'a'     ; handle a-f
        jl handle_digit_error
        cmp al, 'f'
        jg handle_digit_error
        sub al, 'a'-10  ; convert to numeric value

print_decimal:          ; print al contents as decimal 0-255
        mov di, endBuf  ; set buffer pointer to char after digits
next_digit:
        dec di          ; advance buffer pointer to next char
        xor ah, ah      ; clear high byte of ax for division
        div byte [ten]  ; ah = ax % 10, al = ax / 10
        add ah, '0'     ; convert ah to ascii
        mov [di], ah    ; copy to buffer
        or al, al       ; set condition codes with al
        jnz next_digit  ; jump if more digits to print

print_digits:
        mov dx, msgOut  ; offset address of message preamble
        mov ah, 9       ; print string function
        int 21h
        mov dx, di      ; offset address of converted digits
        mov ah, 9       ; print string function
        int 21h

        jmp continue    ; otherwise, get next input

handle_digit_error:
        mov dx, msgErr  ; offset address of message to display
        mov ah, 9       ; print string function
        int 21h
        jmp continue

exit:                   ; exit program
        mov ah, 4ch     ; exit to DOS function
        int 21h         ; see you later!
查看更多
Rolldiameter
3楼-- · 2019-07-25 04:37

check_for_upper:

    cmp al, 'A'     ; handle A-F
    jl check_for_lower

this should go be:

    jl handle_digit_error

as Ucase "A" is 41h, and Lcase "a" is 61h

查看更多
登录 后发表回答