1use core::cell::Cell;
31use kernel::utilities::StaticRef;
32use tock_cells::volatile_cell::VolatileCell;
33
34use x86::registers::io::{inb, outb};
37
38#[repr(u8)]
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum Color {
42 Black = 0x0,
43 Blue = 0x1,
44 Green = 0x2,
45 Cyan = 0x3,
46 Red = 0x4,
47 Magenta = 0x5,
48 Brown = 0x6,
49 LightGray = 0x7,
50 DarkGray = 0x8,
51 LightBlue = 0x9,
52 LightGreen = 0xA,
53 LightCyan = 0xB,
54 LightRed = 0xC,
55 Pink = 0xD,
56 Yellow = 0xE,
57 White = 0xF,
58}
59
60#[repr(transparent)]
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub struct ColorCode(u8);
65
66impl ColorCode {
67 pub const fn new(fg: Color, bg: Color, blink: bool) -> Self {
71 let mut b = (fg as u8) & 0x0F;
72 b |= ((bg as u8) & 0x07) << 4;
73 if blink {
74 b |= 1 << 7;
75 }
76 Self(b)
77 }
78
79 #[inline(always)]
81 pub const fn as_u8(self) -> u8 {
82 self.0
83 }
84
85 #[inline(always)]
87 pub const fn from_u8(b: u8) -> Self {
88 Self(b)
89 }
90
91 #[inline(always)]
93 pub const fn fg(self) -> Color {
94 match self.0 & 0x0F {
95 0x0 => Color::Black,
96 0x1 => Color::Blue,
97 0x2 => Color::Green,
98 0x3 => Color::Cyan,
99 0x4 => Color::Red,
100 0x5 => Color::Magenta,
101 0x6 => Color::Brown,
102 0x7 => Color::LightGray,
103 0x8 => Color::DarkGray,
104 0x9 => Color::LightBlue,
105 0xA => Color::LightGreen,
106 0xB => Color::LightCyan,
107 0xC => Color::LightRed,
108 0xD => Color::Pink,
109 0xE => Color::Yellow,
110 _ => Color::White, }
112 }
113 #[inline(always)]
116 pub const fn bg(self) -> Color {
117 match (self.0 >> 4) & 0x07 {
118 0x0 => Color::Black,
119 0x1 => Color::Blue,
120 0x2 => Color::Green,
121 0x3 => Color::Cyan,
122 0x4 => Color::Red,
123 0x5 => Color::Magenta,
124 0x6 => Color::Brown,
125 _ => Color::LightGray, }
127 }
128 pub const fn blink(self) -> bool {
129 (self.0 & 0x80) != 0
130 }
131}
132
133#[non_exhaustive]
135#[derive(Clone, Copy, Debug, PartialEq, Eq)]
136pub enum VgaMode {
137 Text80x25,
138 Graphics640x480_16,
139 Graphics800x600_16,
140}
141
142const TEXT_BUFFER_ADDR: usize = 0xB8000;
147const TEXT_BUFFER_WIDTH: usize = 80;
149const TEXT_BUFFER_HEIGHT: usize = 25;
150const LFB_PHYS_BASE: u32 = 0xE0_00_0000;
152
153const VGA_CELLS: StaticRef<[VolatileCell<u16>; TEXT_BUFFER_WIDTH * TEXT_BUFFER_HEIGHT]> =
154 unsafe { StaticRef::new(TEXT_BUFFER_ADDR as *const _) };
155
156struct TextBuf {
164 cells: StaticRef<[VolatileCell<u16>; TEXT_BUFFER_WIDTH * TEXT_BUFFER_HEIGHT]>,
165}
166
167impl TextBuf {
168 #[inline(always)]
169 const fn new() -> Self {
170 Self { cells: VGA_CELLS }
171 }
172
173 #[inline(always)]
175 fn iter(&self) -> core::slice::Iter<'_, VolatileCell<u16>> {
176 self.cells[..].iter()
177 }
178
179 #[inline(always)]
181 fn rows(&self) -> core::slice::ChunksExact<'_, VolatileCell<u16>> {
182 self.cells[..].chunks_exact(TEXT_BUFFER_WIDTH)
183 }
184
185 #[inline(always)]
187 pub fn get(&self, row: usize, col: usize) -> Option<&VolatileCell<u16>> {
188 if row < TEXT_BUFFER_HEIGHT && col < TEXT_BUFFER_WIDTH {
189 Some(&self.cells[row * TEXT_BUFFER_WIDTH + col])
190 } else {
191 None
192 }
193 }
194}
195impl<'a> IntoIterator for &'a TextBuf {
196 type Item = &'a VolatileCell<u16>;
197 type IntoIter = core::slice::Iter<'a, VolatileCell<u16>>;
198 #[inline(always)]
199 fn into_iter(self) -> Self::IntoIter {
200 self.iter()
201 }
202}
203
204impl core::ops::Index<usize> for TextBuf {
205 type Output = VolatileCell<u16>;
206 #[inline(always)]
207 fn index(&self, i: usize) -> &Self::Output {
208 &self.cells[i]
209 }
210}
211
212impl core::ops::Index<(usize, usize)> for TextBuf {
213 type Output = VolatileCell<u16>;
214 #[inline(always)]
215 fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
216 &self.cells[row * TEXT_BUFFER_WIDTH + col] }
218}
219
220pub struct VgaDevice;
223
224impl VgaDevice {
225 const TEXT: TextBuf = TextBuf::new();
227 pub fn set_mode(mode: VgaMode) {
229 match mode {
230 VgaMode::Text80x25 => Self::program_text_mode(),
231 VgaMode::Graphics640x480_16 => panic!("VGA 640×480 mode not implemented"),
232 VgaMode::Graphics800x600_16 => panic!("VGA 800×600 mode not implemented"),
233 }
234 }
235
236 pub fn map_for_mode(mode: VgaMode, page_dir: &mut x86::registers::bits32::paging::PD) {
238 use x86::registers::bits32::paging::{PAddr, PDEntry, PDFlags, PDFLAGS};
239
240 if matches!(
241 mode,
242 VgaMode::Graphics640x480_16 | VgaMode::Graphics800x600_16
243 ) {
244 let pde_idx = (LFB_PHYS_BASE >> 22) as usize;
245 let pa = PAddr::from(LFB_PHYS_BASE);
246 let mut flags = PDFlags::new(0);
247 flags.write(PDFLAGS::P::SET + PDFLAGS::RW::SET + PDFLAGS::PS::SET);
248 page_dir[pde_idx] = PDEntry::new(pa, flags);
249 }
250 }
251
252 fn program_text_mode() {
255 unsafe {
257 outb(0x3D4, 0x11);
259 outb(0x3D5, 0x00);
260
261 inb(0x3DA);
263 }
264
265 for (idx, val) in [
270 (0x00, 0x00u8), (0x01, 0x01), (0x02, 0x02), (0x03, 0x03), (0x04, 0x04), (0x05, 0x05), (0x06, 0x14), (0x07, 0x07), (0x08, 0x38), (0x09, 0x39), (0x0A, 0x3A), (0x0B, 0x3B), (0x0C, 0x3C), (0x0D, 0x3D), (0x0E, 0x3E), (0x0F, 0x3F), (0x10, 0x0C), (0x12, 0x0F), ]
289 .iter()
290 .copied()
291 {
292 unsafe {
293 outb(0x3C0, idx);
295 outb(0x3C0, val);
297 }
298 }
299
300 unsafe {
302 inb(0x3DA);
303
304 outb(0x3C0, 0x20);
306 }
307 }
308}
309
310pub struct Vga {
314 col: Cell<usize>,
315 row: Cell<usize>,
316 attr: Cell<u8>,
321}
322impl Vga {
323 pub const fn new() -> Self {
324 Self {
325 col: Cell::new(0),
326 row: Cell::new(0),
327 attr: Cell::new(ColorCode::new(Color::LightGray, Color::Black, false).as_u8()),
329 }
330 }
331
332 fn update_hw_cursor(&self) {
333 let pos = (self.row.get() * TEXT_BUFFER_WIDTH + self.col.get()) as u16;
334 unsafe {
335 outb(0x3D4, 0x0F);
336 outb(0x3D5, (pos & 0xFF) as u8);
337 outb(0x3D4, 0x0E);
338 outb(0x3D5, (pos >> 8) as u8);
339 }
340 }
341
342 fn scroll_up(&self) {
343 let blank = ((self.attr.get() as u16) << 8) | b' ' as u16;
344
345 let rows = VgaDevice::TEXT.rows();
347 let src_rows = rows.clone().skip(1);
348 let dst_rows = rows;
349 for (src_row, dst_row) in src_rows.zip(dst_rows) {
350 for (src, dst) in src_row.iter().zip(dst_row.iter()) {
351 dst.set(src.get()); }
353 }
354
355 if let Some(last_row) = VgaDevice::TEXT.rows().last() {
357 for cell in last_row {
358 cell.set(blank);
359 }
360 }
361
362 self.row.set(TEXT_BUFFER_HEIGHT - 1);
363 self.col.set(0);
364 }
365
366 pub fn set_cursor(&self, col: usize, row: usize) {
367 if VgaDevice::TEXT.get(row, col).is_some() {
368 self.col.set(col);
369 self.row.set(row);
370 self.update_hw_cursor();
371 }
372 }
373
374 #[inline(always)]
376 pub fn set_color_code(&self, code: ColorCode) {
377 self.attr.set(code.as_u8());
378 }
379
380 #[inline(always)]
382 pub fn set_colors(&self, fg: Color, bg: Color, blink: bool) {
383 self.set_color_code(ColorCode::new(fg, bg, blink));
384 }
385
386 #[inline(always)]
388 pub fn color_code(&self) -> ColorCode {
389 ColorCode::from_u8(self.attr.get())
390 }
391
392 pub fn clear(&self) {
393 let blank = ((self.attr.get() as u16) << 8) | b' ' as u16;
394 for cell in &VgaDevice::TEXT {
395 cell.set(blank);
396 }
397 self.col.set(0);
398 self.row.set(0);
399 self.update_hw_cursor();
400 }
401
402 pub fn write_byte(&self, byte: u8) {
403 match byte {
404 b'\n' => {
405 self.col.set(0);
406 self.row.set(self.row.get() + 1);
407 }
408 b'\r' => {
409 self.col.set(0);
410 }
411 b => {
412 let val = ((self.attr.get() as u16) << 8) | b as u16;
413
414 if let Some(cell) = VgaDevice::TEXT.get(self.row.get(), self.col.get()) {
416 cell.set(val);
417 } else {
418 self.scroll_up();
419 if let Some(cell) = VgaDevice::TEXT.get(self.row.get(), self.col.get()) {
420 cell.set(val);
421 }
422 }
423
424 let mut col = self.col.get() + 1;
426 let mut row = self.row.get();
427 if col >= TEXT_BUFFER_WIDTH {
428 col = 0;
429 row += 1;
430 }
431 self.col.set(col);
432 self.row.set(row);
433 }
434 }
435
436 if self.row.get() >= TEXT_BUFFER_HEIGHT {
438 self.scroll_up();
439 }
440
441 self.update_hw_cursor();
442 }
443}
444
445const _: () = {
446 match VgaMode::Text80x25 {
448 VgaMode::Text80x25 => (),
449 VgaMode::Graphics640x480_16 => (),
450 VgaMode::Graphics800x600_16 => (),
451 }
452};
453
454pub fn framebuffer() -> Option<(*mut u8, usize)> {
456 None
457}
458
459pub(crate) fn new_text_console(_page_dir_ptr: &mut x86::registers::bits32::paging::PD) {
461 VgaDevice::set_mode(VgaMode::Text80x25);
463
464 let blank: u16 = 0x0720; for cell in &VgaDevice::TEXT {
467 cell.set(blank);
468 }
469}