1use core::fmt;
2use mycelium_util::{
3 io,
4 sync::{blocking::Mutex, spin::Spinlock, Lazy},
5};
6use volatile::Volatile;
7static BUFFER: Lazy<Mutex<Buffer, Spinlock>> = Lazy::new(|| {
8 Mutex::new_with_raw_mutex(
9 Buffer {
10 col: 0,
11 row: 0,
12 color: ColorSpec::new(Color::LightGray, Color::Black),
13 buf: Volatile::new(unsafe { &mut *(0xb8000u64 as *mut Buf) }),
14 },
15 Spinlock::new(),
16 )
17});
18
19pub fn writer() -> Writer {
20 Writer(())
21}
22
23pub unsafe fn init_with_offset(offset: u64) {
26 BUFFER.lock().buf = Volatile::new(&mut *((0xb8000u64 + offset) as *mut Buf));
28}
29
30#[derive(Debug)]
31pub struct Writer(());
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[repr(u8)]
35pub enum Color {
36 Black = 0,
37 Blue = 1,
38 Green = 2,
39 Cyan = 3,
40 Red = 4,
41 Magenta = 5,
42 Brown = 6,
43 LightGray = 7,
44 DarkGray = 8,
45 LightBlue = 9,
46 LightGreen = 10,
47 LightCyan = 11,
48 LightRed = 12,
49 Pink = 13,
50 Yellow = 14,
51 White = 15,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55#[repr(transparent)]
56pub struct ColorSpec(u8);
57
58pub struct Buffer {
59 col: usize,
60 row: usize,
61 color: ColorSpec,
62 buf: Volatile<&'static mut Buf>,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66#[repr(C)]
67struct Character {
68 ch: u8,
69 color: ColorSpec,
70}
71
72const BUF_HEIGHT: usize = 25;
73const BUF_WIDTH: usize = 80;
74
75type Buf = [[Character; BUF_WIDTH]; BUF_HEIGHT];
76
77impl ColorSpec {
78 pub const fn new(fg: Color, bg: Color) -> Self {
79 let bg = (bg as u8) << 4;
80 Self(bg | (fg as u8))
81 }
82}
83
84impl Buffer {
85 pub fn set_color(&mut self, color: ColorSpec) {
86 self.color = color;
87 }
88
89 fn blank(&self) -> Character {
90 self.character(b' ')
91 }
92
93 fn character(&self, ch: u8) -> Character {
94 Character {
95 ch,
96 color: self.color,
97 }
98 }
99
100 fn write(&mut self, s: &str) {
101 assert!(s.is_ascii(), "VGA buffer does not support Unicode");
102 for ch in s.bytes() {
103 self.write_char(ch);
104 }
105 }
106
107 fn write_char(&mut self, ch: u8) {
108 if ch == b'\n' {
109 self.newline();
110 return;
111 }
112
113 if self.col >= (BUF_WIDTH - 1) {
114 if self.row >= (BUF_HEIGHT - 1) {
115 self.newline();
116 } else {
117 self.row += 1;
118 self.col = 0;
119 }
120 }
121
122 let ch = self.character(ch);
123 self.buf
124 .as_mut_slice()
125 .index_mut(self.row)
126 .as_mut_slice()
127 .index_mut(self.col)
128 .write(ch);
129 self.col += 1;
130 }
131
132 fn newline(&mut self) {
133 let blank = self.blank();
134 let mut buf = self.buf.as_mut_slice();
135 if self.row >= (BUF_HEIGHT - 1) {
136 for row in 1..(BUF_HEIGHT - 1) {
137 buf.copy_within(row..=row, row);
138 }
139 } else {
140 self.row += 1;
141 }
142
143 let mut row = buf.index_mut(self.row);
144 let mut row = row.as_mut_slice();
145 for i in 0..BUF_WIDTH {
147 row.index_mut(i).write(blank);
148 }
149
150 self.col = 0;
151 }
152
153 fn clear(&mut self) {
154 let ch = self.character(b' ');
155 let mut buf = self.buf.as_mut_slice();
156 for row in 0..BUF_HEIGHT {
158 let mut row = buf.index_mut(row);
159 let mut row = row.as_mut_slice();
160 for col in 0..BUF_WIDTH {
161 row.index_mut(col).write(ch);
162 }
163 }
164 self.row = 0;
165 self.col = 0;
166 }
167}
168
169impl fmt::Write for Buffer {
170 fn write_str(&mut self, s: &str) -> fmt::Result {
171 self.write(s);
172 Ok(())
173 }
174}
175
176impl fmt::Debug for Buffer {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 let Self {
179 row,
180 col,
181 color,
182 buf: _,
183 } = self;
184 f.debug_struct("vga::Buffer")
185 .field("row", row)
186 .field("col", col)
187 .field("color", color)
188 .field("buf", &format_args!("[..]"))
189 .finish()
190 }
191}
192
193impl Writer {
194 pub fn set_color(&mut self, color: ColorSpec) {
195 BUFFER.lock().set_color(color);
196 }
197
198 pub fn clear(&mut self) {
199 BUFFER.lock().clear();
200 }
201
202 #[doc(hidden)]
217 pub unsafe fn force_unlock(&mut self) {
218 BUFFER.force_unlock();
219 }
220}
221
222impl fmt::Write for Writer {
223 fn write_str(&mut self, s: &str) -> fmt::Result {
224 BUFFER.lock().write(s);
225 Ok(())
226 }
227}
228
229impl io::Write for Writer {
230 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
231 let mut lock = BUFFER.lock();
232 for &byte in buf.iter() {
233 lock.write_char(byte)
234 }
235 Ok(buf.len())
236 }
237
238 fn flush(&mut self) -> io::Result<()> {
239 Ok(())
240 }
241}