VGA
Register Groups
VGA hardware is controlled through five groups of I/O-mapped registers. All access is through the kernel's outb/inb wrappers.
| Group | Index Port | Data Port | Purpose |
|---|---|---|---|
| Sequencer (SEQ) | 0x3C4 |
0x3C5 |
Memory sequencing, clock, plane write masks |
| CRTC | 0x3D4 |
0x3D5 |
Display timing, scan line counts, cursor position |
| Graphics Controller (GC) | 0x3CE |
0x3CF |
Read/write modes, color compare, bit masks |
| Attribute Controller (AC) | 0x3C0 |
0x3C0 |
Palette, display enable, pixel clock |
| Miscellaneous Output | 0x3C2 |
— | Clock select, sync polarity, I/O address select |
The Attribute Controller uses a single port for both index and data, toggled by an internal flip-flop. It must be reset by reading 0x3DA (Input Status 1) before writing.
Supported Hardware Modes
set_video_mode(mode: u8) -> bool in video/vga_hw.rs programs all five register groups for the requested mode. Returns true on success, false for an unknown mode number.
| Mode | Number | Resolution | Colors | Use |
|---|---|---|---|---|
| Text 80×25 | 0x03 |
720×400 (character) | 16 | Kernel console, default |
| 16-color 320×200 | 0x0D |
320×200 | 16 | Legacy planar graphics |
| 16-color 640×480 | 0x12 |
640×480 | 16 | Higher-res planar graphics |
| 256-color 320×200 | 0x13 |
320×200 | 256 | Mode 13h, linear byte-per-pixel |
Mode-Switch Sequence
For all modes:
- Write Miscellaneous Output register (
0x3C2). - Write Sequencer registers (
0x3C4/0x3C5), index 0–4. - Unlock CRTC (clear protect bit on index
0x11), then write CRTC registers. - Write Graphics Controller registers.
- Reset AC flip-flop (read
0x3DA), then write all 21 Attribute Controller registers (indices 0–20), finishing with a write of0x20to0x3C0to re-enable display output. - For mode
0x03only: callclear_text_buf(),restore_font(),restore_text_dac().
Mode 0x03 — Text Mode Restoration
Switching back to text mode from a graphics mode requires three additional steps that reprogram state the BIOS would normally set up.
clear_text_buf()
Fills the VGA text buffer at 0xB8000 with space characters (byte 0x20) and attribute byte 0x07 (light gray on black). Clears all 80×25 = 2000 character cells.
restore_font()
Reloads the embedded 8×16 font (VGA_FONT_8X16, 4096 bytes) into VGA plane 2 (the font plane). The sequence:
SEQ[0x00] ← 0x01 (synchronous reset)
SEQ[0x02] ← 0x04 (write to plane 2 only)
SEQ[0x04] ← 0x07 (sequential addressing, odd/even disabled)
GC[0x04] ← 0x02 (read plane 2)
GC[0x05] ← 0x00 (write mode 0, read mode 0)
GC[0x06] ← 0x00 (A000-BFFF range, graph mode off)
for each glyph g (0..256):
write 16 bytes of VGA_FONT_8X16[g*16..] to 0xA0000 + g*32
SEQ[0x02] ← 0x03 (restore write to planes 0+1)
SEQ[0x04] ← 0x03 (odd/even addressing)
GC[0x04] ← 0x00 (read plane 0)
GC[0x05] ← 0x10 (odd/even read)
GC[0x06] ← 0x0E (B800-BFFF range, text mode)
SEQ[0x00] ← 0x03 (release reset)
Each glyph occupies 32 bytes in plane 2 (only the first 16 are significant; the upper 16 are zero-padding required by VGA).
restore_text_dac()
Programs the VGA DAC (Digital-to-Analog Converter) with the standard 16-color EGA-compatible palette. The DAC is accessed via:
- Write palette index to
0x3C8 - Write R, G, B (6-bit, 0–63 each) sequentially to
0x3C9
The standard palette maps the 16 EGA attribute colors (0–15) to their canonical RGB values (e.g., index 0 = black 0,0,0; index 7 = light gray 42,42,42; index 15 = white 63,63,63).
Text Mode Writer (video/vga.rs)
The Writer struct manages the kernel's VGA text console.
pub struct Writer {
col_pos: usize,
row_pos: usize,
color_code: ColorCode,
buffer: *mut Buffer, // 0xB8000
}
Constants
| Constant | Value |
|---|---|
BUFFER_WIDTH |
80 |
BUFFER_HEIGHT |
25 |
BUFFER_ADDRESS |
0xB8000 |
Colors
Color is a 4-bit VGA attribute value (0–15). ColorCode packs foreground and background:
ColorCode = (background << 4) | foreground
Standard color indices: Black=0, Blue=1, Green=2, Cyan=3, Red=4, Magenta=5, Brown=6, LightGray=7, DarkGray=8, LightBlue=9, LightGreen=10, LightCyan=11, LightRed=12, Pink=13, Yellow=14, White=15.
write_byte(byte: u8)
| Byte | Action |
|---|---|
\n (0x0A) |
Advance to next row (new_line()) |
\r / 0x08 |
Backspace: decrement col_pos, write space, do not advance |
| Other | Write character cell at (col_pos, row_pos), advance col_pos; call new_line() on column overflow |
new_line()
When row_pos reaches BUFFER_HEIGHT - 1, the entire buffer is scrolled up one row: rows 1–24 are copied to rows 0–23 using direct memory writes to 0xB8000. Row 24 is cleared. When row_pos < BUFFER_HEIGHT - 1, row_pos is incremented and col_pos is reset to 0.
Hardware Cursor (move_cursor())
The hardware text cursor is positioned by writing the linear cell index (row * 80 + col) as two bytes to CRTC:
CRTC[0x0E] ← (position >> 8) & 0xFF (high byte)
CRTC[0x0F] ← position & 0xFF (low byte)
Called after every character write that changes the cursor position.
VGA VRAM Window
The VGA VRAM window (0xA00_000 virtual, 0xA00_000 physical) is mapped on demand by syscall 0x14 (map_vram). The mapping uses fine-grained 4 KiB pages (a P1 table allocated from PAGE_TABLE_POOL) rather than the 2 MiB huge pages used elsewhere.
The P1 table is allocated once per process on the first map_vram call. Repeated calls are idempotent — the same P1 is reused. After mapping, userland can write directly to 0xA00_000 as Mode 13h VRAM (320×200, one byte per pixel, 64000 bytes).
The kernel's TLB is flushed by reloading CR3 after mapping.
Legacy VGA Module (src/vga/)
An older VGA module lives under src/vga/ (distinct from src/video/). It contains:
src/vga/buffer.rs— character/color cell typessrc/vga/write.rs— writer implementationsrc/vga/screen.rs— screen helper
This module predates the current video/ subsystem. The canonical implementation is src/video/vga.rs.