#[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 const WIDTH: usize = 80; pub const HEIGHT: usize = 25; #[derive(Clone, Copy)] #[repr(C, packed)] pub struct CharState(pub u8); impl CharState { pub const fn from_colors(fg: Color, bg: Color) -> Self { Self((fg as u8) | ((bg as u8) << 4)) } pub fn set_fg(&mut self, fg: Color) { self.0 = (self.0 & ((HEIGHT as u8) - 10)) | (fg as u8) } pub fn set_bg(&mut self, bg: Color) { self.0 = (self.0 & 15) | ((bg as u8) << 4) } } #[derive(Clone, Copy)] #[repr(C, packed)] pub struct VgaChar { pub byte: u8, pub state: CharState, } impl VgaChar { pub fn from_state_and_byte(state: CharState, byte: u8) -> Self { Self { byte, state } } } pub struct OStream { pos: (usize, usize), cursor: *mut VgaChar, state: CharState, centered_mode: bool, } impl OStream { pub const fn new() -> Self { Self { pos: (0, 0), cursor: Self::at(0), state: CharState::from_colors(Color::White, Color::Black), centered_mode: false, } } const fn at(n: usize) -> *mut VgaChar { (0xb8000 + (n << 1)) as *mut VgaChar } fn compute_cursor(&mut self) { self.cursor = Self::at(self.pos.0 as usize + self.pos.1 as usize * WIDTH) } pub fn set_col(&mut self, col: usize) { self.pos.0 = core::cmp::min(col, WIDTH - 1); self.compute_cursor() } pub fn set_row(&mut self, row: usize) { self.pos.1 = core::cmp::min(row, HEIGHT - 1); self.compute_cursor() } pub fn set_cursor(&mut self, col: usize, row: usize) { self.pos = ( core::cmp::min(col, WIDTH - 1), core::cmp::min(row, HEIGHT - 1), ); self.compute_cursor() } pub fn set_char(&mut self, c: VgaChar) { unsafe { self.cursor.write_volatile(c) } } pub fn put_char(&mut self, c: VgaChar) { if c.byte == b'\n' { self.new_line(); } else if self.pos.0 >= WIDTH { self.new_line(); self.put_char(c); } else { self.set_char(c); self.cursor = self.cursor.wrapping_offset(1); self.pos.0 += 1; } } pub fn put_byte(&mut self, b: u8) { self.put_char(VgaChar::from_state_and_byte(self.state, b)) } pub fn clear(&self) { let c = VgaChar::from_state_and_byte(self.state, b' '); for i in 0..(WIDTH * HEIGHT) { unsafe { Self::at(i).write_volatile(c) } } } pub fn set_centered(&mut self, b: bool) { self.centered_mode = b } pub fn new_line(&mut self) { if self.pos.1 >= HEIGHT - 1 { self.set_col(0); for i in 0..1920 { unsafe { Self::at(i).write_volatile(*Self::at(i + WIDTH)) } } } else { self.set_cursor(0, self.pos.1 + 1); } } pub fn set_state(&mut self, state: CharState) { self.state = state } pub fn put_bytes(&mut self, s: &[u8]) { for &b in s { self.put_byte(b) } } pub fn print(&mut self, s: &[u8]) { if self.centered_mode { self.print_centered(s) } else { self.put_bytes(s) } } pub fn print_centered(&mut self, s: &[u8]) { for line in s.split(|&c| c == b'\n') { if line.is_empty() { self.new_line() } for chunk in line.chunks(WIDTH) { self.set_col((WIDTH - chunk.len()) >> 1); self.put_bytes(chunk); self.new_line() } } } } impl core::fmt::Write for OStream { fn write_str(&mut self, s: &str) -> core::fmt::Result { self.print(s.as_bytes()); Ok(()) } }