use super::*; use core::fmt; use volatile::Volatile; use crate::sync::NullLock; use crate::sync::interface::Mutex; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] struct ScreenChar { ascii_character: u8, color_code: ColorCode, } const BUFFER_HEIGHT: usize = 25; const BUFFER_WIDTH: usize = 80; #[repr(transparent)] struct Buffer { chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], } pub struct VgaWriter { column_position: usize, color_code: ColorCode, buffer: Option<&'static mut Buffer>, } impl VgaWriter { pub fn write_byte(&mut self, byte: u8) { match byte { b'\n' => self.new_line(), byte => { if self.column_position >= BUFFER_WIDTH { self.new_line(); } let row = BUFFER_HEIGHT - 1; let col = self.column_position; let color_code = self.color_code; if let Some(buffer) = &mut self.buffer { buffer.chars[row][col].write(ScreenChar { ascii_character: byte, color_code, }); } self.column_position += 1; } } } fn new_line(&mut self) { for row in 1..BUFFER_HEIGHT { for col in 0..BUFFER_WIDTH { if let Some(buffer) = &mut self.buffer { let character = buffer.chars[row][col].read(); buffer.chars[row - 1][col].write(character); } } } self.clear_row(BUFFER_HEIGHT - 1); self.column_position = 0; } fn clear_row(&mut self, row: usize) { let blank = ScreenChar { ascii_character: b' ', color_code: self.color_code, }; for col in 0..BUFFER_WIDTH { if let Some(buffer) = &mut self.buffer { buffer.chars[row][col].write(blank); } } } pub fn write_string(&mut self, s: &str) { for byte in s.bytes() { match byte { // printable ASCII byte or newline 0x20..=0x7e | b'\n' => self.write_byte(byte), // not part of printable ASCII range _ => self.write_byte(0xfe), } } } } impl NullLock { #[allow(dead_code)] pub fn write_byte(&self, byte: u8) { self.lock(|writer| { writer.write_byte(byte); }); } #[allow(dead_code)] fn new_line(&self) { self.lock(|writer| { writer.new_line(); }); } #[allow(dead_code)] fn clear_row(&self, row: usize) { self.lock(|writer| { writer.clear_row(row); }); } pub fn write_string(&self, s: &str) { self.lock(|writer| { writer.write_string(s); }); } } impl fmt::Write for VgaWriter { fn write_str(&mut self, s: &str) -> fmt::Result { self.write_string(s); Ok(()) } } impl NullLock { pub fn init(&self) { self.lock(|writer| { writer.buffer = Some(unsafe { &mut *(0xb8000 as *mut Buffer) }); }) } } #[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; WRITER.lock(|writer| { writer.write_fmt(args).unwrap(); }); } #[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::vga::_print(format_args!($($arg)*))); } #[macro_export] macro_rules! println { () => (print!("\n")); ($($arg:tt)*) => (print!("{}\n", format_args!($($arg)*))); } pub static WRITER: NullLock = NullLock::new(VgaWriter { column_position: 0, color_code: ColorCode::new(Color::Yellow, Color::Black), buffer: None, });