os_essentials 0.0.3

A collection of tools for building simple educational operating systems in Rust in an x86 system. NOTE: MEANT TO BE BAREMETAL, YOU MUST HAVE compiler-buildtins-mem, core, compiler_builtins, alloc and a suited target, a vm or physical computer and a bootable usb required to test.
Documentation
use core::sync::atomic::{AtomicUsize, Ordering};

/// Colors module for defining VGA color constants.
pub mod colors {
    pub const BLACK: u8 = 0x00;
    pub const BLUE: u8 = 0x01;
    pub const GREEN: u8 = 0x02;
    pub const CYAN: u8 = 0x03;
    pub const RED: u8 = 0x04;
    pub const MAGENTA: u8 = 0x05;
    pub const BROWN: u8 = 0x06;
    pub const LIGHT_GRAY: u8 = 0x07;
    pub const DARK_GRAY: u8 = 0x08;
    pub const LIGHT_BLUE: u8 = 0x09;
    pub const LIGHT_GREEN: u8 = 0x0A;
    pub const LIGHT_CYAN: u8 = 0x0B;
    pub const LIGHT_RED: u8 = 0x0C;
    pub const LIGHT_MAGENTA: u8 = 0x0D;
    pub const LIGHT_YELLOW: u8 = 0x0E;
    pub const WHITE: u8 = 0x0F;
    pub const YELLOW: u8 = 0x0E;
    pub const DARK_RED: u8 = 0x10;
    pub const DARK_GREEN: u8 = 0x11;
    pub const DARK_BLUE: u8 = 0x12;
    pub const DARK_CYAN: u8 = 0x13;
    pub const DARK_MAGENTA: u8 = 0x14;
    pub const DARK_YELLOW: u8 = 0x15;
    pub const TRANSPARENT: u8 = 0x16;
}

/// Text mode handling for VGA output, including print and screen management.
pub mod textmode {
    use crate::vga::colors::*;
    use core::sync::atomic::{AtomicUsize, Ordering};
    use core::ptr::null_mut;

    const VGA_ADDRESS: usize = 0xB8000;
    const VGA_WIDTH: usize = 80;
    const VGA_HEIGHT: usize = 25;

    static mut VGA_MEMORY: *mut u16 = VGA_ADDRESS as *mut u16;
    static CURSOR_COL: AtomicUsize = AtomicUsize::new(0);
    static CURSOR_ROW: AtomicUsize = AtomicUsize::new(0);

    fn create_attr_byte(fg: u8, bg: u8) -> u8 {
        (bg << 4) | (fg & 0x0F)
    }

    /// Prints a character at a specified position with specified foreground and background colors.
    pub fn vga_print_char(character: char, col: usize, row: usize, fg: u8, bg: u8) {
        if col >= VGA_WIDTH || row >= VGA_HEIGHT {
            return;
        }

        let offset = (row * VGA_WIDTH + col) as usize;
        let attribute_byte = create_attr_byte(fg, bg);
        let color = (attribute_byte as u16) << 8 | character as u16;

        if bg == TRANSPARENT {
            let current_cell = unsafe { VGA_MEMORY.add(offset).read_volatile() };
            let new_color = (attribute_byte as u16) << 8 | character as u16;
            unsafe { VGA_MEMORY.add(offset).write_volatile(new_color) };
        } else if fg == TRANSPARENT {
            let new_color = (attribute_byte as u16) << 8 | b' ' as u16;
            unsafe { VGA_MEMORY.add(offset).write_volatile(new_color) };
        } else {
            unsafe { VGA_MEMORY.add(offset).write_volatile(color) };
        }
    }

    pub fn vga_clear(fg: u8, bg: u8) {
        let clear_value = ((create_attr_byte(fg, bg) as u16) << 8) | b' ' as u16;
        
        for y in 0..VGA_HEIGHT {
            for x in 0..VGA_WIDTH {
                let offset = (y * VGA_WIDTH + x) as usize;
                unsafe { VGA_MEMORY.add(offset).write_volatile(clear_value) };
            }
        }

        CURSOR_COL.store(0, Ordering::SeqCst);
        CURSOR_ROW.store(0, Ordering::SeqCst);
    }

    pub fn vga_print_line(text: &str, fg: u8, bg: u8) {
        for c in text.chars() {
            match c {
                '\n' => {
                    CURSOR_COL.store(0, Ordering::SeqCst);
                    let row = CURSOR_ROW.fetch_add(1, Ordering::SeqCst);
                    if row >= VGA_HEIGHT {
                        scroll_up();
                    }
                },
                _ => {
                    let col = CURSOR_COL.load(Ordering::SeqCst);
                    if col >= VGA_WIDTH {
                        CURSOR_COL.store(0, Ordering::SeqCst);
                        let row = CURSOR_ROW.fetch_add(1, Ordering::SeqCst);
                        if row >= VGA_HEIGHT {
                            scroll_up();
                        }
                    }
                    vga_print_char(c, col, CURSOR_ROW.load(Ordering::SeqCst), fg, bg);
                    CURSOR_COL.fetch_add(1, Ordering::SeqCst);
                }
            }
        }
    }

    pub fn move_cursor(col: usize, row: usize) {
        if col < VGA_WIDTH && row < VGA_HEIGHT {
            CURSOR_COL.store(col, Ordering::SeqCst);
            CURSOR_ROW.store(row, Ordering::SeqCst);
        }
    }

    pub fn check_char(x: usize, y: usize) -> bool {
        if x >= VGA_WIDTH || y >= VGA_HEIGHT {
            return false;
        }

        let offset = (y * VGA_WIDTH + x) as usize;
        let color = unsafe { VGA_MEMORY.add(offset).read_volatile() };
        let character = (color & 0xFF) as u8;
        character != b' '
    }

    pub fn print_at(x: usize, y: usize, text: &str, fg: u8, bg: u8) {
        for (i, c) in text.chars().enumerate() {
            let pos_x = x + i;
            if pos_x < VGA_WIDTH && y < VGA_HEIGHT {
                vga_print_char(c, pos_x, y, fg, bg);
            } else {
                break;
            }
        }
    }

    pub fn printp(text: &str, fg: u8, bg: u8) {
        let mut x = CURSOR_COL.load(Ordering::SeqCst);
        let mut y = CURSOR_ROW.load(Ordering::SeqCst);

        for c in text.chars() {
            match c {
                '\n' => {
                    x = 0;
                    y += 1;
                    if y >= VGA_HEIGHT {
                        scroll_up();
                    }
                },
                _ => {
                    if x >= VGA_WIDTH {
                        x = 0;
                        y += 1;
                    }
                    if y >= VGA_HEIGHT {
                        scroll_up();
                    }
                    vga_print_char(c, x, y, fg, bg);
                    x += 1;
                }
            }
        }

        move_cursor(x, y);
    }

    pub fn get_pointer_position() -> (usize, usize) {
        (CURSOR_COL.load(Ordering::SeqCst), CURSOR_ROW.load(Ordering::SeqCst))
    }

    fn scroll_up() {
        let clear_value = ((create_attr_byte(WHITE, BLACK) as u16) << 8) | b' ' as u16;
        let line_size = VGA_WIDTH;
        let total_size = VGA_WIDTH * (VGA_HEIGHT - 1);

        unsafe {
            for y in 1..VGA_HEIGHT {
                for x in 0..VGA_WIDTH {
                    let old_offset = (y * VGA_WIDTH + x) as usize;
                    let new_offset = ((y - 1) * VGA_WIDTH + x) as usize;
                    let value = VGA_MEMORY.add(old_offset).read_volatile();
                    VGA_MEMORY.add(new_offset).write_volatile(value);
                }
            }

            for i in total_size..VGA_HEIGHT * VGA_WIDTH {
                VGA_MEMORY.add(i).write_volatile(clear_value);
            }
        }
        CURSOR_ROW.store(VGA_HEIGHT - 1, Ordering::SeqCst);
    }
}