mango_hal/io/
vga_text_buffer.rs

1#[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
53// VgaTextBufferImpl
54
55struct 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        // printable ASCII byte or newline
124        0x20..=0x7e | b'\n' => self.write_byte(byte),
125        // not part of printable ASCII range
126        _ => self.write_byte(0xfe),
127      }
128    }
129  }
130}
131
132// VgaTextBuffer
133
134pub 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}