0

I am trying to write a simple assembly code to spit out hex values to the screen. There are two files print_screen.asm which is working with other modules. I think the problem is in my logic when trying to convert hex to string. My code is:

[org 0x7c00]

xor dx,dx
xor ax,ax
xor bx,bx

mov dx, 0x1fb6

call print_hex

jmp endi;

print_hex:
    pusha

    mov ax,0x0001
    and ax,dx
    add ah,48
    mov byte [HEX_OUT+5],ah

    mov ax,0x0010
    and ax,dx
    add ah,48
    mov byte [HEX_OUT + 4],ah

    mov ax,0x0100
    and ax,dx
    add ah,48
    mov byte [HEX_OUT + 3],ah

    mov ax,0x1000
    and ax,dx
    add ah,48
    mov byte [HEX_OUT + 2],ah

    mov bx,HEX_OUT
    call print_string

    popa
    ret

jmp endi

%include "print_string.asm"

endi:
;data
HEX_OUT: db '0x0000',0

SAMPLE: db 'a',0
times 510 - ($-$$) db  0
dw 0xaa55

print_screen.asm (working with other modules):

 print_string:
    pusha
    cld
    mov ah,0x0e

config: mov al,[bx]
    ;Comparing the strings
    cmp byte [bx],0x00  ;Comparing for null
    jne print
    je end

print:  int 0x10
    add bx,1
    jmp config

end:    popa
    ret
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Bipul Adh
  • 103
  • 1
  • 2
  • 6
  • You are only masking out 1 bit at a time instead of 4. Also, you leave it in place. – Jester Oct 22 '17 at 10:15
  • What do you think happens when you do `mov ax,0x0010; and ax,dx`? Put in some value for an example, and see what comes out. – Jester Oct 22 '17 at 10:17
  • @Jester i gotta use F. Dang stupid me. Thanks a lot. And infact keep shifting those values too. Thanks again. – Bipul Adh Oct 22 '17 at 10:24
  • @Jester is it possible for me to use a loop?? If possible how do i add offset to the HEX_OUT label. – Bipul Adh Oct 22 '17 at 10:27
  • Yes, you can use a loop. You can do something like `[HEX_OUT+bx]`. – Jester Oct 22 '17 at 10:40
  • Related: See [How to convert a number to hex?](https://stackoverflow.com/q/53823756) for efficient code for a 32-bit integer. – Peter Cordes Dec 23 '18 at 17:00

2 Answers2

3
mov ax,0x0001
and ax,dx
add ah,48
mov byte [HEX_OUT+5],ah

In the above snippet you only keep a single bit where you need to keep 4 bits.
You also do an addition on AH when the result definitely is in AL.
Because of how the ASCII set is organized, you can't just merily add 48 to convert into the hexadecimal. There is a gap between the encoding for '9' (57) and the encoding for 'A' (65). Your code needs to account for this!

For the least significant hex digit:

    mov ax, dx     ;Original number
    and al, 15     ;Keep 4 bits
    add al, '0'    ;Make text
    cmp al, '9'
    jbe .LessA     ;Already fine for '0' to '9'
    add al, 7      ;Bridge the gap to reach 'A' to 'F'
.LessA:
    mov [HEX_OUT + 5], al

For the next hexdigit this would become:

    mov ax, dx     ;Original number
    shr ax, 4
    and al, 15     ;Keep 4 bits
    add al, '0'    ;Make text
    cmp al, '9'
    jbe .LessA     ;Already fine for '0' to '9'
    add al, 7      ;Bridge the gap to reach 'A' to 'F'
.LessA:
    mov [HEX_OUT + 4], al

For the next hexdigit this would become:

    mov ax, dx     ;Original number
    shr ax, 8
    and al, 15     ;Keep 4 bits
    add al, '0'    ;Make text
    cmp al, '9'
    jbe .LessA     ;Already fine for '0' to '9'
    add al, 7      ;Bridge the gap to reach 'A' to 'F'
.LessA:
    mov [HEX_OUT + 3], al

For the next hexdigit this would become:

    mov ax, dx     ;Original number
    shr ax, 12
    and al, 15     ;Keep 4 bits
    add al, '0'    ;Make text
    cmp al, '9'
    jbe .LessA     ;Already fine for '0' to '9'
    add al, 7      ;Bridge the gap to reach 'A' to 'F'
.LessA:
    mov [HEX_OUT + 2], al

This rapidly got longer than is good for us, so using a loop will be much better.
Next solution will start from the high end but the end result will be no different.

    mov bx, 2      ;Position for most significant digit
.Next:
    ror dx, 4      ;Bring digit in lowest 4 bits
    mov al, dl     ;Copy number
    and al, 15     ;Keep 4 bits
    add al, '0'    ;Make text
    cmp al, '9'
    jbe .LessA     ;Already fine for '0' to '9'
    add al, 7      ;Bridge the gap to reach 'A' to 'F'
.LessA:
    mov [HEX_OUT + bx], al
    inc bx
    cmp bx, 6      ;Did we fill chars at +2 +3 +4 +5 ?
    jb  .Next      ;Not yet

Because there are 4 iterations in this loop and the number in DX is rotated 4x each time, DX will hold the original value in the end. No need to preserve it.


jmp endi;

What's this supposed to achieve? This is jumping to data and that's certainly not executable code! If you want an endless loop then simply write:

jmp $

The other file, that you say is working with other modules, is a mess!!
Everybody keeps neglecting this, but the BIOS teletype function requires the BH register to have the desired display page. Therefore it's always a bad idea to use BX as the string pointer.
Here's a good solution that doesn't require you to change all of your existing code (concerning the use of BX):

print_string:
    pusha
    mov     si, bx
    mov     bh, 0      ;Display page 0
    ;mov     bl, 7      ;Color if this were a graphical screen
    cld                ;Required to use LODSB correctly
    jmp     .start
  .write:
    mov     ah, 0x0E   ;BIOS.Teletype
    int     0x10
  .start:
    lodsb              ;Increments the pointer automatically
    cmp     al, 0      ;Comparing for null
    jne     .write
    popa
    ret
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
0

There's code on the Russian side of Stack Overflow https://ru.stackoverflow.com/questions/924141/%d0%a0%d0%b0%d0%b1%d0%be%d1%82%d0%b0-%d1%81-%d0%b4%d0%b8%d1%81%d0%ba%d0%be%d0%bc-int-13h-bios for conversion of hex byte to string that's cleaner. Here's the snippet:

mov bh, 0x00
mov bl, ah
shr bl, 0x04
mov al, [bx+hex_nums]
mov [error_code_hex], al
and ah, 0x0F
mov bl, ah
mov ah, [bx+hex_nums]
mov [error_code_hex+1], ah
hex_nums: db "0123456789ABCDEF"

error_code_hex is where it prints the hex character. It basically takes the 4 bit as a sequence to reference the bytes of the ASCII hex sequence. Has anyone made this work?

user145453
  • 137
  • 10
  • 1
    `and al, 0xF0` is redundant before `shr al, 4`. The bits masked away are about to be shifted out. It's also pointless to use AL instead of doing `mov bl, ah` / `shr bl, 4`, saving the `mov bl,al` instruction. – Peter Cordes Dec 23 '18 at 16:57
  • That's better. What do you mean "Has anyone made this work?" Are you trying to ask a new question? Looks totally normal for converting AH to a 2-digit hex string. Except that `hex_nums:` can't be in-line with the code, or those 16 bytes will be decoded as instructions!. You need to put the lookup table in its own section, or after your other code. – Peter Cordes Dec 23 '18 at 18:35
  • @PeterCordes you can have it in code section if you do jump before. – Алексей Неудачин Jan 11 '21 at 11:46
  • @АлексейНеудачин: Yes, of course, but there's [little to no benefit to mixing code and data](https://stackoverflow.com/a/55609077/224132) on x86, just the downside of having to `jmp` over it. Which this code doesn't do. I did mention the "after your other code" alternative to another section. – Peter Cordes Jan 11 '21 at 12:18
  • Related: [How to convert a binary integer number to a hex string?](https://stackoverflow.com/q/53823756) – Peter Cordes Jan 11 '21 at 12:18
  • @PeterCordes if you have .data at all. say in bootloaders you don't – Алексей Неудачин Jan 11 '21 at 12:24