In text modes the EGA and VGA draw characters from fonts held in the card's own memory, not from a fixed ROM as the CGA and MDA did. That makes the font replaceable: a program can load the ROM's other fonts or one of its own, redraw single glyphs (box drawing, national letters, a mouse arrow made from characters), and by loading a lower font fit more lines on the screen. Function 11h does all of this; AL selects the subfunction.
| AL | What it does |
|---|---|
| 00h | load CX glyphs of a user font from ES:BP, from character DX on, BH bytes each, into font block BL |
| 01h | load the ROM 8×14 font |
| 02h | load the ROM 8×8 font |
| 03h | choose which font blocks are in use (BL) |
| 04h | load the ROM 8×16 font (VGA) |
| 10h, 11h, 12h, 14h | as 00h, 01h, 02h, 04h, and set the character height and the number of rows to match |
| 20h–24h | fonts for graphics modes (through vectors 1Fh and 43h) |
| 30h | get information: BH chooses the font, ES:BP returns its address |
The 0xh forms only replace glyphs, for changing a few characters. The 1xh forms also reprogram the character height and recompute the rows, as a mode set would; they are meant to be called right after function 00h. 12h in mode 03h gives 400 / 8 = 50 lines on a VGA, 350 / 8 = 43 on an EGA:
MOV AX, 1112h ; load the 8x8 ROM font, recompute the rowsMOV BL, 0 ; into font block 0, the one in useINT 10hMOV AX, 1130h ; font information: DL = rows - 1MOV BH, 0INT 10hINC DLMOV [ROWS1], DLMOV CX, 001Eh ; wait 2 s: CX:DX = 1E8480h microsecondsMOV DX, 8480hMOV AH, 86h ; BIOS: waitINT 15hMOV AX, 0003h ; mode 03h: the 8x16 font and 25 rows againINT 10hMOV AH, 09hMOV DX, OFFSET TXT1INT 21hMOV AL, [ROWS1]CALL PDEC2MOV AH, 09hMOV DX, OFFSET CRLF1INT 21hROWS1 DB 0TXT1 DB "rows on the screen: $"CRLF1 DB 13, 10, "$"PDEC2: ; print AL (0..99) as two decimal digitsAAM ; AH = AL / 10, AL = AL mod 10ADD AX, 3030h ; both into "0".."9"PUSH AXMOV DL, AHMOV AH, 02hINT 21hPOP AXMOV DL, ALMOV AH, 02hINT 21hRET
Font information
With AL = 30h the BIOS returns in ES:BP the address of the font chosen by BH, in CX the height of the font in use (16 in mode 03h on a VGA) and in DL the number of rows minus one. The fonts are laid out simply: one byte per row of dots, bit 7 on the left, the rows of a character one after the other, so in the 8×16 font character n starts at n × 16. Here the BIOS's "A" is printed with DOS, a # for each dot:
MOV AX, 1130h ; get font informationMOV BH, 06h ; which font: the ROM 8x16INT 10h ; ES:BP = font, CX = height in use, DL = rows - 1MOV [FH1], CLINC DLMOV [ROWS1], DLMOV AL, [FH1]CALL PDEC2MOV AH, 09hMOV DX, OFFSET TXT1INT 21hMOV AL, [ROWS1]CALL PDEC2MOV AH, 09hMOV DX, OFFSET TXT2INT 21hADD BP, 41h * 16 ; "A" is character 41h: 16 bytes eachMOV SI, 16 ; 16 rows in the glyphGROW1:MOV BL, ES:[BP] ; one row: a bit per dot, bit 7 on the leftINC BPMOV CX, 8GBIT1:MOV DL, "."SHL BL, 1 ; the next dot into CFJNC GDOT1MOV DL, "#"GDOT1:MOV AH, 02hINT 21hLOOP GBIT1MOV AH, 09hMOV DX, OFFSET CRLF1INT 21hDEC SIJNZ GROW1FH1 DB 0ROWS1 DB 0TXT1 DB " scan lines, $"TXT2 DB " rows", 13, 10, "$"CRLF1 DB 13, 10, "$"PDEC2: ; print AL (0..99) as two decimal digitsAAM ; AH = AL / 10, AL = AL mod 10ADD AX, 3030h ; both into "0".."9"PUSH AXMOV DL, AHMOV AH, 02hINT 21hPOP AXMOV DL, ALMOV AH, 02hINT 21hRET
| BH | Font returned by AL = 30h |
|---|---|
| 00h | the one vector 1Fh points to (characters 80h–FFh in CGA graphics modes) |
| 01h | the one vector 43h points to (graphics modes) |
| 02h | ROM 8×14 |
| 03h | ROM 8×8, characters 00h–7Fh |
| 04h | ROM 8×8, characters 80h–FFh |
| 05h | ROM 9×14 replacement glyphs |
| 06h | ROM 8×16 (VGA) |
| 07h | ROM 9×16 replacement glyphs (VGA) |
Pitfalls
A program that changes the font or the number of lines must put it back, and setting mode 03h again is the simple way: it reloads the 8×16 font and 25 lines. Many programs assume 25 lines and misbehave otherwise. CX from AL = 30h is the height of the font in use, not of the font BH asked for. A loaded font only lives in the card's memory; the next mode set replaces it.