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