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 }