use core::fmt; use spin::{Lazy, Mutex}; use x86_64::instructions::interrupts; pub use super::framebuffer::screensize_px; use super::framebuffer::{Color, FRAMEBUFFER_WRITER}; mod font; use font::FONT; static FONT_WIDTH: u32 = 8; static FONT_HEIGHT: u32 = 16; pub static WRITER: Lazy> = Lazy::new(|| Mutex::new(Writer::new())); pub fn screensize_chars() -> (u32, u32) { let writer = WRITER.lock(); (writer.screen_width, writer.screen_height) } pub struct Writer { /// Measured in chars not pixels. screen_width: u32, /// Measured in chars not pixels. screen_height: u32, /// 16 pixels tall. text_line: u32, /// 8 pixels wide. text_col: u32, fg_color: Color, bg_color: Color, offset1: usize, offset2: usize, } impl Writer { pub fn new() -> Self { if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { Self { screen_width: writer.width() as u32 / 8, screen_height: writer.height() as u32 / 16, text_line: 0, text_col: 0, fg_color: Color::White, bg_color: Color::Black, offset1: 16, offset2: 0, } } else { panic!("Framebuffer writer not initialized"); } } pub fn write_char(&mut self, c: u16) { if c as u8 == b'\n' { self.newline(); return; } // get the character data from the font array. -- each byte is a row of pixels let data: &[u8] = &FONT[c as usize * 16..(c as usize + 1) * 16]; if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { for row in 0..16 { let line: u8 = data[row]; for col in 0..8 { let pixel_x: u32 = self.text_col * FONT_WIDTH + col; let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32; if line & (0x80 >> col) != 0 { // write the foreground color writer.write_pixel(pixel_x as usize, pixel_y as usize, self.fg_color); } else { // write the background color writer.write_pixel(pixel_x as usize, pixel_y as usize, self.bg_color); } } } } // go to next position if self.text_col + 1 >= self.screen_width { self.newline(); } else { self.text_col += 1; } } pub fn set_offset(&mut self, offset1: usize, offset2: usize) { self.offset1 = offset1; self.offset2 = offset2; } pub fn dimensions(&self) -> (u32, u32) { (self.screen_width, self.screen_height) } pub fn next_char(&mut self) { self.text_col += 1; } pub fn newline(&mut self) { self.text_col = 0; if self.text_line + 1 >= self.screen_height { self.text_line = 0; } else { self.text_line += 1; } } pub fn write_string(&mut self, s: &str) { for c in s.chars() { self.write_char(c as u16); } } pub fn set_colour(&mut self, fg: Color, bg: Color) { self.fg_color = fg; self.bg_color = bg; } pub fn reset_colour(&mut self) { self.fg_color = Color::White; self.bg_color = Color::Black; } } impl core::fmt::Write for Writer { fn write_str(&mut self, s: &str) -> core::fmt::Result { self.write_string(s); Ok(()) } } fn write(args: fmt::Arguments, fg: Color, bg: Color) { use core::fmt::Write; interrupts::without_interrupts(|| { let mut writer = WRITER.lock(); writer.set_colour(fg, bg); writer.write_fmt(args).unwrap(); writer.reset_colour(); }); } pub fn _print(args: fmt::Arguments) { x86_64::instructions::interrupts::without_interrupts(|| { write(args, Color::White, Color::Black); }) } pub fn _print_err(args: fmt::Arguments) { x86_64::instructions::interrupts::without_interrupts(|| { write(args, Color::Red, Color::Black); }) } pub fn _print_log(args: fmt::Arguments) { x86_64::instructions::interrupts::without_interrupts(|| { write(args, Color::Yellow, Color::Black); }) } pub fn clear_screen() { interrupts::without_interrupts(|| { let mut writer = WRITER.lock(); writer.text_line = 0; writer.text_col = 0; if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { writer.clear(); } }); } #[macro_export] macro_rules! println_log { () => ($crate::print_log!("\n")); ($($arg:tt)*) => ($crate::print_log!("{}\n", format_args!($($arg)*))); } #[macro_export] macro_rules! print_log { ($($arg:tt)*) => ($crate::io::ascii::_print_log(format_args!($($arg)*))); } #[macro_export] macro_rules! println { () => ($crate::print!("\n")); ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } #[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::io::ascii::_print(format_args!($($arg)*))); } #[macro_export] macro_rules! printlnerr { () => ($crate::printerr!("\n")); ($($arg:tt)*) => ($crate::printerr!("{}\n", format_args!($($arg)*))); } #[macro_export] macro_rules! printerr { ($($arg:tt)*) => ($crate::_print_err(format_args!($($arg)*))); }