3

I actually want to print the content of the dx register with nasm. Thereby the content is a 16 bit hex digit such as 0x12AB.

Therefore I've first implemented a function which is able to print a string:

print_string:
    pusha
    mov ah, 0xe

print_character:
    mov al, [bx]
    inc bx
    or al, al
    jz print_done
    int 0x10
    jmp print_character

print_done:
    popa
    ret

You can use this function in this way:

mov bx, MSG
call print_string

MSG:
    db 'Test',0

Now i want to have a function, which converts the hex to a string, so that print_string is capable to print it. I was thinking about something like that:

print_hex:
    pusha
    mov bx, HEX_OUT
    ; HEX_OUT is a kind of template string
    ; now i want to get the hex of dx into this template in order to be able to print it
    ; However I'm not sure how to manage this
    call print_string
    popa
    ret

HEX_OUT:
    db '0x0000', 0

Unfortunately I'm not sure how I get the hex from dx into bx, respectively the HEX_OUT. Can someone help me or does someone have an idea?

I want to use it at the end like this:

mov dx, 0x12AA
call print_hex

Thanks you already in advance!

UPDATE:

As mentioned I could achieve the separating and printing like this:

print_hex:
    pusha
    mov bx, PREFIX
    call print_string

next_character:
    mov bx, dx
    and bx, 0xf000
    shr bx, 4
    add bh, 0x30
    cmp bh, 0x39
    jg add_7

print_character_hex:
   mov al, bh
   mov ah, 0x0e
   int 0x10
   shl dx, 4
   or dx, dx
   jnz next_character
   popa
   ret

add_7
   add bh, 0x7
   jmp print_character_hex

PREFIX:
   db '0x', 0

I tried something like this to print it with my function and the buffer:

print_hex:
    ;Added this here    
    mov cx, HEX_OUT + 2

print_character_hex:
    mov [cx], bh

Though I can't assemble this due to "invalid effective address". What do I need to do in order to accomplish this?

fr33g
  • 2,249
  • 1
  • 12
  • 12
  • What have you tried so far to split a number into hexadecimal digits? (Excluding effort on the `print_string` subroutine; you might as well have used DOS function 09h.) – Ruud Helderman Dec 24 '14 at 18:53
  • You can isolate hex digits by shifting right by multiples of 4 bits and ANDing with 0x0F. Then map that value to a character that you place in your string buffer. Repeat until you've covered all the digits. – Michael Dec 24 '14 at 19:59
  • 1
    For to seperate at first the high nibble(half byte) we can use the "rol dx, 4"-instruction. And then we can move the lowest byte into a second register for ANDing the values with 0x0F.Now we can convert the value to ASCII with adding 0x30 and for all numbers from 10 to 15 we have to add the number 7 for to get the letters from A to F. – Dirk Wolfgang Glomp Dec 24 '14 at 22:48
  • Thank you for your responses. What I've accomplished so far is to isolate the hex digits and also to convert it into the corresponding ascii code. I can also print this then per digit. However I'd like to use the string buffer and my string print function. Right now I'm not able to put the ascii into my buffer. Could you help me with that as well? I've updated the post above. – fr33g Dec 25 '14 at 00:31
  • Ok, I was able to do it. I couldn't use cx for that purpose. Instead I had to use si as the indexing register. – fr33g Dec 25 '14 at 00:38
  • Your update 2 solution has a drawback. It can only correctly convert once! See what happens if the first number is 0x12AB and then a second number of say 0x7C00. Either zero HEX_OUT each time or always perform 4 iterations in stead of bailing out on DX becoming 0. – Sep Roland Dec 28 '14 at 12:33
  • I haven't tried your precise instructions. However I'm using it in my program currently and it's working with more than one conversion!? – fr33g Dec 29 '14 at 10:03
  • Related: [How to convert a binary integer number to a hex string?](//stackoverflow.com/q/53823756) shows how to use a rotate to make it easy to get nibbles 1 at a time, starting with the highest. – Peter Cordes Dec 29 '19 at 00:38

3 Answers3

2

This adapts the accepted answer to include Kyle G.'s fix for the 0 issue.

; vim: :set ft=nasm:

[bits 16]

; subroutine to print a hex number.
;
; ```
; dx = the hexadecimal value to print
; ```
;
; Usage
;
; ```
; mov dx, 0x1fb6
; call print_hex
; ```
;
; used as an answer here: https://stackoverflow.com/a/27686875/7132678
;
print_hex:
        ; push all registers onto the stack
        pusha

        ; use si to keep track of the current char in our template string
        mov si, HEX_OUT + 2

        ; start a counter of how many nibbles we've processed, stop at 4
        mov cx, 0

next_character:
        ; increment the counter for each nibble
        inc cx

        ; isolate this nibble
        mov bx, dx
        and bx, 0xf000
        shr bx, 4

        ; add 0x30 to get the ASCII digit value
        add bh, 0x30

        ; If our hex digit was > 9, it'll be > 0x39, so add 7 to get
        ; ASCII letters
        cmp bh, 0x39
        jg add_7

add_character_hex:
        ; put the current nibble into our string template
        mov [si], bh

        ; increment our template string's char position
        inc si

        ; shift dx by 4 to start on the next nibble (to the right)
        shl dx, 4

        ; exit if we've processed all 4 nibbles, else process the next
        ; nibble
        cmp cx, 4
        jnz next_character
        jmp _done

_done:
        ; copy the current nibble's ASCII value to a char in our template
        ; string
        mov bx, HEX_OUT

        ; print our template string
        call print_string

        ; pop all arguments
        popa

        ; return from subroutine
        ret

add_7:
        ; add 7 to our current nibble's ASCII value, in order to get letters
        add bh, 0x7

        ; add the current nibble's ASCII
        jmp add_character_hex

; our global template string. We'll replace the zero digits here with the
; actual nibble values from the hex input.
HEX_OUT:
        db '0x0000', 0

Also, worth noting that this is an exercise from https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf, section 3.5.1, question 5, page 23.

jethro
  • 168
  • 3
  • 13
  • `and bx, 0xf000` is not needed; `shr bh,4` or `shr bx,4` already shifts in zeros, so the nibble we want is isolated at the bottom of BH. Interesting partial-register hack to do this instead of `rol dx, 4` before copying to a tmp reg and masking the bottom nibble. (One of the good options for 32-bit mode where there's no byte reg for the top byte: [How to convert a binary integer number to a hex string?](https://stackoverflow.com/q/53823756)). Also `jmp _done` is pointless, it's already the next instruction. – Peter Cordes Apr 28 '22 at 05:06
  • Oh, I see that partial-register trick and the unnecessary `and` were already in the original, and you didn't improve the efficiency any. (Another thing you could do is move the `add bh, '0'` *after* the `jg add_7`; if you're already going to jump out-of-line and come back, you can just add two different constants, `'0'` or `'A'-10`, so either path of execution only goes through one add instruction. Adding 7 (`'A'-10 - '0'`) is only a good choice if you weren't putting any block at the end, so it's either a taken branch or a not-taken and an add, to avoid a `jmp` to skip an else part.) – Peter Cordes Apr 28 '22 at 05:18
-1

All right, I was able to manage it. Thank you for your help! Here is the working code:

print_hex:
   pusha
   mov si, HEX_OUT + 2

next_character:
  mov bx, dx
  and bx, 0xf000
  shr bx, 4
  add bh, 0x30
  cmp bh, 0x39
  jg add_7

add_character_hex:
  mov al, bh
  mov [si], bh
  inc si
  shl dx, 4
  or dx, dx
  jnz next_character
  mov bx, HEX_OUT
  call print_string
  popa
  ret

add_7:
  add bh, 0x7
  jmp add_character_hex

HEX_OUT:
  db '0x0000', 0
fr33g
  • 2,249
  • 1
  • 12
  • 12
  • 1
    This will eventually fail when 16-bit words end with 1 or more 0s. Try calling this twice, the first time with `mov dx,0x1fb6` and the second time with `mov dx,0x2000`. It will print out `0x1FB6` then `0x2FB6`. To correct this, use ax (or any other unused register) as a counter to force the loop to run 4 times. – Kyle G. Jul 22 '15 at 19:15
  • @KyleG.: Or store a `0` byte after the loop: `mov byte [si], 0`. It does unconditionally do a first iteration (like a do{}while loop), so an input of `0` would still result in `'0x0'', 0, garbage` – Peter Cordes Apr 28 '22 at 05:12
-1

You can use this code to print a hex number

mov cx,12
  mov dx,1f34h 
  st: 
   push dx
   shr dx,cl  
   and dl,15
  print:
  add dl, 0x30 
  cmp dl, 0x39 
  jle go
  add dl, 0x7  
  go:
  mov ah, 0x2
  int 0x21  
  sub cl,4  
  pop dx  
  cmp cl,0
  jl end_code
  jmp st
  end_code:
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Fares
  • 1
  • Good SO answers should comment the code and/or include text to explain *how* they work, and why it's a good idea to do it this way. Also, you should indent your code one more level than your labels for good style. This code is actually not bad as far as efficiency. or at least code-size: printing with `int 0x21` is not going to be fast. – Peter Cordes Jan 12 '19 at 18:18