8086 Assembly (TASM): Displaying an ASCII character value as HEX

44,997

Solution 1

Your logic for base 10 conversion looks good: each remainder from dividing by 10 will give you a digit, and they will be ordered from the least significant digit to the most significant one, so you need to reverse the obtained digits before printing them. Pushing them to a stack and popping them when done will do the reversing in a nice and compact way.

For converting to hex, the operation is the same: divide by 16, get the remainder, reverse, and print. However, there are a few things to note: dividing by 16 is shifting right 4 bits. Remainder is value AND 0fH, so you don't actually need any arithmetic operations. Bit shifts and AND operation is sufficient. Moreover, you don't even need to involve the stack. You can do this from most significant nibble (four bits) to the least significant one, and print as you calculate them. The second thing that you seem to be struggling with is converting a nibble to a hex digit. Simply adding 30h is not enough. It is good enough for decimals, but for hex values, you've got the numbers 10 to 15 as well. These need to be added to 41h ('A' in ASCII) minus 10 instead. Alternatively, you can put the digits '0' to '9' and 'A' to 'F' in a table, and index them by the value of the nibble you want to print.

You may also want to write a routine that will read a number from input. Read each digit until you read a newline, convert character to decimal value, and update an accumulator that will keep the number you've read. You will need to use this for both hex and decimal output functions.

Solution 2

Write your program in C or some other language first. dont use the languages libraries, for example dont use printf("%x\n",number) to display hex, but take the number do the repeated divide by 10s as you suggest, saving them somewhere. Now remember the remainders (modulo) from dividing by ten is something between 0-9 to display this somewhere in ascii you need to add 0x30 as you mentioned. 123 becomes 12 remainder 3, then 12 becomes 1 remainder 2, and 1 becomes 0 remainder 1, 1+0x30 = 0x31 display that 2+0x30 becomes 0x32 display that, 3+0x30 becomes 0x33 display that.

hex is no different, the base is 16 not 10. As vhallac mentions though you can mask and shift instead of using the divide operator.

Once you have the "program" working in C or some other language, at a low level using basic operations (add, sub, divide, shift, and, or, etc) then re-write that code in assembly language.

Eventually you may get to the point where you dont need to prove out your program in some other language and start with asm.

Share:
44,997
SalarianEngineer
Author by

SalarianEngineer

Holding the line, one reaper at a time.

Updated on July 20, 2022

Comments

  • SalarianEngineer
    SalarianEngineer almost 2 years

    ** Edited for clarification and "cleaner" code.

    I'm trying to accept a character from the keyboard (any character) and convert it's ASCII value to hex, then display it.

    I know how to convert from base 10 to hex, but just to make sure I'm not using incorrect terminology:

    If I enter "c" in as my ASCII value, it's decimal value is 63. 63 divided by 16 (hex is base 16) = 3.9375. Save the quotient of 3 for later. Remainder * base (.9375 * 16) = 15. 15 is Hex character "F".

    Quotient divided by base (3 / 16) = 0.1875. Quotient is zero, so this is the last step in conversion. Remainder * base (.1875 * 16) = 3

    Read them from last to first ("first in, last out", if thinking about the stack), and I get "3F" as the Hex value for the decimal number "63" (which is ASCII for "c")

    This is correct, yes?

    It's pretty simple to read, I hope. I just get the character from the keyboard (AL), set BX as the divisor (base 16), and then divide AL by BX, store the remainder in the stack, and later on loop through the stack and try to display it...

    I think my problem is that something is wrong with my multiplication and with my INT 21H/02H. I'm not sure if I need to add 30h in order to display the character or not...

    On top of that, I'm not even sure if I need to be at that point (displaying) yet anyway, because I still haven't figured out how to convert 10-15 to A-F for hex.

    I tried speaking with my teacher, and after waiting 30 minutes for him to finish speaking to another gaggle of students in my class (about something from another one of his classes, and not this one...which irked me quite a bit), the most that I got was "start over".

    When asked about "what about using SHR and SHL?" as it was pointed out to me, but I was told that it can be done without that, and that I can't use those commands (they haven't been covered in the class).

    Any input on what I'm doing wrong? Thanks!

        CHAR2HEX PROC                   ; Accept a character, print it's ascii value in hex.
    
            MOV DX, OFFSET AskChar      ; Display prompt
            MOV AH, 09H
            INT 21H
    
            MOV AX, 0                   ; Clear AX
    
            MOV AH, 07H                 ; Get keyboard input w/ no echo (AL)
            INT 21H
    
            MOV BX, 16                  ; Set up the divisor (base 16)
            MOV CX, 0                   ; Initialize the counter
            MOV DX, 0                   ; Clear DX
    
            Divide:                         
                                        ; Dividend (what's being divided) in DX/AX pair, Quotient in AX, Remainder in DX.
                DIV BX                  ; Divide (will be word sized).
                PUSH AX                 ; Save DX (the remainder) to stack.
    
                ADD CX, 1               ; Add one to counter
    
                MOV DX, 0               ; Clear Remainder (DX)
                CMP AX, 0               ; Compare Quotient (AX) to zero
                JNE Divide              ; If AX not 0, go to "Divide:"
    
            Multiply:
                                        ; Multiply remainder (from stack) by 16 to get hex value.               
                MOV DX, 0               ; Clear DX           
                POP AX                  ; Get remainder from stack into AX.
    
                MUL BX                  ; Multiply AX * BX.  (DX= high order bits, AX = low order bits)
                MOV DX, AX
    
                SUB DL, 30h             ; ADD 30h to DL 
                MOV AH, 02h             ; 02h to display AH (DL)
                INT 21H                 ; Send to DOS
    
                LOOP Multiply           ; If more to do, Multiply again
                                        ; LOOP subtracts 1 from CX. If non-zero, loop.
                RET
        CHAR2HEX ENDP
    END START
    

    EDITED **

    I finally got it! I was able to get the program to return the hex value of the ascii char that was pushed on the keyboard, but it only worked if each remainder was 0 through 9. It wouldn't display the A through F, and instead used colons, semicolons, etc...

    I looked at an Ascii/Deicmal/Hex chart online, and noticed that the characters 0 through 9 are 30h through 39h. But character A (for hex 10) doesn't start until 40h. So I changed the program so that if the value was greater than 39h, it added 7h to DL and THEN display it.

        CHAR2HEX PROC                   ; Accept a character, print it's ascii value in hex.
    
            MOV DX, OFFSET AskChar      ; Display prompt
            MOV AH, 09H
            INT 21H
    
            MOV AH, 07H                 ; Get keyboard input w/ no echo (AL)
            INT 21H
    
            MOV CL, AL                  ; Copy user input (AL) to CL
            MOV AX, 0                   ; Clear AX (get rid of HO bits)
            MOV AL, CL                  ; Copy user input back into AL
    
            MOV BX, 16                  ; Set up the divisor (base 16)
            MOV CX, 0                   ; Initialize the counter
            MOV DX, 0                   ; Clear DX
    
            Div2:                         
                                        ; Dividend (what's being divided) in DX/AX pair, Quotient in AX, Remainder in DX.
                DIV BX                  ; Divide (will be word sized).
                PUSH DX                 ; Save DX (the remainder) to stack.
    
                ADD CX, 1               ; Add one to counter
                MOV DX, 0               ; Clear Remainder (DX)
                CMP AX, 0               ; Compare Quotient (AX) to zero
                JNE Div2              ; If AX not 0, go to "Div2:"
    
            getHex2:
                MOV DX, 0               ; Clear DX.
                POP DX                  ; Put top of stack into DX.
                ADD DL, 30h             ; Conv to character.
    
                CMP DL, 39h
                JG MoreHex2
    
            HexRet2:        
    
                MOV AH, 02h             ; 02h to display AH (DL)
                INT 21H                 ; Send to DOS
    
                LOOP getHex2            ; If more to do, getHex2 again
                                        ; LOOP subtracts 1 from CX. If non-zero, loop.
                JMP Skip2
            MoreHex2:
                ADD DL, 7h
                JMP HexRet2             ; Return to where it left off before adding 7h.
            Skip2:
                RET
        CHAR2HEX ENDP
    
    • SalarianEngineer
      SalarianEngineer about 12 years
      I can get my program to convert from ASCII to HEX, but only if each value is 9 or less. So, for instance, using "a" as input results in "61" printing to the screen. "b" results in "62". But if I type in "K", it results in "4;", and doesn't actually convert that last digit to "A-F"... That's what I can't figure out, currently. This stuff is hard!
    • vhallac
      vhallac about 12 years
      You want to code if (a<= 9) a += 0x30 else a += 0x41 (assuming a is always between 0 and 15) to get those numbers. It is not really hard, but tedious. :)
  • SalarianEngineer
    SalarianEngineer about 12 years
    Umm...not quite sure how to post code in a reply, so I'll just make a comment on my own post I guess? Bottom line: I wrote a console app in C#, asking for a number. I then divided that number by 10, saving the remainder and quotient in different variables, outputting the remainder to a List<int> (my "stack"). After all was done, I had a loop go through the List and add 30 to each value, append a "h", and print that out. I'm not sure if that was a "shortcut" or not (adding the "h") as I'm not sure how to do anything with Hex in C#.
  • SalarianEngineer
    SalarianEngineer about 12 years
    Vhallac, my apologies if this is redundant, but I posted new code up top in the original post (with an edit)...I wasn't sure if it notifies you of this or not, but if you have a moment could you take a look?
  • SalarianEngineer
    SalarianEngineer about 12 years
    Of particular difficulty is when you wrote about "value AND 0fh"...I'm not familiar with this time at all.
  • vhallac
    vhallac about 12 years
    You seem to try and put the whole program together without making sure each piece works fine. If I were you, I'd start off with each subroutine, starting off with printing either the decimal or hex value, and test it with constant data (not read from keyboard). Once you have these building blocks, you can add reading from keyboard, etc. Also dwelch's answer below is very sensible: you sort out the algorithm first using a language you are comfortable with.
  • vhallac
    vhallac about 12 years
    As for the code you've added, it has a lot of problems (both on the algorithm and code level). An example of algorithm problem is, you are multiplying your digits with 16 before printing. Code level problem is where you want to push DX, but push AX instead. As for AND 0fh bit, don't worry about it. It is an optimization, and you don't need it yet. I'd suggest you finish the code with division first, then look into it.
  • SalarianEngineer
    SalarianEngineer about 12 years
    I understand that the code isn't as clean as it COULD be, but I can only work with what I know how to use, as it's a class, and some teachers don't like any deviation from the topics they've been teaching (otherwise I would have used a DB at the top of the program as an array, and cycled through that to print what I wanted...which looks like it would have worked if I had been allowed to try it). At first, I was doing decimal > hex conversion, but I misunderstood the problem and was told to start over, but focus only on the ascii > hex conversion. Good idea, because the dec > hex this codel
  • vhallac
    vhallac about 12 years
    Looks like you've got it. Congratulations. :) My comments were not about code clarity, but correctness. I like the fact that you managed to add 7 instead of doing it the naive way. One possible clean up would be to jump over the add when value is less than 0x3a instead of jumping out, and jumping back in. It would make the code easier to follow. If you want to keep it like this, you can move the two lines after RET, and get rid of Skip2 usage. Best of luck in the rest of your course.
  • SalarianEngineer
    SalarianEngineer about 12 years
    The "naive way" being...just doing the math and hoping that it ends up at the correct answer?
  • vhallac
    vhallac about 12 years
    No. It was the comment I made under your question in C. I was hoping you'd convert it into the simplified version of what you have now - because you'd require more branching to implement it that way.
  • SalarianEngineer
    SalarianEngineer about 12 years
    Vhallac, Thank you! So far in Assembler, I find much of the information just doesn't "stick", mostly because of the way new programmers are educated these days - no math required, critical thinking left at the door (myself included). I taught myself BASIC, COBOL, ColdFusion/ASP, and recently C#. It takes a different way of thinking, in my opinion, to be successful in Assembler. How much math did I use in all that? Not much, and the VB/HTML/JS/ASP was for large companies. My teacher REQUIRES code like this. Is the "naive way"...just doing the math and hoping for the correct answer?
  • SalarianEngineer
    SalarianEngineer about 12 years
    Right now I'm working on the "decimal to hex" conversion (that was originally in this question before the instructor told me to focus on the ASCII conversion first). Turns out, it's not that hard...change the base for BX from 16 to 10, convert the decimals that way and then run it through the same code as what I posted above. I need to find out if we're allowed to use the ASM equivalent of "Includes", because that would definitely make life easier, haha!