If you output via DOS (you're using DOS.PrintString function 09h), you loose the ability to produce color. Luckily BIOS does support colorful text. Read all about the available DOS and BIOS text output functions in Displaying characters with DOS or BIOS.
For the text video mode, you can use the WriteStringWithAttributeTVM code that expects DS:SI to point at your zero-terminated text and BL to contain the attribute to use (specifying the foreground color in the low nibble and the background color in the high nibble). Just like the DOS.PrintString function 09h, it will expand tabs.
; IN (bl,ds:si) OUT ()
WriteStringWithAttributeTVM:
pusha
mov bh, 0 ; Display page 0
jmp .d
.a: cmp al, 9
je .Tab
cmp al, 13
ja .b
mov cx, 1101_1010_0111_1111b
bt cx, ax
jnc .c ; {7,8,10,13} don't need the color
.b: mov cx, 1
mov ah, 09h ; BIOS.WriteCharacterAndAttribute
int 10h
.c: mov ah, 0Eh ; BIOS.Teletype
int 10h
.d: lodsb
test al, al
jnz .a
popa
ret
.Tab: mov cx, 1 ; Start displaying colored space(s)
mov ax, 0920h ; BIOS.WriteCharacterAndAttribute
int 10h
mov ah, 0Eh ; BIOS.Teletype
int 10h
mov ah, 03h ; BIOS.GetCursor
int 10h ; -> CX DX
test dl, 7
jnz .Tab ; Column not yet multiple of 8
jmp .d
There is an additional BIOS function that can display a string in color but it will not expand tabs like the DOS.PrintString function 09h does. This is also the solution in one of the other answers but over there it contains an error. A further drawback is that you need to know the length of the string beforehand and that it requires the weird ES:BP pointer. The only nice thing about it, is that you can specify the column and row in the call...
; IN (bl,cx,dx,bp) OUT ()
; BL Attribute eg. 1Fh is BrightWhiteOnBlue
; CX Length of the string
; DL Column
; DH Row
; BP Offset address of the string
WriteString:
push ax
push bx
push es
mov ax, ds ; DS:BP --> ES:BP
mov es, ax
mov bh, 0 ; Display page 0
mov ax, 1301h ; BIOS.WriteString
int 10h
pop es
pop bx
pop ax
ret
About mov ax, 1301h
: Because the WriteMode in AL is set to 1, the attribute is taken from the BL register AND the cursor gets updated (just like the DOS.PrintString function 09h would have done).