diff options
| author | Natasha Moongrave <natasha@256phi.eu> | 2026-02-17 19:07:01 +0100 |
|---|---|---|
| committer | Natasha Moongrave <natasha@256phi.eu> | 2026-02-17 19:07:01 +0100 |
| commit | 4649fc861a4a83dcf8b1b7daf06c8d0c1ede0ccb (patch) | |
| tree | 477527813ad48a118be0b85556d445fd19171d20 | |
| parent | 2699709a7a008b00373992f0e1510e439cbc25f5 (diff) | |
implemented a proper vga buffer and handling for println! and panic! macros in the buffer
| -rw-r--r-- | StrixKernel/src/vga_buffer.rs | 104 |
1 files changed, 84 insertions, 20 deletions
diff --git a/StrixKernel/src/vga_buffer.rs b/StrixKernel/src/vga_buffer.rs index 311187e..e2e388a 100644 --- a/StrixKernel/src/vga_buffer.rs +++ b/StrixKernel/src/vga_buffer.rs @@ -1,5 +1,20 @@ -// vga_buffer.rs +use core::fmt; +use lazy_static::lazy_static; +use spin::Mutex; +use volatile::Volatile; + +lazy_static! { + /// A global `Writer` instance that can be used for printing to the VGA text buffer. + /// + /// Used by the `print!` and `println!` macros. + pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer { + column_position: 0, + color_code: ColorCode::new(Color::Yellow, Color::Black), + buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, + }); +} +/// The standard color palette in VGA text mode. #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] @@ -22,16 +37,19 @@ pub enum Color { White = 15, } +/// A combination of a foreground and a background color. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] struct ColorCode(u8); impl ColorCode { + /// Create a new `ColorCode` with the given foreground and background colors. fn new(foreground: Color, background: Color) -> ColorCode { ColorCode((background as u8) << 4 | (foreground as u8)) } } +/// A screen character in the VGA text buffer, consisting of an ASCII character and a `ColorCode`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] struct ScreenChar { @@ -39,23 +57,31 @@ struct ScreenChar { color_code: ColorCode, } +/// The height of the text buffer (normally 25 lines). const BUFFER_HEIGHT: usize = 25; +/// The width of the text buffer (normally 80 columns). const BUFFER_WIDTH: usize = 80; +/// A structure representing the VGA text buffer. #[repr(transparent)] struct Buffer { - chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT], + chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT], } - +/// A writer type that allows writing ASCII bytes and strings to an underlying `Buffer`. +/// +/// Wraps lines at `BUFFER_WIDTH`. Supports newline characters and implements the +/// `core::fmt::Write` trait. pub struct Writer { column_position: usize, color_code: ColorCode, buffer: &'static mut Buffer, } - impl Writer { + /// Writes an ASCII byte to the buffer. + /// + /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. pub fn write_byte(&mut self, byte: u8) { match byte { b'\n' => self.new_line(), @@ -68,20 +94,21 @@ impl Writer { let col = self.column_position; let color_code = self.color_code; - self.buffer.chars[row][col] = ScreenChar { + self.buffer.chars[row][col].write(ScreenChar { ascii_character: byte, color_code, - }; + }); self.column_position += 1; } } } - fn new_line(&mut self) {/* TODO */} -} - -impl Writer { - pub fn write_string(&mut self, s: &str) { + /// Writes the given ASCII string to the buffer. + /// + /// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not** + /// support strings with non-ASCII characters, since they can't be printed in the VGA text + /// mode. + fn write_string(&mut self, s: &str) { for byte in s.bytes() { match byte { // printable ASCII byte or newline @@ -89,19 +116,56 @@ impl Writer { // not part of printable ASCII range _ => self.write_byte(0xfe), } + } + } + /// Shifts all lines one line up and clears the last row. + 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_position = 0; + } + + /// Clears a row by overwriting it with blank characters. + fn clear_row(&mut self, row: usize) { + let blank = ScreenChar { + ascii_character: b' ', + color_code: self.color_code, + }; + for col in 0..BUFFER_WIDTH { + self.buffer.chars[row][col].write(blank); } } } -pub fn print_something() { - let mut writer = Writer { - column_position: 0, - color_code: ColorCode::new(Color::Yellow, Color::Black), - buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, - }; +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_string(s); + Ok(()) + } +} - writer.write_byte(b'H'); - writer.write_string("ello "); - writer.write_string("Wörld!"); +/// Like the `print!` macro in the standard library, but prints to the VGA text buffer. +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); } + +/// Like the `println!` macro in the standard library, but prints to the VGA text buffer. +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); +} + +/// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance. +#[doc(hidden)] +pub fn _print(args: fmt::Arguments) { + use core::fmt::Write; + WRITER.lock().write_fmt(args).unwrap(); +}
\ No newline at end of file |
