use spin::{Mutex, MutexGuard}; pub const fn color_state_from_colors(fg: Color, bg: Color) -> u8 { fg as u8 | ((bg as u8) << 4) } const DEFAULT_COLOR_STATE: u8 = color_state_from_colors(Color::White, Color::Black); const DEFAULT_VGA_TEXT_BUFFER: *mut VgaChar = 0xb8000 as *mut VgaChar; /// A character of Code page 437 with a foreground and background color state. #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[repr(transparent)] pub struct VgaChar(u16); impl VgaChar { const NULL: Self = Self(0); const fn from_bytes(bytes: [u8; 2]) -> Self { Self(u16::from_ne_bytes(bytes)) } pub const fn from_byte(byte: u8) -> Self { Self::from_bytes([byte, DEFAULT_COLOR_STATE]) } pub const fn from_byte_with_color_state(byte: u8, color_state: u8) -> Self { Self::from_bytes([byte, color_state]) } pub fn set_byte(&mut self, byte: u8) { let byte = u16::to_ne_bytes(self.0)[0]; self.0 = u16::from_ne_bytes([byte, byte]); } pub fn set_color(&mut self, byte: u8) { let color = u16::to_ne_bytes(self.0)[1]; self.0 = u16::from_ne_bytes([byte, color]); } } impl From for VgaChar { fn from(byte: u8) -> Self { Self::from_byte(byte) } } #[allow(dead_code)] #[repr(u8)] pub enum Color { Black = 0, Blue = 1, Green = 2, Cyan = 3, Red = 4, Magenta = 5, Brown = 6, LightGray = 7, DarkGray = 8, LightBlue = 9, LightGreen = 10, LightCyan = 11, LightRed = 12, Pink = 13, Yellow = 14, White = 15, } pub struct VgaTerminalController { width: usize, height: usize, area: usize, start: *mut VgaChar, cursor: Mutex<*mut VgaChar>, } unsafe impl Send for VgaTerminalController {} unsafe impl Sync for VgaTerminalController {} pub struct VgaTerminalControllerLock<'a> { color_state: u8, controller: &'a VgaTerminalController, lock: MutexGuard<'a, *mut VgaChar>, } pub static DEFAULT_VGA_TERMINAL_CONTROLLER: VgaTerminalController = unsafe { VgaTerminalController::new( core::num::NonZeroUsize::new_unchecked(80), core::num::NonZeroUsize::new_unchecked(25), DEFAULT_VGA_TEXT_BUFFER, ) }; impl VgaTerminalController { pub fn lock(&self) -> VgaTerminalControllerLock { VgaTerminalControllerLock { color_state: DEFAULT_COLOR_STATE, controller: self, lock: self.cursor.lock(), } } pub const fn width(&self) -> usize { self.width } pub const fn height(&self) -> usize { self.height } pub const fn area(&self) -> usize { self.area } pub const fn address(&self) -> *mut VgaChar { self.start } /// Creates a new VGA terminal controller with a width, height and an memory mapped I/O /// address to the VGA text buffer. /// /// # Safety /// The address must be a memory mapped I/O VGA text buffer with the given width and height. /// Otherwise UB is to be expected. pub const unsafe fn new( width: core::num::NonZeroUsize, height: core::num::NonZeroUsize, addr: *mut VgaChar, ) -> Self { let area = width.get() * height.get(); Self { width: width.get(), height: height.get(), area, start: addr, cursor: Mutex::new(addr), } } } impl<'a> VgaTerminalControllerLock<'a> { pub const fn controller(&self) -> &'a VgaTerminalController { self.controller } #[inline] const fn start(&self) -> *mut VgaChar { self.controller.start } #[inline] const fn area(&self) -> usize { self.controller.area } pub const fn width(&self) -> usize { self.controller.width } pub const fn height(&self) -> usize { self.controller.height } fn move_up(&mut self, lines: usize) { let offset = lines * self.controller.width; assert!(offset <= self.area()); unsafe { core::intrinsics::volatile_copy_memory( self.start(), self.start().offset(offset as isize), self.area() - offset, ); } let pos = self.area() - offset; *self.lock = unsafe { self.start().offset(pos as isize) }; unsafe { core::intrinsics::volatile_set_memory(*self.lock, 0u8, offset) } } pub fn set_color_state(&mut self, fg: Color, bg: Color) { self.color_state = color_state_from_colors(fg, bg); } pub fn clear(&mut self) { unsafe { core::intrinsics::volatile_set_memory(*self.lock, 0u8, self.area()); *self.lock = self.start(); } } pub fn get_coord(&self) -> isize { unsafe { self.lock.offset_from(self.start()) } } pub fn set_coord(&mut self, n: usize) { assert!(n < self.area()); *self.lock = unsafe { self.start().offset(n as isize) }; } pub fn put_vga_chars(&mut self, chars: &[VgaChar]) { if chars.len() >= self.area() { *self.lock = self.start(); self.put_vga_chars(&chars[chars.len() - self.area()..]); } else { let end = self.get_coord() + chars.len() as isize; if end > self.area() as isize { self.move_up( (end + self.controller.width as isize - 1 - self.area() as isize) as usize / self.controller.width, ); } unsafe { core::intrinsics::volatile_copy_memory(*self.lock, chars.as_ptr(), chars.len()); *self.lock = self.lock.offset(chars.len() as isize); } } } pub fn put_const_byte_str(&mut self, bytes: [u8; N]) { self.put_vga_chars(&bytes.map(|b| VgaChar::from_byte_with_color_state(b, self.color_state))) } pub fn put_byte_str(&mut self, bytes: &[u8]) { for &byte in bytes { self.put_vga_chars(&[VgaChar::from_byte_with_color_state(byte, self.color_state)]); } } pub fn put_arguments<'b>(&mut self, args: core::fmt::Arguments<'b>) { let _ = core::fmt::write( &mut WritableVgaTerminalControllerLock { controller: self }, args, ); } pub fn new_line(&mut self) { let line = self.get_coord() / self.controller.width as isize; let line = if line + 1 >= self.controller.height as isize { self.move_up(1); line } else { line + 1 }; *self.lock = unsafe { self.start().offset(line * self.controller.width as isize) }; } pub fn new_lines(&mut self, n: usize) { // TODO: optimize for _ in 0..n { self.new_line() } } } struct WritableVgaTerminalControllerLock<'a, 'b> { controller: &'a mut VgaTerminalControllerLock<'b>, } impl<'a, 'b> core::fmt::Write for WritableVgaTerminalControllerLock<'a, 'b> { fn write_str(&mut self, s: &str) -> core::fmt::Result { Ok(self.controller.put_byte_str(s.as_bytes())) } } #[macro_export] macro_rules! vga_width { () => { $crate::io::vga_text::DEFAULT_VGA_TERMINAL_CONTROLLER.width() }; } #[macro_export] macro_rules! vga_height { () => { $crate::io::vga_text::DEFAULT_VGA_TERMINAL_CONTROLLER.height() }; } #[macro_export] macro_rules! vga_lock { () => { $crate::io::vga_text::DEFAULT_VGA_TERMINAL_CONTROLLER.lock() }; } #[macro_export] macro_rules! vga_print { (fg: $fg:expr, bg: $bg:expr, $($arg:tt)*) => {{ let mut lock = $crate::vga_lock!(); lock.set_color_state($fg, $bg); lock.put_arguments(core::format_args!($($arg)*)); }}; ($($arg:tt)*) => { $crate::vga_lock!().put_arguments(core::format_args!($($arg)*)) }; } #[macro_export] macro_rules! vga_println { () => { $crate::io::vga_text::DEFAULT_VGA_TERMINAL_CONTROLLER.lock().new_line() }; (fg: $fg:expr, bg: $bg:expr, $($arg:tt)*) => {{ let mut lock = $crate::vga_lock!(); lock.set_color_state($fg, $bg); lock.put_arguments(core::format_args!($($arg)*)); lock.new_line(); }}; ($($arg:tt)*) => {{ let mut lock = $crate::vga_lock!(); lock.put_arguments(core::format_args!($($arg)*)); lock.new_line(); }}; } #[macro_export] macro_rules! vga_clear { () => { $crate::vga_lock!().clear() }; (fg: $fg:expr, bg: $bg:expr) => {{ let mut lock = $crate::vga_lock!(); lock.set_color_state($fg, $bg); lock.clear(); }}; }