summaryrefslogtreecommitdiff
path: root/src/vga/buffer.rs
diff options
context:
space:
mode:
authorChristian Cunningham <c@localhost>2022-08-22 20:54:54 -0700
committerChristian Cunningham <c@localhost>2022-08-22 20:54:54 -0700
commit188a08c3a340005d59d497e836993cb9349c9cbe (patch)
treea69cc6cc1ced12043b706dc62f5a1645d9061adb /src/vga/buffer.rs
Initial state
Diffstat (limited to 'src/vga/buffer.rs')
-rw-r--r--src/vga/buffer.rs157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/vga/buffer.rs b/src/vga/buffer.rs
new file mode 100644
index 0000000..34c665d
--- /dev/null
+++ b/src/vga/buffer.rs
@@ -0,0 +1,157 @@
+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<ScreenChar>; 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<VgaWriter> {
+ #[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<VgaWriter> {
+ 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<VgaWriter> = NullLock::new(VgaWriter {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: None,
+});