4

I'm doing an iso file in assembly and I want to add color to the text (in this case: red).
Does anyone know how to do it?

[BITS 16]
[ORG 0x7C00]

jmp main

main:
    mov si, string ; si=string
    call printstr
    jmp $

printstr:
    lodsb ; al=&si[0]
    cmp al,0 ;FLAGS = 0
    jnz print
    ret

print:
    mov  ah,0Eh
    int  10h
    jmp printstr

string db "HELLO WORLD!",13,10,0

times 510 - ($-$$) db 0
dw 0xAA55
Fifoernik
  • 9,779
  • 1
  • 21
  • 27
Flag
  • 41
  • 1
  • 3
  • 2
    You may wish to consider [Int 10/AH=09h](http://www.ctyme.com/intr/rb-0099.htm) – Michael Petch Mar 05 '19 at 18:42
  • 2
    Or you can write directly to the text-mode video-buffer at `B800h` which consists of char:attr byte pairs. [This SO question](https://stackoverflow.com/q/9653169/1305969) may be useful in that case. The 'attr' sets the text color foreground and background. – zx485 Mar 05 '19 at 18:58
  • Possible duplicate of [bootloader printing on video memory 0xb8000](https://stackoverflow.com/questions/22961979/bootloader-printing-on-video-memory-0xb8000) – Preston Hager Mar 06 '19 at 06:56

2 Answers2

5

As a preliminary advice, always setup the segment registers that your bootloader depends on. Here, because of lodsb together with [ORG 0x7C00], you must set DS=0.
Best also make sure the direction flag DF is in a known state. A simple cld will be enough.

To answer your question. The BIOS.Teletype function 0Eh that you use, can produce the desired red color but only while in a graphics video mode. Next solution will thus work:

[BITS 16]
[ORG 7C00h]
    jmp     main
    ...
main:
    xor     ax, ax     ; DS=0
    mov     ds, ax
    cld                ; DF=0 because our LODSB requires it
    mov     ax, 0012h  ; Select 640x480 16-color graphics video mode
    int     10h
    mov     si, string
    mov     bl, 4      ; Red
    call    printstr
    jmp     $

printstr:
    mov     bh, 0     ; DisplayPage
print:
    lodsb
    cmp     al, 0
    je      done
    mov     ah, 0Eh   ; BIOS.Teletype
    int     10h
    jmp     print
done:
    ret

string db "HELLO WORLD!",13,10,0

times 510 - ($-$$) db 0
dw      0AA55h

If however you want to work with the text video mode then BIOS.WriteCharacterWithAttribute function 09h is the right choice.

  • Pay attention because the parameters are different. BL now holds an attribute byte that specifies 2 colors at the same time (foreground in the low nibble and background in the high nibble) and an extra parameter uses the CX register.
  • Another point is that this function will show a colored glyph for every ASCII code. So the carriage return (13) and linefeed (10) will not get interpreted correctly unless you take measures.
  • The most important fact however is that this function does not advance the cursor. Luckily there's a neat trick. Just invoke both functions 09h and 0Eh in a row and voilà...

Example:

[BITS 16]
[ORG 7C00h]
    jmp     main
    ...
main:
    xor     ax, ax     ; DS=0
    mov     ds, ax
    cld                ; DF=0 because our LODSB requires it
    mov     ax, 0003h  ; Select 80x25 16-color text video mode
    int     10h
    mov     si, string
    mov     bl, 04h    ; RedOnBlack
    call    printstr
    jmp     $

printstr:
    mov     cx, 1     ; RepetitionCount
    mov     bh, 0     ; DisplayPage
print:
    lodsb
    cmp     al, 0
    je      done
    cmp     al, 32
    jb      skip
    mov     ah, 09h   ; BIOS.WriteCharacterWithAttribute
    int     10h
skip:
    mov     ah, 0Eh   ; BIOS.Teletype
    int     10h
    jmp     print
done:
    ret

string db "HELLO WORLD!",13,10,0

times 510 - ($-$$) db 0
dw      0AA55h
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • 4
    I'm the upvote, but I want to mention that if you are going to use the string instructions (LODSx/CMPSx/SCASx etc) you will also want to ensure the direction flag is set as you need it (forward in this code would need CLD of course). The BIOS doesn't guarantee the direction flag is cleared. On a side note: one of the emulators at one time had an interesting bug where if you used STD to reverse the direction, the Int 13h/Ah=2 disk reads failed LOL. – Michael Petch Mar 10 '19 at 22:39
2

You can use Int 10/AH:0x09. It has the same arguments as Int 10/AH:0x0E, except that BH is the text color. Simply add the following line to your code.

mov ah, 09h
mov bh, 0xF0     ; add this line, this is the text color (white on black)
int 10h

Another alternative that I use, since BIOS functions, aren't available in protected mode. Using the memory at 0x0B800. The general code then becomes:

mov ebx, 0xb800      ; the address for video memeory
mov ah, 0xF0         ; the color byte is stored in the upper part of ax (ah).
printstr:
  lodsb              ; load char at si into al and increment si.
  cmp al, 0
  je .done
  mov [ebx], ax      ; move the character into video memeory.
  add ebx, 2         ; move the video memeory pointer up two bytes.
  jmp printstr
.done:
  ret

Additional resources for looking into this may include:

Preston Hager
  • 1,514
  • 18
  • 33
  • In your first section of code did you mean `mov ah, 0Eh` or `mov ah, 09h`? – Michael Petch Mar 06 '19 at 22:05
  • @Michael I did in fact mean `mov ah, 09h`. Nice catch the answer has been edited to the correct number. – Preston Hager Mar 07 '19 at 15:15
  • 3
    Note that `Int 10/AH:0x09` has very different behavior to `Int 10/AH:0x0E` for control characters. Specifically, the former will display characters for things like carriage return, line feed and backspace; and the latter will move the cursor appropriately instead of displaying control characters. – Brendan Mar 11 '19 at 08:14