Search code examples
assemblyx86masm

Print ASCII char and its corresponding hexadecimal code using multiple modules in x86 DOS ASM


I have an assignment for tomorrow and I'm losing my mind I don't know how to do this. Basically I have to print all chars from position 32 to 127 and their respective code in hexadecimal format near them. Something like this:

[space char] 20h
! 21h
" 22h
etc

Until now I have only managed to do this: https://i.sstatic.net/UneFx.png

The thing is I don't know how to convert from a decimal value to a printable (so I have to print for example if I want to print the character '3' I would need to print the value 33h, etc.) hexadecimal value.

10l9.asm:

; 10. Print on the screen, for each number between 32 and 126, the value of the number (in base 16) and the character whose ASCII code the number is.

assume cs:code, ds:data

data segment public
nr db 32
hex db 20h
data ends

code segment public
extrn   tipar:proc
start:
mov ax, data
mov ds, ax

lea si, nr
cld
bucla:
    cmp nr, 127
    je final
    mov ah, 0
    mov al, nr
    mov bh, 0
    mov bl, hex
    call tipar
    inc nr
    inc hex
jmp bucla
final:
mov ax, 4C00h
int 21h
code ends
end start

print.asm:

    assume cs:code, ds:data
data segment public
    buffer dw 15 dup (?)
    tmp db 5 dup (?), 13, 10, '$'
data ends

code segment public
public tipar    ; the subprogram 'tipar' is made visible to other modules too
tipar:
; input: ax = the number that has to be printed on the screen
; the subprogram prints the number on the screen
; it does not modify the registers, except for ax

; we save the registers so that we can use them inside the subprogram
    push cx
    push dx
; we compute the representation on the number in base 10
    ;mov bx, offset tmp+5  ; bx=the address of the least written digit
    ;mov cx, 10 ; cx = 10 (constant)
    mov si, 0
    mov buffer[si], ax
    mov cx, 5
    spatiere:
        inc si
        mov buffer[si], 32
        loop spatiere       
    inc si
    mov bh, 0
    mov buffer[si], bx
    inc bx
    inc si
    mov buffer[si], 104
    inc si
    mov buffer[si], 13
    inc si
    mov buffer[si], 10
    inc si
    mov buffer[si], '$'

    mov dx, offset buffer
    mov ah, 09h
    int 21h

    pop dx
    pop cx
    ret

code ends
end

i've managed to do it (well partially) .. i'll post below how i did it in case someone has a similar problem:

mov bx, offset tmp+5
mov cx, 16
bucla:
    mov dx, 0
    div cx
    dec bx
    add dl, '0'
    mov byte ptr [bx], dl
    cmp ax, 0
    jne bucla

Solution

  • To convert a byte (in AL) to two bytes corresponding to the ASCII codes for the respective nibbles (in AX, little endian (high nibble in AL)), you can use this code (GNU as):

            .intel_syntax noprefix
            .code16
    c2hex:
            mov     ah,al
            shr     al,4
            and     ah,0x0F
            add     ax,0x3030
            cmp     al,0x39
            jbe     1f
            add     al,7
    1:      cmp     ah,0x39
            jbe     2f
            add     ah,7
    2:      ret
    

    The output is suitable for STOSWing into a string, for example.

    Like numbers/digits, letters are sequential. The difference between 0x3A (0x30 + 10) and 'A' (0x41) just happens to be 7, which is where that number in the second-to-last line comes from. (The line two up, at the 1: label, checks for a letter.)