hal_x86_64/
vga.rs

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
23/// # Safety
24/// fuck off
25pub unsafe fn init_with_offset(offset: u64) {
26    // lmao
27    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        // XXX(eliza): it would be cool if this could be a memset...
146        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        // XXX(eliza): it would be cool if this could also be a ``smemset`.
157        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    /// Forcibly unlock the VGA buffer's mutex.
203    ///
204    /// If a lock is currently held, it will be released, regardless of who's
205    /// holding it. Of course, this is **outrageously, disgustingly unsafe** and
206    /// you should never do it.
207    ///
208    /// # Safety
209    ///
210    /// This deliberately violates mutual exclusion.
211    ///
212    /// Only call this method when it is _guaranteed_ that no stack frame that
213    /// has previously locked the mutex will ever continue executing.
214    /// Essentially, this is only okay to call when the kernel is oopsing and
215    /// all code running on other cores has already been killed.
216    #[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}