INT 10h is the BIOS's video service. On the first PCs it lived in the system BIOS and knew the monochrome (MDA) and CGA cards; an EGA or VGA card brings its own ROM at segment C000h, which takes over the vector at boot and adds the functions from 10h up: palettes, fonts, information about the card. A program puts the function number in AH and the arguments in other registers and executes INT 10h. The same call works whatever card is fitted, which was the whole point.

Registers a function does not return are normally kept, but not every BIOS was careful: some early ones destroy BP in the scroll functions, and a few change AX even where nothing is returned. Do not keep a value across the call in a register you cannot easily reload, or push it first.

Text modes and graphics modes

In a text mode the card stores characters, not pixels. Each cell of the screen is two bytes of video memory, the character code and its attribute (its colours), and the card draws the glyph from a font as it scans the screen; 80 × 25 cells take 4000 bytes. In a graphics mode every pixel is stored, as 1, 2, 4 or 8 bits, and text is something the BIOS draws dot by dot from its font. DOS and its command line run in a text mode, normally mode 03h.

Common modes

ModeKindSizeColoursMemoryCard
00htext40×2516 (grey on a CGA)B800hCGA
01htext40×2516B800hCGA
02htext80×2516 (grey on a CGA)B800hCGA
03htext80×2516B800hCGA; the usual DOS mode
04hgraphics320×2004B800hCGA
05hgraphics320×2004 (grey)B800hCGA
06hgraphics640×2002B800hCGA
07htext80×25monoB000hMDA, Hercules
0Dhgraphics320×20016A000hEGA
0Ehgraphics640×20016A000hEGA
0Fhgraphics640×350monoA000hEGA
10hgraphics640×35016A000hEGA
11hgraphics640×4802A000hVGA, MCGA
12hgraphics640×48016A000hVGA
13hgraphics320×200256A000hVGA, MCGA

Modes 08h–0Ah belonged to the PCjr, and 0Bh and 0Ch were used internally by the EGA BIOS; nothing sets them on an EGA or VGA. A VGA can show every mode in the table. Higher resolutions are VESA modes, reached through INT 10h as well, with AH = 4Fh. DOSBox-X emulates an S3 SVGA card by default, so all of them work there.

The attribute byte

In the colour text modes the second byte of a cell holds its colours: bits 0–3 the foreground, bits 4–6 the background, bit 7 blink. So an attribute is background × 16 + foreground: 07h is the usual light grey on black, 1Fh white on blue, 4Eh yellow on red. A blinking character flashes; INT 10h AH=10h with AL = 03h trades the blink bit for eight more background colours, which most full-screen programs of the time preferred.

ValueColourValueColour
0black8dark grey
1blue9light blue
2greenAhlight green
3cyanBhlight cyan
4redChlight red
5magentaDhlight magenta
6brownEhyellow
7light greyFhwhite

On a monochrome card in mode 07h the same byte means normal (07h), underlined (01h), bright (0Fh), reverse (70h) or invisible (00h), and bit 7 still blinks.

Pages

Video memory is larger than one screen. In mode 03h an EGA or VGA holds eight pages of 4096 bytes (4000 of them used): page n starts at B800h:n × 1000h, and the card shows one of them, the active page, chosen with INT 10h AH=05h. Every page has its own cursor, and the services that write characters take a page number in BH, so a program can prepare a page out of sight and then flip to it.

The BIOS keeps its state in the BIOS data area at segment 40h, and programs may read it: the mode at 40h:49h, the columns at 40h:4Ah, the page size at 40h:4Ch, the cursor positions of the eight pages from 40h:50h (a word each, column in the low byte), the cursor shape at 40h:60h, the active page at 40h:62h and, on the EGA and later, the number of rows minus one at 40h:84h.

Where video memory lives

Colour text is at segment B800h, monochrome text at B000h, EGA and VGA graphics at A000h. In 80×25 text the cell at row r, column c is at offset (r × 80 + c) × 2: the character at the even offset, the attribute right after it. Mode 13h is the easy graphics mode: one byte per pixel, 320 bytes per line, so the pixel at x, y is at offset y × 320 + x. The 16-colour EGA and VGA modes spread each pixel over four bit planes behind the same addresses and are programmed through the card's registers, and the CGA modes keep the even lines at offset 0 and the odd ones from 2000h.

BIOS or video memory?

The BIOS is portable and simple and takes care of the cursor and scrolling, but every character or pixel is a call, and for pixels that is very slow. Writing to video memory with MOV or STOSW is many times faster, and it is what games and full-screen editors did; the price is that the program must know the mode and its layout, and no cursor moves. A common mix is to set the mode and the cursor through the BIOS and write the contents directly. Here a word goes straight into B800h, on the row where the cursor is:

Prints "Direct to video memory ->"; on the screen the same line also shows B800h in yellow on blue from column 60.
MOV AH, 03h ; BIOS: where is the cursor? DH = row
MOV BH, 0 ; page 0
INT 10h
MOV AL, 160 ; 80 cells of 2 bytes per row
MUL DH ; AX = row * 160
ADD AX, 60 * 2 ; column 60
MOV DI, AX
MOV AX, 0B800h ; colour text memory
MOV ES, AX
MOV SI, OFFSET TXT1
MOV AH, 1Eh ; attribute: yellow on blue
CLD
NEXT1:
LODSB ; AL = next character
OR AL, AL ; 0 ends the text
JZ DONE1
STOSW ; the character, then the attribute
JMP NEXT1
DONE1:
MOV AH, 09h
MOV DX, OFFSET MSG1 ; DOS prints on the same row
INT 21h
TXT1 DB "B800h", 0
MSG1 DB "Direct to video memory ->", 13, 10, "$"

A program that writes video memory directly should first check the mode with INT 10h AH=0Fh: B000h in mode 07h, B800h in the other text modes, and the active page's offset if it is not page 0. In mode 13h one pixel is one byte:

MOV AX, 0A000h ; mode 13h: a byte per pixel at A000h
MOV ES, AX
MOV DI, 100 * 320 + 160 ; x = 160, y = 100: the middle
MOV BYTE PTR ES:[DI], 4 ; colour 4, red

DOS and the BIOS

DOS writes its console output through the video BIOS, so INT 21h AH=09h text appears at the BIOS cursor and in the current mode, and BIOS and DOS output mix freely on one screen. In the workbench only DOS output is copied to the terminal: anything written with INT 10h or straight into video memory shows only on the DOS screen. DOSBox-X's built-in DOS calls its video routines internally rather than through the vector, so a program that hooks INT 10h does not see DOS's own output there, as it would on a real PC.