On a VGA a colour goes through two tables. A text or 16-colour pixel value 0–15 first selects one of 16 palette registers in the attribute controller, inherited from the EGA. The value found there (0–63) selects one of 256 DAC registers, and each DAC register holds a red, a green and a blue intensity of 6 bits, 0–63. In mode 13h the pixel value effectively selects the DAC register directly.
In mode 03h the palette registers send colours 0–5 and 7 to the DAC registers of the same numbers, brown (6) to 14h, and the bright colours 8–15 to 38h–3Fh. Changing DAC register 1 therefore changes every blue on the screen at once, without touching video memory: fades, palette animation and the colour cycling of old games all work this way.
| AL | What it does |
|---|---|
| 00h | set palette register BL to the value BH |
| 01h | set the border (overscan) colour to BH |
| 02h | set all 16 palette registers and the border from 17 bytes at ES:DX |
| 03h | BL = 0: bit 7 of the attribute means bright background; BL = 1: it means blink |
| 07h | read palette register BL into BH (VGA) |
| 08h | read the border colour into BH (VGA) |
| 09h | read the 16 palette registers and the border into 17 bytes at ES:DX (VGA) |
| 10h | set DAC register BX to red DH, green CH, blue CL |
| 12h | set CX DAC registers from BX on, from 3 bytes each at ES:DX |
| 13h | colour paging: BL = 0 selects the paging mode in BH, BL = 1 the page in BH |
| 15h | read DAC register BX into DH, CH, CL |
| 17h | read CX DAC registers from BX on into 3 bytes each at ES:DX |
| 1Ah | read the colour paging state: BL = mode, BH = page |
| 1Bh | turn CX DAC registers from BX on into shades of grey |
The example reads DAC register 1, shows a blue bar, makes register 1 orange for a second and then puts the old value back:
MOV AX, 1015h ; read one DAC registerMOV BX, 1 ; register 1: what text colour 1, blue, showsINT 10h ; DH = red, CH = green, CL = blue, 0..63MOV [RED1], DHMOV [GRN1], CHMOV [BLU1], CLMOV AX, 0920h ; a bar of 20 blanks...MOV BX, 001Fh ; ...on page 0, white on blueMOV CX, 20INT 10hMOV BH, 0 ; page 0MOV AX, 0E0Dh ; BIOS teletype: CR...INT 10hMOV AX, 0E0Ah ; ...and LF: on to the next lineINT 10hCALL WAIT1 ; a second of blueMOV AX, 1010h ; set one DAC registerMOV BX, 1MOV DH, 63 ; redMOV CH, 32 ; greenMOV CL, 0 ; blue: together, orangeINT 10hCALL WAIT1 ; a second of orangeMOV AX, 1010h ; the old colour backMOV BX, 1MOV DH, [RED1]MOV CH, [GRN1]MOV CL, [BLU1]INT 10hMOV AH, 09hMOV DX, OFFSET MSG1INT 21hMOV AL, [RED1]CALL PHEX2MOV DL, " "MOV AH, 02hINT 21hMOV AL, [GRN1]CALL PHEX2MOV DL, " "MOV AH, 02hINT 21hMOV AL, [BLU1]CALL PHEX2MOV AH, 09hMOV DX, OFFSET CRLF1INT 21hRED1 DB 0GRN1 DB 0BLU1 DB 0MSG1 DB "DAC 1 was $"CRLF1 DB 13, 10, "$"WAIT1: ; wait 1 s: CX:DX = 0F4240h microsecondsMOV CX, 000FhMOV DX, 4240hMOV AH, 86hINT 15hRETPHEX2: ; print AL as two hex digits; changes AX, CX, DXPUSH AXMOV CL, 4SHR AL, CL ; the high nibble firstCALL PDIG1POP AXAND AL, 0Fh ; then the low one, falling into PDIG1PDIG1:ADD AL, "0"CMP AL, "9"JBE PDIG2ADD AL, 7 ; "A".."F"PDIG2:MOV DL, ALMOV AH, 02h ; DOS: print DLINT 21hRET
Bright backgrounds
AX = 1003h with BL = 00h switches blinking off: attribute bit 7 then selects the bright colours 8–15 as backgrounds, so all 16 backgrounds become available, as most text-mode user interfaces of the 1990s used. BL = 01h turns blinking back on, and a mode set does so too.
MOV AX, 1003h ; blink / intensityMOV BL, 0 ; 0: bright backgrounds, 1: blinkINT 10h
Pitfalls
DAC values have 6 bits: 63 is full intensity, and larger values do not make a brighter colour (the top bits are ignored). The registers belong to the whole screen, so restore what you change: read it first with 15h or 17h, or set mode 03h again, which reloads the defaults. Changing many DAC registers while the screen is being drawn can flicker on real cards, so programs wait for the vertical retrace (bit 3 of port 3DAh) first. An EGA has only subfunctions 00h–03h. The block functions take ES:DX, and in a .COM program ES already equals DS.