Files
engfinal/src/vga_buffer.rs
T
2026-06-04 04:20:07 -04:00

99 lines
3.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use core::ptr::write_volatile;
/// VGA text mode display dimensions.
///
/// The standard VGA text mode provides an 80×25 grid of character cells.
/// Each cell is 2 bytes: [ASCII byte] [attribute byte].
/// The buffer starts at physical address 0xB8000 and is memory-mapped into
/// the CPU's address space. The x86_64 bootloader identity-maps this region,
/// so we can write to it with regular memory stores — but we must use volatile
/// writes to prevent the compiler from optimizing away MMIO operations.
pub const VGA_ADDR: *mut u8 = 0xB8000 as *mut u8;
pub const WIDTH: usize = 80;
pub const HEIGHT: usize = 25;
/// Standard VGA text-mode colors.
///
/// The attribute byte layout:
/// bits 0-3: foreground color (0 = black, 15 = white)
/// bits 4-6: background color (0-7, hardware limit; bit 7 = blink)
#[allow(dead_code)]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum Color {
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,
}
/// A packed attribute byte encoding foreground + background color.
#[derive(Clone, Copy)]
pub struct ColorCode(u8);
impl ColorCode {
/// Constructs a color code from foreground and background colors.
///
/// ## Hardware note
/// The VGA attribute byte stores the background color in bits 4-6 (3 bits,
/// limiting backgrounds to 0-7). Bit 7 is the blink enable bit, which we
/// keep cleared. The foreground color uses bits 0-3 (including intensity
/// on bit 3), giving 16 possible foreground colors.
pub const fn new(fg: Color, bg: Color) -> ColorCode {
ColorCode(((bg as u8) << 4) | (fg as u8))
}
}
/// Clears the entire VGA screen to the given background color.
///
/// Writes a space character (0x20) with the appropriate attribute byte
/// to every cell on the screen.
pub fn clear_screen(bg: Color) {
let color = ColorCode::new(Color::White, bg);
for offset in (0..(WIDTH * HEIGHT * 2)).step_by(2) {
unsafe {
write_volatile(VGA_ADDR.add(offset), b' ');
write_volatile(VGA_ADDR.add(offset + 1), color.0);
}
}
}
/// Writes a single character at position (row, col) with the given color.
///
/// The position is bounds-checked against the 80×25 screen. Writes outside
/// the visible area are silently ignored.
pub fn write_char(row: usize, col: usize, ch: u8, color: ColorCode) {
if row < HEIGHT && col < WIDTH {
let offset = (row * WIDTH + col) * 2;
unsafe {
write_volatile(VGA_ADDR.add(offset), ch);
write_volatile(VGA_ADDR.add(offset + 1), color.0);
}
}
}
/// Writes a string starting at position (row, col) with the given color.
///
/// Stops at the right edge of the screen to avoid wrapping.
pub fn write_str(row: usize, col: usize, s: &str, color: ColorCode) {
for (i, &ch) in s.as_bytes().iter().enumerate() {
if col + i < WIDTH {
write_char(row, col + i, ch, color);
} else {
break;
}
}
}