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.

ALWhat it does
00hset palette register BL to the value BH
01hset the border (overscan) colour to BH
02hset all 16 palette registers and the border from 17 bytes at ES:DX
03hBL = 0: bit 7 of the attribute means bright background; BL = 1: it means blink
07hread palette register BL into BH (VGA)
08hread the border colour into BH (VGA)
09hread the 16 palette registers and the border into 17 bytes at ES:DX (VGA)
10hset DAC register BX to red DH, green CH, blue CL
12hset CX DAC registers from BX on, from 3 bytes each at ES:DX
13hcolour paging: BL = 0 selects the paging mode in BH, BL = 1 the page in BH
15hread DAC register BX into DH, CH, CL
17hread CX DAC registers from BX on into 3 bytes each at ES:DX
1Ahread the colour paging state: BL = mode, BH = page
1Bhturn 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:

Prints "DAC 1 was 00 00 2A" (red 0, green 0, blue 42 of 63: the standard blue). On the screen a blue bar shows for a second, turns orange for a second, together with anything else blue, and is blue again.
MOV AX, 1015h ; read one DAC register
MOV BX, 1 ; register 1: what text colour 1, blue, shows
INT 10h ; DH = red, CH = green, CL = blue, 0..63
MOV [RED1], DH
MOV [GRN1], CH
MOV [BLU1], CL
MOV AX, 0920h ; a bar of 20 blanks...
MOV BX, 001Fh ; ...on page 0, white on blue
MOV CX, 20
INT 10h
MOV BH, 0 ; page 0
MOV AX, 0E0Dh ; BIOS teletype: CR...
INT 10h
MOV AX, 0E0Ah ; ...and LF: on to the next line
INT 10h
CALL WAIT1 ; a second of blue
MOV AX, 1010h ; set one DAC register
MOV BX, 1
MOV DH, 63 ; red
MOV CH, 32 ; green
MOV CL, 0 ; blue: together, orange
INT 10h
CALL WAIT1 ; a second of orange
MOV AX, 1010h ; the old colour back
MOV BX, 1
MOV DH, [RED1]
MOV CH, [GRN1]
MOV CL, [BLU1]
INT 10h
MOV AH, 09h
MOV DX, OFFSET MSG1
INT 21h
MOV AL, [RED1]
CALL PHEX2
MOV DL, " "
MOV AH, 02h
INT 21h
MOV AL, [GRN1]
CALL PHEX2
MOV DL, " "
MOV AH, 02h
INT 21h
MOV AL, [BLU1]
CALL PHEX2
MOV AH, 09h
MOV DX, OFFSET CRLF1
INT 21h
RED1 DB 0
GRN1 DB 0
BLU1 DB 0
MSG1 DB "DAC 1 was $"
CRLF1 DB 13, 10, "$"
WAIT1: ; wait 1 s: CX:DX = 0F4240h microseconds
MOV CX, 000Fh
MOV DX, 4240h
MOV AH, 86h
INT 15h
RET
PHEX2: ; print AL as two hex digits; changes AX, CX, DX
PUSH AX
MOV CL, 4
SHR AL, CL ; the high nibble first
CALL PDIG1
POP AX
AND AL, 0Fh ; then the low one, falling into PDIG1
PDIG1:
ADD AL, "0"
CMP AL, "9"
JBE PDIG2
ADD AL, 7 ; "A".."F"
PDIG2:
MOV DL, AL
MOV AH, 02h ; DOS: print DL
INT 21h
RET

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 / intensity
MOV BL, 0 ; 0: bright backgrounds, 1: blink
INT 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.