use core::sync::atomic::{AtomicUsize, Ordering};
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;
}
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)
}
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);
}
}