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