mango-hal 0.2.1

Hardware Abstraction Layer for the mango operationg system.
Documentation
#[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],
}

// VgaTextBufferImpl

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
      {
        // printable ASCII byte or newline
        0x20..=0x7e | b'\n' => self.write_byte(byte),
        // not part of printable ASCII range
        _ => self.write_byte(0xfe),
      }
    }
  }
}

// VgaTextBuffer

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);
  }
}