#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[allow(dead_code)]
enum Color
{
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
struct ColorCode(u8);
impl ColorCode
{
fn new(foreground: Color, background: Color) -> ColorCode
{
ColorCode(((background as u8) << 4) | (foreground as u8))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar
{
ascii_character: u8,
color_code: ColorCode,
}
const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;
#[repr(transparent)]
struct Buffer
{
chars: [[volatile::Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
struct VgaTextBufferImpl
{
column_position: usize,
color_code: ColorCode,
buffer: &'static mut Buffer,
}
impl VgaTextBufferImpl
{
fn write_byte(&mut self, byte: u8)
{
match byte
{
b'\n' => self.new_line(),
byte =>
{
if self.column_position >= BUFFER_WIDTH
{
self.new_line();
}
let row = BUFFER_HEIGHT - 1;
let col = self.column_position;
let color_code = self.color_code;
self.buffer.chars[row][col].write(ScreenChar {
ascii_character: byte,
color_code,
});
self.column_position += 1;
}
}
}
fn new_line(&mut self)
{
for row in 1..BUFFER_HEIGHT
{
for col in 0..BUFFER_WIDTH
{
let character = self.buffer.chars[row][col].read();
self.buffer.chars[row - 1][col].write(character);
}
}
self.clear_row(BUFFER_HEIGHT - 1);
self.column_position = 0;
}
fn clear_row(&mut self, row: usize)
{
let blank = ScreenChar {
ascii_character: b' ',
color_code: self.color_code,
};
for col in 0..BUFFER_WIDTH
{
self.buffer.chars[row][col].write(blank);
}
}
}
impl VgaTextBufferImpl
{
fn write_string(&mut self, s: &str)
{
for byte in s.bytes()
{
match byte
{
0x20..=0x7e | b'\n' => self.write_byte(byte),
_ => self.write_byte(0xfe),
}
}
}
}
pub struct VgaTextBuffer
{
implementation: spin::Mutex<VgaTextBufferImpl>,
}
impl VgaTextBuffer
{
pub fn new() -> VgaTextBuffer
{
VgaTextBuffer {
implementation: spin::Mutex::new(VgaTextBufferImpl {
column_position: 0,
color_code: ColorCode::new(Color::Yellow, Color::Black),
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
}),
}
}
pub fn initialise(&self) {}
}
use core::fmt;
impl fmt::Write for VgaTextBuffer
{
fn write_str(&mut self, s: &str) -> fmt::Result
{
self.implementation.lock().write_string(s);
Ok(())
}
}
impl crate::devices::Device for VgaTextBuffer {}
#[cfg(feature = "test_build")]
pub fn test_vga_buffer_println_output(vga_text_buffer: &mut VgaTextBuffer)
{
use core::fmt::Write;
let s = "Some test string that fits on a single line";
mango_core::println_to!(vga_text_buffer, "{}", s);
for (i, c) in s.chars().enumerate()
{
let screen_char =
vga_text_buffer.implementation.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
}