灵符lingfu

ling-lang

↓ 0 downloads

Ling - The Omniglot Systems Language

Keywords systems, polyglot, ai, crypto, game
lingfu install ling-lang
LicenseApache-2.0 OR MIT
AuthorsLing Lin, Sanny Lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (995)

crates/ling-kernel/src/drivers/vga.rs
use crate::arch::io::outb;
use core::ptr;

const VGA_BUF: *mut u8 = 0xB8000 as *mut u8;
const WIDTH: usize = 80;
const HEIGHT: usize = 25;

/// Upload a custom 256-glyph, 8x16, 1bpp bitmap font into the VGA text-mode
/// character generator (plane 2), replacing the BIOS default font. This is
/// the classic "VGA custom font" register sequence (Sequencer + Graphics
/// Controller reprogramming to reach font plane 2, write glyphs, then
/// restore normal text-mode addressing) — see the OSDev wiki's "VGA Fonts"
/// article. `font` is 256 glyphs * 16 rows, one byte per row (MSB = leftmost
/// pixel), i.e. the standard raw PC screen-font glyph layout.
pub unsafe fn load_font(font: &[u8; 4096]) {
    // Enter font-access mode: select plane 2, sequential addressing.
    outb(0x3C4, 0x00);
    outb(0x3C5, 0x01); // sequencer reset
    outb(0x3C4, 0x02);
    outb(0x3C5, 0x04); // map mask: plane 2 only
    outb(0x3C4, 0x04);
    outb(0x3C5, 0x07); // sequential addressing, extended mem
    outb(0x3C4, 0x00);
    outb(0x3C5, 0x03); // end reset

    outb(0x3CE, 0x04);
    outb(0x3CF, 0x02); // read map select: plane 2
    outb(0x3CE, 0x05);
    outb(0x3CF, 0x00); // write mode 0, read mode 0
    outb(0x3CE, 0x06);
    outb(0x3CF, 0x04); // map at 0xA0000, disable odd/even

    // Glyphs are stored on 32-byte centers even though only 16 bytes (an 8x16
    // cell) are meaningful, matching the VGA font plane's fixed glyph pitch.
    let font_mem = 0xA0000 as *mut u8;
    for glyph in 0..256usize {
        for row in 0..16usize {
            ptr::write_volatile(font_mem.add(glyph * 32 + row), font[glyph * 16 + row]);
        }
    }

    // Restore normal text-mode addressing (planes 0/1, odd/even, 0xB8000).
    outb(0x3C4, 0x00);
    outb(0x3C5, 0x01);
    outb(0x3C4, 0x02);
    outb(0x3C5, 0x03); // map mask: planes 0+1
    outb(0x3C4, 0x04);
    outb(0x3C5, 0x03); // odd/even addressing
    outb(0x3C4, 0x00);
    outb(0x3C5, 0x03);

    outb(0x3CE, 0x04);
    outb(0x3CF, 0x00);
    outb(0x3CE, 0x05);
    outb(0x3CF, 0x10); // odd/even mode
    outb(0x3CE, 0x06);
    outb(0x3CF, 0x0E); // map at 0xB8000, odd/even enabled
}

/// Registered by the generated per-project `_start` (if a build-time idle
/// font was found and rasterized) so `keyboard::read_char`'s idle detector
/// can swap to it without ling-kernel needing to know about any specific
/// project's font asset.
static mut IDLE_FONT: Option<&'static [u8; 4096]> = None;

pub fn set_idle_font(font: &'static [u8; 4096]) {
    unsafe {
        IDLE_FONT = Some(font);
    }
}

/// A snapshot of whatever font was actually active (the BIOS default,
/// almost always — nothing else has loaded a custom one yet) taken the
/// first time `use_idle_font` swaps away from it, so `restore_normal_font`
/// has something real to bring back. There's no way to ask the hardware
/// "what was the original font" after overwriting it, so this has to be
/// captured before that first swap, not reconstructed after.
static mut ORIGINAL_FONT: [u8; 4096] = [0u8; 4096];
static mut ORIGINAL_FONT_SAVED: bool = false;

unsafe fn save_original_font_if_needed() {
    if ptr::read(&raw const ORIGINAL_FONT_SAVED) {
        return;
    }
    // Same font-plane-2 access dance as `load_font`, but reading instead of
    // writing (Graphics Controller "read map select" instead of the
    // Sequencer's write "map mask").
    outb(0x3C4, 0x00);
    outb(0x3C5, 0x01);
    outb(0x3C4, 0x02);
    outb(0x3C5, 0x04);
    outb(0x3C4, 0x04);
    outb(0x3C5, 0x07);
    outb(0x3C4, 0x00);
    outb(0x3C5, 0x03);

    outb(0x3CE, 0x04);
    outb(0x3CF, 0x02); // read map select: plane 2
    outb(0x3CE, 0x05);
    outb(0x3CF, 0x00);
    outb(0x3CE, 0x06);
    outb(0x3CF, 0x04);

    let font_mem = 0xA0000 as *const u8;
    for glyph in 0..256usize {
        for row in 0..16usize {
            let byte = ptr::read_volatile(font_mem.add(glyph * 32 + row));
            (*&raw mut ORIGINAL_FONT)[glyph * 16 + row] = byte;
        }
    }

    outb(0x3C4, 0x00);
    outb(0x3C5, 0x01);
    outb(0x3C4, 0x02);
    outb(0x3C5, 0x03);
    outb(0x3C4, 0x04);
    outb(0x3C5, 0x03);
    outb(0x3C4, 0x00);
    outb(0x3C5, 0x03);
    outb(0x3CE, 0x04);
    outb(0x3CF, 0x00);
    outb(0x3CE, 0x05);
    outb(0x3CF, 0x10);
    outb(0x3CE, 0x06);
    outb(0x3CF, 0x0E);

    ORIGINAL_FONT_SAVED = true;
}

/// Swap to the registered idle font, if one was set. No-op otherwise.
/// Snapshots whatever font was active first, so `restore_normal_font` can
/// bring it back later.
pub fn use_idle_font() {
    unsafe {
        save_original_font_if_needed();
        if let Some(font) = IDLE_FONT {
            load_font(font);
        }
    }
}

/// Swap back to the font that was active before `use_idle_font` — called
/// on real keyboard/mouse activity after the idle font fired. No-op if
/// `use_idle_font` was never called (nothing to restore).
pub fn restore_normal_font() {
    unsafe {
        if ptr::read(&raw const ORIGINAL_FONT_SAVED) {
            load_font(&*&raw const ORIGINAL_FONT);
        }
    }
}

/// Disable the hardware text-mode cursor (CRTC "Cursor Start" register,
/// index 0x0A, bit 5). Nothing in this driver ever programs the CRTC
/// cursor-position registers (0x3D4/0x3D5 index 0x0E/0x0F) either, so
/// leaving the hardware cursor enabled means it just sits wherever GRUB/BIOS
/// last left it, blinking, never tracking real writes -- the reported "cursor
/// in a random position" bug. `term.rs` draws its own cursor instead (always
/// derived from the live write position), so the hardware one just needs to
/// get out of the way, not be kept in sync. Call once, at boot.
pub unsafe fn disable_hardware_cursor() {
    outb(0x3D4, 0x0A);
    outb(0x3D5, 0x20);
}

/// Set one VGA DAC palette entry (index 0..=15, matching `Color`'s ordinal
/// values). `r`/`g`/`b` are 6-bit (0..=63) — the VGA DAC's native precision,
/// not the 8-bit values a CSS-style hex color gives you; scale by dividing
/// an 8-bit channel by 4 (see `THEME_LINGOS`'s doc comment).
pub unsafe fn set_palette_entry(index: u8, r: u8, g: u8, b: u8) {
    outb(0x3C8, index);
    outb(0x3C9, r);
    outb(0x3C9, g);
    outb(0x3C9, b);
}

/// A full 16-entry palette, in `Color` ordinal order, each channel 6-bit.
pub type Theme = [(u8, u8, u8); 16];

/// The standard VGA/EGA default palette — every theme starts as a copy of
/// this and overrides only the entries it actually wants to restyle, so
/// colors nobody themed still look like reasonable, conventional colors.
pub const DEFAULT_THEME: Theme = [
    (0, 0, 0),    // 0  Black
    (0, 0, 42),   // 1  Blue
    (0, 42, 0),   // 2  Green
    (0, 42, 42),  // 3  Cyan
    (42, 0, 0),   // 4  Red
    (42, 0, 42),  // 5  Magenta
    (42, 21, 0),  // 6  Brown
    (42, 42, 42), // 7  LightGrey
    (21, 21, 21), // 8  DarkGrey
    (21, 21, 63), // 9  LightBlue
    (21, 63, 21), // 10 LightGreen
    (21, 63, 63), // 11 LightCyan
    (63, 21, 21), // 12 LightRed
    (63, 21, 63), // 13 LightMagenta
    (63, 63, 21), // 14 Yellow
    (63, 63, 63), // 15 White
];

/// LingOS's default theme: a dark indigo background with taupe, navy,
/// red, gold, cyan, and green accents — restyles the palette slots the
/// boot banner and shell actually use (`Black`/bg, `Blue`, `Green`, `Red`,
/// `LightGrey`, `LightCyan`, `Yellow`), leaving the rest at their standard
/// VGA values. 8-bit-RGB-to-6-bit-DAC: divide each channel by 4.
pub const THEME_LINGOS: Theme = {
    let mut t = DEFAULT_THEME;
    t[Color::Black as usize] = (0, 0, 0); // true black background
    t[Color::Blue as usize] = (7, 5, 25); // dark navy-blue accent
    t[Color::Green as usize] = (7, 35, 7); // green accent
    t[Color::Red as usize] = (63, 15, 16); // red/coral accent
    t[Color::LightGrey as usize] = (49, 44, 43); // taupe/mauve accent
    t[Color::LightCyan as usize] = (0, 63, 60); // bright cyan accent
    t[Color::Yellow as usize] = (63, 49, 0); // gold accent
    t
};

/// A light theme: soft-white background with darkened variants of every
/// color slot the boot banner/shell actually use, so switching themes
/// stays readable everywhere those show up, not just the background.
pub const THEME_LIGHT: Theme = {
    let mut t = DEFAULT_THEME;
    t[Color::Black as usize] = (60, 60, 58); // background: soft white
    t[Color::Blue as usize] = (0, 0, 35);
    t[Color::Green as usize] = (0, 30, 0);
    t[Color::Red as usize] = (35, 0, 0);
    t[Color::LightGrey as usize] = (8, 8, 8); // near-black body text
    t[Color::LightCyan as usize] = (0, 28, 28);
    t[Color::Yellow as usize] = (35, 22, 0);
    t[Color::LightRed as usize] = (40, 8, 8);
    t[Color::LightGreen as usize] = (0, 35, 0);
    t[Color::LightBlue as usize] = (5, 5, 40);
    t[Color::LightMagenta as usize] = (30, 0, 30);
    t
};

/// Program every DAC entry from `theme` — the whole point of keeping
/// themes as plain data (`Theme` = `[(u8,u8,u8); 16]`) is that "switch
/// theme" is just "call this again with a different table."
pub fn apply_theme(theme: &Theme) {
    for (i, &(r, g, b)) in theme.iter().enumerate() {
        unsafe { set_palette_entry(i as u8, r, g, b) };
    }
}

#[repr(u8)]
#[derive(Copy, Clone)]
pub enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGrey = 7,
    DarkGrey = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    LightMagenta = 13,
    Yellow = 14,
    White = 15,
}

pub(crate) fn color_byte(fg: u8, bg: u8) -> u8 {
    (bg as u8) << 4 | (fg as u8)
}

static mut FG: u8 = 7;
static mut BG: u8 = 0;
static mut COL: usize = 0;
static mut ROW: usize = 0;

pub fn set_color(fg: Color, bg: Color) {
    unsafe {
        FG = fg as u8;
        BG = bg as u8;
    }
}

pub fn clear() {
    unsafe {
        let color = color_byte(FG, BG);
        for i in 0..(WIDTH * HEIGHT) {
            ptr::write_volatile(VGA_BUF.add(i * 2), b' ');
            ptr::write_volatile(VGA_BUF.add(i * 2 + 1), color);
        }
        COL = 0;
        ROW = 0;
    }
}

pub fn write_char(c: u8) {
    unsafe {
        if c == b'\n' {
            COL = 0;
            ROW += 1;
            if ROW >= HEIGHT {
                scroll();
                ROW = HEIGHT - 1;
            }
            return;
        }
        if c == b'\r' {
            COL = 0;
            return;
        }
        let pos = ROW * WIDTH + COL;
        let color = color_byte(FG, BG);
        if pos < WIDTH * HEIGHT {
            ptr::write_volatile(VGA_BUF.add(pos * 2), c);
            ptr::write_volatile(VGA_BUF.add(pos * 2 + 1), color);
        }
        COL += 1;
        if COL >= WIDTH {
            COL = 0;
            ROW += 1;
            if ROW >= HEIGHT {
                scroll();
                ROW = HEIGHT - 1;
            }
        }
    }
}

pub fn write(s: &[u8]) {
    for &c in s {
        write_char(c);
    }
}

pub fn write_str(s: &str) {
    write(s.as_bytes());
}

fn scroll() {
    unsafe {
        let color = color_byte(FG, BG);
        for row in 1..HEIGHT {
            for col in 0..WIDTH {
                let src = row * WIDTH + col;
                let dst = (row - 1) * WIDTH + col;
                let byte = ptr::read_volatile(VGA_BUF.add(src * 2));
                let attr = ptr::read_volatile(VGA_BUF.add(src * 2 + 1));
                ptr::write_volatile(VGA_BUF.add(dst * 2), byte);
                ptr::write_volatile(VGA_BUF.add(dst * 2 + 1), attr);
            }
        }
        let last_row = (HEIGHT - 1) * WIDTH;
        for col in 0..WIDTH {
            ptr::write_volatile(VGA_BUF.add((last_row + col) * 2), b' ');
            ptr::write_volatile(VGA_BUF.add((last_row + col) * 2 + 1), color);
        }
    }
}

← Back to index