Search code examples
assemblyx86-16emu8086real-mode

Error while printing a string from assembly code


I'm new to assembly and the course that I follow uses EMU8086. I wrote this code to print Testing and the ASCII code of 50 which is number 2 but it only prints Testing and ignores the rest. What's wrong?

.model tiny
.code

org 100h


main proc

    mov ah, 09h
    mov dx, offset msg
    int 21h 
    mov ah, 4ch
    mov dx ,0  
    mov dl, 2
    add dl, 48
    int 21h      

endp

msg db "Testing$"

end main

After reading the comments from @500 - Internal Server Error, I updated my code to this:

.model tiny
.code

org 100h


main proc

    mov ah, 09h
    mov dx, offset msg
    int 21h 
    mov dl, 2
    add dl, 48
    int 21h      

endp

msg db "Testing$"

end main

It still isn't working, so what's wrong?


Solution

  • The DOS interrupt INT 21h/AH=9h doesn't take a character value to print, it takes the memory offset to the beginning of a $ terminated string to print.

    DOS 1+ - WRITE STRING TO STANDARD OUTPUT

    AH = 09h

    DS:DX -> '$'-terminated string

    Return:

    AL = 24h (the '$' terminating the string, despite official docs which state that nothing is returned) (at least DOS 2.1-7.0 and NWDOS)

    If you want to print out a single character with INT 21h/AH=9h then you'll need to move the value into a buffer that is terminated with a $ sign. You then pass the address of that buffer into INT 21h/AH=9h. Based on your second example something like this should work:

    .model tiny
    .code
    org 100h
    
    main proc
        mov ah, 09h             ; DOS Interrupt ah=9h print $ terminated string
        mov dx, offset msg      ; Address of msg 
        int 21h                 ; Int 21h/ah=9h Print msg 
        mov outchar, 48+2       ; Move ASCII val for `2` to outchar buffer
        mov dx, offset outchar  ; Address of the $ terminated outchar buffer in DX
        int 21h                 ; AH is still 9h, so this prints $ terminated string
    
        mov ax, 4c00h           ; Exit program with return value 0
        int 21h    
    endp
    
    msg db "Testing$"           ; msg string
    outchar db ?, "$"           ; output buffer for single character terminated with $
    
    end main
    

    Instead of mov outchar, 48+2 you could just use the ASCII value like this:

    mov outchar, '2'
    

    Alternatively you could do it with a single call to INT 21h/AH=9h by placing the character you want into the middle of an output buffer:

    main proc
        mov outchar, '2'    ; Place the ASCII value for '2' in the output buffer
        mov ah, 09h         
        mov dx, offset msg  
        int 21h             ; Print $ terminated string starting at `msg`
        mov ax, 4c00h
        int 21h             ; Exit with error code 0
    endp
    
    msg     db "Testing"
    outchar db ?, "$"
    

    The reason this works is because INT 21h/AH=9h will blindly print everything it finds starting at offset msg until it finds a $ terminating character. We effectively do the substitution of the character at outchar first so that when INT 21h/AH=9h is executed it will encounter Testing2$ in memory.

    Once it reaches the $ it will stop printing so the output would look like:

    Testing2


    You also have the option of using two different DOS (INT 21h) interrupts. While INT 21h/AH=9h prints a $ terminated string, INT 21h/AH=2h displays a single character:

    DOS 1+ - WRITE CHARACTER TO STANDARD OUTPUT

    AH = 02h

    DL = character to write

    Return: AL = last character output (despite the official docs which state nothing is returned) (at least DOS 2.1-7.0)

    You could code your program to display the msg string as you did, but then you can use INT 21h/AH=2h to display a single character. Your code could then look like this:

    .model tiny
    .code
    org 100h
    
    main proc
        mov ah, 09h             ; DOS Interrupt ah=9h print $ terminated string
        mov dx, offset msg      ; Address of msg 
        int 21h                 ; Int 21h/ah=9h Print msg 
        mov ah, 02h             ; DOS interrupt ah=2h print single character
        mov dl, '2'             ; DL = ASCII value of character to print
        int 21h                 ; Int 21h/ah=2h print single character in DL 
    
        mov ax, 4c00h           ; Exit program with return value 0
        int 21h    
    endp
    
    msg db "Testing$"           ; msg string
    
    end main