VGA Fonts
So you know how to display characters in text mode, and now you want to do it in graphics mode. It's not complicated, but definitely more complex than writing and ASCII code at a specific offset in memory. You'll have to do it pixel by pixel.
But how do you know what to draw? It's stored in data matrix called bitmap fonts.
Decoding of bitmap fonts
How is a character stored in memory? It's quite simple, 0 encodes background, 1 encodes foreground color. VGA fonts are always 8 bits wide so that each byte contains exactly one row. For letter 'A' in the typical 16x8 font it would be (in binary):
00000000b byte 0 00000000b byte 1 00000000b byte 2 00010000b byte 3 00111000b byte 4 01101100b byte 5 11000110b byte 6 11000110b byte 7 11111110b byte 8 11000110b byte 9 11000110b byte 10 11000110b byte 11 11000110b byte 12 00000000b byte 13 00000000b byte 14 00000000b byte 15
The full bitmap contains bitmaps for every character, thus it's 256*16 bytes, 4096 bytes long. If you want to get the bitmap for a specific character, you have to multiply the ASCII code by 16 (number of rows in a character), add the offset of your bitmap and you're ready to go.
How to get fonts?
There're several ways. You can have it in a file on your filesystem. You can hardcode it in an array. But sometimes 4k is so much that you cannot afford, and reading a file is not an option (like in a boot loader), in which case you'll have to read the one used by the card (to display text mode characters) from VGA RAM.
Store it in an array
Easiest way, but increases your code by 4k. There are several sources that provide the entire font in binary or source format so you do not need to manually write it out.
Store it in a file
Most modular way. You can use different fonts if you like. Downside you'll need a working filesystem implementation.
Get the copy stored in the VGA BIOS
It's a standard BIOS call. If you're still in real mode, it's quite easy to use.
;in: es:di=4k buffer
;out: buffer filled with font
push ds
push es
;ask BIOS to return VGA bitmap fonts
mov ax, 1130h
mov bh, 6
int 10h
;copy charmap
push es
pop ds
pop es
mov si, bp
mov cx, 256*16/4
rep movsd
pop ds
Get from VGA RAM directly
Maybe you're already in protected mode, so cannot access BIOS functions. In this case you can still get the bitmap by programming VGA registers. Be careful that the VGA always reserves space for 8x32 fonts so you will need to either copy 8k of data or trim off the bottom 16 bytes of each character during the copy:
;in: edi=8k buffer
;out: buffer filled with font
;clear even/odd mode
mov dx, 03ceh
mov ax, 5
out dx, ax
;map VGA memory to 0A0000h
mov ax, 0406h
out dx, ax
;set bitplane 2
mov dx, 03c4h
mov ax, 0402h
out dx, ax
;clear even/odd mode (the other way, don't ask why)
mov ax, 0704h
out dx, ax
;copy charmap
mov esi, 0A0000h
mov cx, 256*32/4
rep movsd
;restore VGA state to normal operation
mov ax, 0302h
out dx, ax
mov ax, 0304h
out dx, ax
mov dx, 03ceh
mov ax, 1005h
out dx, ax
mov ax, 0A06h
out dx, ax
It worth mentioning that it has to be done before you switch to a graphics mode, because the VGA font is wiped from memory during transition and especially in the case of VBE where VGA registers are usually not accessible afterwards.
Set VGA fonts
If you're still in text mode and want the VGA card to draw different glyphs, or you want to change back from a graphics mode, you can set the VGA font. It's worthless in graphics mode (because characters displayed by your code there, not by the card), I only wrote this section for completeness. Modifying the font bitmaps in VGA RAM isn't hard if you read carefully what's written so far. I'll left it to you as a homework.
Set fonts via BIOS
Hint: check Ralph Brown Interrupt list Int 10/AX=1110h.
Set fonts directly
Hint: use the same code as above, but swap source and destination for "rep movsd".
Displaying a character
Okay, we have a font bitmap, but how to use it? Here's a simple example:
//this is the bitmap font you've loaded
unsigned char *font;
void drawchar(unsigned char c, int x, int y, int fgcolor, int bgcolor)
{
int cx,cy;
int mask[8]={1,2,4,8,16,32,64,128};
unsigned char *gylph=font+(int)c*8*16;
for(cy=0;cy<16;cy++){
for(cx=0;cx<8;cx++){
putpixel(glyph[cy]&mask[cx]?fgcolor:bgcolor,x+cx,y+cy);
}
}
}
It's not so complicated. You supply the function with arguments:
c - the character's ASCII code you want to display x,y - where to draw fgcolor - foreground color bgcolor - background color
It could be handly to draw the background as well, because it erase the area under the glyph. But sometimes it could be annoying, so here's a slightly modificated version that's using transparent background:
//this is the bitmap font you've loaded
unsigned char *font;
void drawchar_transparent(unsigned char c, int x, int y, int fgcolor)
{
int cx,cy;
int mask[8]={1,2,4,8,16,32,64,128};
unsigned char *gylph=font+(int)c*8*16;
for(cy=0;cy<16;cy++){
for(cx=0;cx<8;cx++){
if(glyph[cy]&mask[cx]) putpixel(fgcolor,x+cx,y+cy);
}
}
}
As you can see, we only have one fgcolor this time. The putpixel call has a condition, only invoked if the bit in the bitmap is set. If it's clear, nothing will happen, thus makes the background transparent.