I\'m using the 10h interrupt with AH as 0Eh to output \"Hello World!\" The text is ouputted but its not colored. I\'m running it on qemu-system-x86_64, assembling with NASM,
I think a better idea than the one marked as accepted is to use function 13h of interrupt 10h. This function is supposed to send entire strings to the screen, along with attributes (colors). There are four modes of operation for this function, specified in the AL
register.
AL=00h: Assign all characters the attribute in
BL
; do not update the cursor position.
AL=01h: Assign all characters the attribute inBL
; update the cursor position.
AL=02h: Use attributes in string; do not update the cursor position.
AL=03h: Use attributes in string; update the cursor position.
So you can either use the same attribute (colors) for the entire string by specifying the attribute in BL
for modes 00h
or 01h
, or intermingle the attributes in the string itself to print each character with a different attribute.
The only drawback I see with this approach is that you have to know the length of the string upfront (to put it into CX
), since this function doesn't work with null-terminated strings.
But, on the other hand, storing a one-byte length of the string preceding its characters instead of the null character after its characters could have some benefits: You don't need to traverse the entire string to know its length; and a one-byte length won't take any more place than the null terminator. Even a two-byte length is not much a waste. You can load a two-byte length directly into CX
. You can do the same with a one-byte length, too, but make sure to clear out the CH
afterwards.
Having the ability attribute each character of the string alone also could be useful sometimes.
Other registers are as usual:
BH
is the page number.
DX
is where to start the string on the screen: DH:DL = Y:X
ES:BP
points to the first character of the string in memory.
If you use the teletype modes (01h
or 03h
), ASCII control characters are properly interpreted instead of being printed as symbols. They also update the cursor position to the end of the string.
To make it work continuously, you can use the function AH=03h
to get the cursor position. It is made so that it loads the cursor position to the DH:DL
, so it can be used afterwards directly in the subsequent call to AH=13h
to print the string from that position. Here's how I do it:
# Get cursor position.
getcur: mov $0x03, %ah # Get cursor position into DH:DL = Y:X.
int $0x10 # Video BIOS interrupt.
ret # Return to the caller.
# Print string with attributes.
# `putsa` expects attributes in `BL`.
# `puts` uses the default attributes (silver on black).
# Both expect a pointer to the string in `ES:SI`.
# The string should start with a 2-byte length information.
puts: mov $0x07, %bl # Default attribute: silver on black.
putsa: call getcur # Get cursor position into DH:DL.
mov (%si), %cx # Length of the string into `CX`.
mov %si, %bp # Prepare the pointer:
add $2, %bp # Skip the 2-byte length word.
mov $0, %bh # Use page #0.
mov $0x1301, %ax # Print string and update cursor.
int $0x10 # Video BIOS interrupt.
ret # Return to the caller.
Calling (assuming the ES
is properly set):
# Print a string with attributes.
lea msgHello, %si # String to print (after 2-byte length)
mov $0x0C, %bl # Attributes: light red on black.
call putsa
# Print it one more time with different attributes.
# Note we don't have to set the pointer: it's already set.
mov $0x0C, %bl # Attributes: yellow on black.
call putsa
The data section:
msgHello: .word 13 # Length of the string.
.ascii "Hello, World!" # The string itself.
Oh, and service is available only for XTs dated 01/19/1986 and later, ATs, EGAs, and PC Convertibles. But I guess it won't pose any problem, unless you're dealing with a serious piece of old junk ;-J