kernel/io/console/
vga_text.rs

1//! A temporary tool to allow for easy printing to the screen.
2//! We are using the VGA text mode buffer to print to the screen.
3
4use crate::{memory_management::virtual_space::VirtualSpace, multiboot2};
5
6use super::{VideoConsole, VideoConsoleAttribute};
7
8/// White on black text
9const DEFAULT_ATTRIB: u8 = 0x0f;
10
11pub(super) struct VgaText {
12    pos: (usize, usize),
13    attrib: u8,
14    pitch: usize,
15    height: usize,
16    width: usize,
17    memory: VirtualSpace<[u8]>,
18}
19
20impl VgaText {
21    pub fn new(framebuffer: multiboot2::Framebuffer) -> Self {
22        assert!(matches!(
23            framebuffer.color_info,
24            multiboot2::FramebufferColorInfo::EgaText
25        ));
26        let physical_addr = framebuffer.addr;
27        let memory_size = framebuffer.pitch * framebuffer.height;
28        let memory =
29            unsafe { VirtualSpace::new_slice(physical_addr, memory_size as usize).unwrap() };
30
31        Self {
32            pos: (0, 0),
33            attrib: DEFAULT_ATTRIB,
34            pitch: framebuffer.pitch as usize,
35            height: framebuffer.height as usize,
36            width: framebuffer.width as usize,
37            memory,
38        }
39    }
40
41    fn get_arr_pos(&self, pos: (usize, usize)) -> usize {
42        pos.0 * 2 + pos.1 * self.pitch
43    }
44
45    fn fix_after_advance(&mut self) {
46        if self.pos.0 >= self.width {
47            self.pos.0 = 0;
48            self.pos.1 += 1;
49        }
50        if self.pos.1 >= self.height {
51            // scroll up
52            for i in 0..self.height - 1 {
53                let copy_to = self.get_arr_pos((0, i));
54                let copy_from = self.get_arr_pos((0, i + 1));
55                let size = self.pitch;
56                let (before, after) = self.memory.split_at_mut(copy_from);
57
58                before[copy_to..copy_to + size].copy_from_slice(&after[..size]);
59            }
60
61            self.pos.1 -= 1;
62            self.clear_line(self.pos.1);
63            // just to make sure we are not out of bounds by more than 1 line
64            self.fix_after_advance();
65        }
66    }
67
68    #[allow(dead_code)]
69    fn clear(&mut self) {
70        for i in 0..self.height {
71            self.clear_line(i);
72        }
73        self.pos = (0, 0);
74    }
75
76    fn clear_line(&mut self, line: usize) {
77        for i in 0..self.width {
78            let pos = self.get_arr_pos((i, line));
79
80            self.memory[pos] = b' ';
81            self.memory[pos + 1] = 0x0;
82        }
83    }
84}
85
86impl VideoConsole for VgaText {
87    fn init(&mut self) {
88        self.clear();
89    }
90
91    fn set_attrib(&mut self, attrib: VideoConsoleAttribute) {
92        let to_vga_color = |color: u8| {
93            let mappings = &[
94                0,  // black
95                4,  // red
96                2,  // green
97                6,  // brown
98                1,  // blue
99                5,  // magenta
100                3,  // cyan
101                7,  // light gray
102                8,  // dark gray
103                12, // light red
104                10, // light green
105                14, // yellow
106                9,  // light blue
107                13, // light magenta
108                11, // light cyan
109                15, // white
110            ];
111            mappings[color as usize]
112        };
113
114        let mut fg_index = attrib.foreground as u8;
115        if attrib.faint && fg_index >= 8 {
116            fg_index -= 8;
117        }
118        if attrib.bold && fg_index < 8 {
119            fg_index += 8;
120        }
121
122        let fg = to_vga_color(fg_index);
123        let bg = to_vga_color(attrib.background as u8);
124        self.attrib = (bg << 4) | fg;
125    }
126
127    fn write_byte(&mut self, c: u8) {
128        if c == b'\n' {
129            self.pos.0 = 0;
130            self.pos.1 += 1;
131            self.fix_after_advance();
132            return;
133        }
134        let i = self.get_arr_pos(self.pos);
135        self.memory[i] = c;
136        self.memory[i + 1] = self.attrib;
137        self.pos.0 += 1;
138        self.fix_after_advance();
139    }
140
141    fn backspace(&mut self) {
142        if self.pos.0 == 0 {
143            if self.pos.1 == 0 {
144                return;
145            }
146            self.pos.0 = self.width - 1;
147            self.pos.1 -= 1;
148        } else {
149            self.pos.0 -= 1;
150        }
151        let i = self.get_arr_pos(self.pos);
152        self.memory[i] = b' ';
153        self.memory[i + 1] = self.attrib;
154    }
155}