Search code examples
assemblyx86kernelosdevvga

How to change the foreground color of a string (32 Bit Assembly kernel)?


I am currently programming my own operating system (just for fun, I am 16) and have a problem with the outprint function i have crated. I want to change the text color (not the background color) but it won't work.

I have crated my own printf function SystemOutPrint/ln (named after Java cause thats my "main" language, for those who wounder) and figured out that I can change the background color by writing in the AH register. Before this function, i did nothing in the Kernel at all and the bootloader only sets the GDT, LDT and the mode from 16 to 32 bits. So the video memory is untouched until the mov ebx, 0xb8000.

The relevant code:

kmain:
mov ebx, 0xb8000    ;Video Memory
mov esi, kernelVersion
mov ah, 0x0
;setting ah to 0x0 is not neccessary, because its the default but if you
;would put in A for example it would be light green, etc.
call SystemOutPrintln

SystemOutPrintln:
mov ecx, ebx

.printChar:
lodsb
test al,al
jz .newLine
or eax,0x0F00
mov word [ebx], ax
add ebx, 2
jmp .printChar

.newLine:
mov edx, ebx
sub edx, ecx
mov ecx, 0x000A0
sub ecx, edx
add ebx, ecx
ret

kernelVersion: db "Kernel Version: 0.0.1", 0

What I tried: Changing every byte in eax to find find the attribute byte for the foregound color. By doing this try and error like, i found out, that changing ah works for the background color, but al is used in test al, al to find the end of the string and the part of eax that is not ax is simply dismissed by the function. I didn't try changing something in the other registers, because they are used for something else or for nothing at all, so it does not make sence to me. A website (that i didnt find to link it) said the attribute byte is defined like that: Hex Value of BG Color (say F for white) * 16 = F0 + Hex Value of FG Color (Lets take the A for light green) what should be FA. If i do "mov ah, 0xFA" i change the background to white, but the foreground still is white (default).

Providing an minimal repoducible example for this would not be minimal anymore, because i would have to give you the bootloader and GDT aswell. But it would be enough of an answer if someone could tell me which byte is the foreground color attribute byte, so i can focus on trying to get that byte to work instead of having to rewrite the whole video momory in try and error.


Solution

  • First; let's optimise your code a little. Specifically, for this loop:

    .printChar:
        lodsb
        or al,al
        jz .newLine
        or eax,0x0F00
        mov word [ebx], ax
        add ebx, 2
        jmp .printChar
    

    ..after the first iteration the value in ah won't change; so that instruction can be lifted out of the loop to improve performance. Also or eax,0x0F000 has the same effect as a shorter or ah,0x0F. With both those changes it ends up like this:

        or ah,0x0F
    .printChar:
        lodsb
        or al,al
        jz .newLine
        mov word [ebx], ax
        add ebx, 2
        jmp .printChar
    

    Now add some comments, like this:

        or ah,0x0F          ;Force the foreground colour for all characters to be white
    .printChar:
        lodsb               ;al = next character
        or al,al            ;Is the next character zero?
        jz .newLine         ; yes, don't print it and move to the next line instead
        mov word [ebx], ax  ;Store next character (from string) and attribute (from outside the loop)
        add ebx, 2          ;bx = address to store next character and attribute
        jmp .printChar
    

    Notice that comments (e.g. "Force the foreground colour for all characters to be white") are useful because:

    • they reduce the time it takes to understand what the code is supposed to be doing

    • they make it easy to see bugs where the instruction doesn't do what the comment says it is supposed to do