veridian_kernel/arch/x86_64/
vga.rs1use core::{fmt, ptr::write_volatile};
2
3use lazy_static::lazy_static;
4use spin::Mutex;
5
6#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[repr(u8)]
11pub enum Color {
12 Black = 0,
13 Blue = 1,
14 Green = 2,
15 Cyan = 3,
16 Red = 4,
17 Magenta = 5,
18 Brown = 6,
19 LightGray = 7,
20 DarkGray = 8,
21 LightBlue = 9,
22 LightGreen = 10,
23 LightCyan = 11,
24 LightRed = 12,
25 Pink = 13,
26 Yellow = 14,
27 White = 15,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[repr(transparent)]
32struct ColorCode(u8);
33
34impl ColorCode {
35 fn new(foreground: Color, background: Color) -> ColorCode {
36 ColorCode(((background as u8) << 4) | (foreground as u8))
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[repr(C)]
42struct ScreenChar {
43 ascii_character: u8,
44 color_code: ColorCode,
45}
46
47const BUFFER_HEIGHT: usize = 25;
48const BUFFER_WIDTH: usize = 80;
49
50#[repr(transparent)]
51struct Buffer {
52 chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
53}
54
55pub struct Writer {
56 column_position: usize,
57 color_code: ColorCode,
58 buffer: &'static mut Buffer,
59}
60
61impl Writer {
62 pub fn write_byte(&mut self, byte: u8) {
63 match byte {
64 b'\n' => self.new_line(),
65 byte => {
66 if self.column_position >= BUFFER_WIDTH {
67 self.new_line();
68 }
69
70 let row = BUFFER_HEIGHT - 1;
71 let col = self.column_position;
72
73 let color_code = self.color_code;
74 unsafe {
78 write_volatile(
79 &mut self.buffer.chars[row][col],
80 ScreenChar {
81 ascii_character: byte,
82 color_code,
83 },
84 );
85 }
86 self.column_position += 1;
87 }
88 }
89 }
90
91 fn new_line(&mut self) {
92 for row in 1..BUFFER_HEIGHT {
93 for col in 0..BUFFER_WIDTH {
94 let character = unsafe { core::ptr::read_volatile(&self.buffer.chars[row][col]) };
98 unsafe {
99 write_volatile(&mut self.buffer.chars[row - 1][col], character);
100 }
101 }
102 }
103 self.clear_row(BUFFER_HEIGHT - 1);
104 self.column_position = 0;
105 }
106
107 fn clear_row(&mut self, row: usize) {
108 let blank = ScreenChar {
109 ascii_character: b' ',
110 color_code: self.color_code,
111 };
112 for col in 0..BUFFER_WIDTH {
113 unsafe {
116 write_volatile(&mut self.buffer.chars[row][col], blank);
117 }
118 }
119 }
120
121 pub fn write_string(&mut self, s: &str) {
122 for byte in s.bytes() {
123 match byte {
124 0x20..=0x7e | b'\n' => self.write_byte(byte),
125 _ => self.write_byte(0xfe),
126 }
127 }
128 }
129}
130
131impl fmt::Write for Writer {
132 fn write_str(&mut self, s: &str) -> fmt::Result {
133 self.write_string(s);
134 Ok(())
135 }
136}
137
138lazy_static! {
139 pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
140 column_position: 0,
141 color_code: ColorCode::new(Color::White, Color::Black),
142 buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
147 });
148}
149
150#[doc(hidden)]
151pub fn _print(args: fmt::Arguments) {
152 use core::fmt::Write;
153
154 use x86_64::instructions::interrupts;
155
156 interrupts::without_interrupts(|| {
157 WRITER.lock().write_fmt(args).expect("VGA write_fmt failed");
158 });
159}