mango_hal/io/
vga_text_buffer.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2#[repr(u8)]
3#[allow(dead_code)]
4enum Color
5{
6 Black = 0,
7 Blue = 1,
8 Green = 2,
9 Cyan = 3,
10 Red = 4,
11 Magenta = 5,
12 Brown = 6,
13 LightGray = 7,
14 DarkGray = 8,
15 LightBlue = 9,
16 LightGreen = 10,
17 LightCyan = 11,
18 LightRed = 12,
19 Pink = 13,
20 Yellow = 14,
21 White = 15,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[repr(transparent)]
26struct ColorCode(u8);
27
28impl ColorCode
29{
30 fn new(foreground: Color, background: Color) -> ColorCode
31 {
32 ColorCode(((background as u8) << 4) | (foreground as u8))
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(C)]
38struct ScreenChar
39{
40 ascii_character: u8,
41 color_code: ColorCode,
42}
43
44const BUFFER_HEIGHT: usize = 25;
45const BUFFER_WIDTH: usize = 80;
46
47#[repr(transparent)]
48struct Buffer
49{
50 chars: [[volatile::Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
51}
52
53struct VgaTextBufferImpl
56{
57 column_position: usize,
58 color_code: ColorCode,
59 buffer: &'static mut Buffer,
60}
61
62impl VgaTextBufferImpl
63{
64 fn write_byte(&mut self, byte: u8)
65 {
66 match byte
67 {
68 b'\n' => self.new_line(),
69 byte =>
70 {
71 if self.column_position >= BUFFER_WIDTH
72 {
73 self.new_line();
74 }
75
76 let row = BUFFER_HEIGHT - 1;
77 let col = self.column_position;
78
79 let color_code = self.color_code;
80 self.buffer.chars[row][col].write(ScreenChar {
81 ascii_character: byte,
82 color_code,
83 });
84 self.column_position += 1;
85 }
86 }
87 }
88
89 fn new_line(&mut self)
90 {
91 for row in 1..BUFFER_HEIGHT
92 {
93 for col in 0..BUFFER_WIDTH
94 {
95 let character = self.buffer.chars[row][col].read();
96 self.buffer.chars[row - 1][col].write(character);
97 }
98 }
99 self.clear_row(BUFFER_HEIGHT - 1);
100 self.column_position = 0;
101 }
102 fn clear_row(&mut self, row: usize)
103 {
104 let blank = ScreenChar {
105 ascii_character: b' ',
106 color_code: self.color_code,
107 };
108 for col in 0..BUFFER_WIDTH
109 {
110 self.buffer.chars[row][col].write(blank);
111 }
112 }
113}
114
115impl VgaTextBufferImpl
116{
117 fn write_string(&mut self, s: &str)
118 {
119 for byte in s.bytes()
120 {
121 match byte
122 {
123 0x20..=0x7e | b'\n' => self.write_byte(byte),
125 _ => self.write_byte(0xfe),
127 }
128 }
129 }
130}
131
132pub struct VgaTextBuffer
135{
136 implementation: spin::Mutex<VgaTextBufferImpl>,
137}
138
139impl VgaTextBuffer
140{
141 pub fn new() -> VgaTextBuffer
142 {
143 VgaTextBuffer {
144 implementation: spin::Mutex::new(VgaTextBufferImpl {
145 column_position: 0,
146 color_code: ColorCode::new(Color::Yellow, Color::Black),
147 buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
148 }),
149 }
150 }
151 pub fn initialise(&self) {}
152}
153
154use core::fmt;
155
156impl fmt::Write for VgaTextBuffer
157{
158 fn write_str(&mut self, s: &str) -> fmt::Result
159 {
160 self.implementation.lock().write_string(s);
161 Ok(())
162 }
163}
164
165impl crate::devices::Device for VgaTextBuffer {}
166
167#[cfg(feature = "test_build")]
168pub fn test_vga_buffer_println_output(vga_text_buffer: &mut VgaTextBuffer)
169{
170 use core::fmt::Write;
171 let s = "Some test string that fits on a single line";
172 mango_core::println_to!(vga_text_buffer, "{}", s);
173 for (i, c) in s.chars().enumerate()
174 {
175 let screen_char =
176 vga_text_buffer.implementation.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
177 assert_eq!(char::from(screen_char.ascii_character), c);
178 }
179}