use core::fmt; use lazy_static::lazy_static; use spin::Mutex; use volatile::Volatile; const BUFFER_HEIGHT: usize = 25; const BUFFER_WIDTH: usize = 80; lazy_static! { pub static ref WRITER: Mutex = Mutex::new(Writer { column: 0, color_code: ColorCode::new(Color::LightGreen, Color::Black), buffer: unsafe { &mut *(0xb8000 as *mut VgaBuffer) } }); } #[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::vga::_print(format_args!($($arg)*))); } #[macro_export] macro_rules! println { () => ($crate::print!("\n")); ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } #[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; use x86_64::instructions::interrupts; interrupts::without_interrupts(|| { WRITER.lock().write_fmt(args).unwrap(); }); } pub fn change_color(color: ColorCode) { WRITER.lock().change_color(color) } pub fn get_color() -> ColorCode { WRITER.lock().get_color() } #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum Color { Black = 0x0, Blue = 0x1, Green = 0x2, Cyan = 0x3, Red = 0x4, Magenta = 0x5, Brown = 0x6, LightGray = 0x7, DarkGray = 0x8, LightBlue = 0x9, LightGreen = 0xa, LightCyan = 0xb, LightRed = 0xc, Pink = 0xd, Yellow = 0xe, White = 0xf, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] pub struct ColorCode(u8); impl ColorCode { pub fn new(fg: Color, bg: Color) -> ColorCode { ColorCode((bg as u8) << 4 | (fg as u8)) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] struct VgaChar { character: u8, color_code: ColorCode, } #[repr(transparent)] struct VgaBuffer { chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], } pub struct Writer { column: usize, color_code: ColorCode, buffer: &'static mut VgaBuffer, } impl Writer { pub fn write_string(&mut self, s: &str) { for byte in s.bytes() { match byte { 0x20..=0x7e | b'\n' => self.write_byte(byte), _ => self.write_byte(0xfe), } } } pub fn write_byte(&mut self, byte: u8) { match byte { b'\n' => self.new_line(), byte => { if self.column >= BUFFER_WIDTH { self.new_line(); } let row = BUFFER_HEIGHT - 1; let col = self.column; let color_code = self.color_code; self.buffer.chars[row][col].write(VgaChar { character: byte, color_code, }); self.column += 1; } } } fn new_line(&mut self) { for row in 1..BUFFER_HEIGHT { for col in 0..BUFFER_WIDTH { let character = self.buffer.chars[row][col].read(); self.buffer.chars[row - 1][col].write(character); } } self.clear_row(BUFFER_HEIGHT - 1); self.column = 0; } fn clear_row(&mut self, row: usize) { let blank = VgaChar { character: b' ', color_code: self.color_code, }; for col in 0..BUFFER_WIDTH { self.buffer.chars[row][col].write(blank); } } fn change_color(&mut self, color: ColorCode) { self.color_code = color; } fn get_color(&mut self) -> ColorCode { self.color_code } } impl fmt::Write for Writer { fn write_str(&mut self, s: &str) -> fmt::Result { self.write_string(s); Ok(()) } }