/ src / vga_buffer.rs
vga_buffer.rs
  1  use volatile::Volatile;
  2  use core::fmt;
  3  use lazy_static::lazy_static;
  4  use spin::Mutex;
  5  
  6  #[allow(dead_code)]
  7  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  8  #[repr(u8)]
  9  pub enum Color {
 10      Black = 0,
 11      Blue = 1,
 12      Green = 2,
 13      Cyan = 3,
 14      Red = 4,
 15      Magenta = 5,
 16      Brown = 6,
 17      LightGray = 7,
 18      DarkGray = 8,
 19      LightBlue = 9,
 20      LightGreen = 10,
 21      LightCyan = 11,
 22      LightRed = 12,
 23      Pink = 13,
 24      Yellow = 14,
 25      White = 15,
 26  }
 27  
 28  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 29  #[repr(transparent)]
 30  struct ColorCode(u8);
 31  
 32  impl ColorCode {
 33      fn new(foreground: Color, background: Color) -> ColorCode {
 34          ColorCode((background as u8) << 4 | (foreground as u8))
 35      }
 36  }
 37  
 38  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 39  #[repr(C)]
 40  struct ScreenChar {
 41      ascii_character: u8,
 42      color_code: ColorCode,
 43  }
 44  
 45  const BUFFER_HEIGHT: usize = 25;
 46  const BUFFER_WIDTH: usize = 80;
 47  
 48  #[repr(transparent)]
 49  struct Buffer {
 50      chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
 51  }
 52  
 53  pub struct Writer {
 54      column_position: usize,
 55      color_code: ColorCode,
 56      buffer: &'static mut Buffer,
 57  }
 58  
 59  impl Writer {
 60      pub fn write_byte(&mut self, byte: u8) {
 61          match byte {
 62              b'\n' => self.new_line(),
 63              byte => {
 64                  if self.column_position >= BUFFER_WIDTH {
 65                      self.new_line();
 66                  }
 67  
 68                  let row = BUFFER_HEIGHT - 1;
 69                  let col = self.column_position;
 70  
 71                  let color_code = self.color_code;
 72                  self.buffer.chars[row][col].write(ScreenChar {
 73                      ascii_character: byte,
 74                      color_code,
 75                  });
 76                  self.column_position += 1;
 77              }
 78          }
 79      }
 80  
 81      pub fn write_string(&mut self, s: &str) {
 82          for byte in s.bytes() {
 83              match byte {
 84                  0x20..=0x7e | b'\n' => self.write_byte(byte),
 85                  _ => self.write_byte(0xfe),
 86              }
 87          }
 88      }
 89  
 90      fn new_line(&mut self) {
 91          for row in 1..BUFFER_HEIGHT {
 92              for col in 0..BUFFER_WIDTH {
 93                  let character = self.buffer.chars[row][col].read();
 94                  self.buffer.chars[row - 1][col].write(character);
 95              }
 96          }
 97          self.clear_row(BUFFER_HEIGHT - 1);
 98          self.column_position = 0;
 99      }
100  
101      fn clear_row(&mut self, row: usize) {
102          let blank = ScreenChar {
103              ascii_character: b' ',
104              color_code: self.color_code,
105          };
106          for col in 0..BUFFER_WIDTH {
107              self.buffer.chars[row][col].write(blank);
108          }
109      }
110  }
111  
112  impl fmt::Write for Writer {
113      fn write_str(&mut self, s: &str) -> fmt::Result {
114          self.write_string(s);
115          Ok(())
116      }
117  }
118  
119  lazy_static! {
120      pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
121          column_position: 0,
122          color_code: ColorCode::new(Color::Red, Color::Black),
123          buffer: unsafe { &mut *(0xb8000 as *mut Buffer)  },
124      });
125  }
126  
127  #[macro_export]
128  macro_rules! print {
129      ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
130  }
131  
132  #[macro_export]
133  macro_rules! println {
134      () => ($crate::print!("\n"));
135      ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
136  }
137  
138  #[doc(hidden)]
139  pub fn _print(args: fmt::Arguments) {
140      use core::fmt::Write;
141      use x86_64::instructions::interrupts;
142  
143      interrupts::without_interrupts(|| {
144          WRITER.lock().write_fmt(args).unwrap();
145      });
146  }
147  
148  #[test_case]
149  fn test_println_simple() {
150      println!("test_println_simple output");
151  }
152  
153  #[test_case]
154  fn test_println_many() {
155      for _ in 0..200 {
156          println!("test_println_many output");
157      }
158  }
159  
160  #[test_case]
161  fn test_println_output() {
162      use core::fmt::Write;
163      use x86_64::instructions::interrupts;
164  
165      let s = "Some test string that fits on a single line";
166      interrupts::without_interrupts(|| {
167          let mut writer = WRITER.lock();
168          writeln!(writer, "\n{}", s).expect("writeln failed");
169          for (i, c) in s.chars().enumerate() {
170              let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
171              assert_eq!(char::from(screen_char.ascii_character), c);
172          }
173      });
174  }