basic vga and vt driver
This commit is contained in:
147
members/oxide/src/vga/mod.rs
Normal file
147
members/oxide/src/vga/mod.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
//! Only designed to handle VGA modes where the display is composed of (u8_char, u8_color). Where
|
||||
//! u8_color is VgaColor
|
||||
|
||||
use core::{marker::PhantomData, ops::Deref};
|
||||
|
||||
pub mod vt;
|
||||
|
||||
/// Only ONE instance must exist at each time, represents ownership over the VGA memory region
|
||||
pub struct VgaBuffer<M: TextMode>(PhantomData<M>);
|
||||
|
||||
impl<M: TextMode> VgaBuffer<M> {
|
||||
const VGA_ADDR: *mut u8 = 0xb8000 as *mut u8;
|
||||
|
||||
pub fn set_mode() {
|
||||
todo!()
|
||||
// bios::video::set_video_mode(M::CODE);
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Only an instance mmust exist at a time
|
||||
pub unsafe fn new() -> Self {
|
||||
Self::set_mode();
|
||||
Self(PhantomData)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Only an instance mmust exist at a time and the vga state is assumed to be this one, it's
|
||||
/// not set
|
||||
pub unsafe fn new_uninitialized() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
|
||||
pub fn get_addr(&mut self, col: u16, row: u16) -> *mut u8 {
|
||||
// TODO: these assertions could be release ones too
|
||||
debug_assert!((0..(M::WIDTH)).contains(&col));
|
||||
debug_assert!((0..(M::HEIGHT)).contains(&row));
|
||||
|
||||
unsafe { Self::VGA_ADDR.add(2 * Into::<usize>::into(col + row * M::WIDTH)) }
|
||||
}
|
||||
|
||||
pub fn write_at(&mut self, col: u16, row: u16, ch: u8) {
|
||||
unsafe {
|
||||
*self.get_addr(col, row).offset(0) = ch;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn color_at(&mut self, col: u16, row: u16, color: VgaColor) {
|
||||
unsafe {
|
||||
*self.get_addr(col, row).offset(1) = *color;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn as_cell_array(&mut self) -> &mut [[(u8, u8); M::WIDTH as usize]; M::HEIGHT as usize]
|
||||
where
|
||||
[[(u8, u8); M::WIDTH as usize]; M::HEIGHT as usize]:,
|
||||
{
|
||||
unsafe { core::mem::transmute(Self::VGA_ADDR) }
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ColorNibble(u8);
|
||||
|
||||
impl ColorNibble {
|
||||
pub const BLACK: Self = Self(0b0000);
|
||||
|
||||
pub const RED: Self = Self(0b0100);
|
||||
pub const GREEN: Self = Self(0b0010);
|
||||
pub const BLUE: Self = Self(0b0001);
|
||||
|
||||
pub const YELLOW: Self = Self(0b0110);
|
||||
pub const CYAN: Self = Self(0b0011);
|
||||
pub const MAGENTA: Self = Self(0b0101);
|
||||
|
||||
pub const GRAY: Self = Self(0b1000);
|
||||
pub const GRAY2: Self = Self(0b0111);
|
||||
|
||||
pub const LIGHT_RED: Self = Self(0b1100);
|
||||
pub const LIGHT_GREEN: Self = Self(0b1010);
|
||||
pub const LIGHT_BLUE: Self = Self(0b1001);
|
||||
|
||||
pub const LIGHT_YELLOW: Self = Self(0b1110);
|
||||
pub const LIGHT_CYAN: Self = Self(0b1011);
|
||||
pub const LIGHT_MAGENTA: Self = Self(0b1101);
|
||||
|
||||
pub const WHITE: Self = Self(0b1111);
|
||||
|
||||
pub const fn set(n: u8) -> Self {
|
||||
assert!((0..=0x0f).contains(&n));
|
||||
Self(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl const Deref for ColorNibble {
|
||||
type Target = u8;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct VgaColor(pub u8);
|
||||
|
||||
impl VgaColor {
|
||||
pub const fn new(fg: ColorNibble, bg: ColorNibble) -> Self {
|
||||
Self((*bg << 4) | *fg)
|
||||
}
|
||||
}
|
||||
|
||||
impl const Deref for VgaColor {
|
||||
type Target = u8;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// https://en.wikipedia.org/wiki/VGA_text_mode#PC_common_text_modes
|
||||
pub trait TextMode {
|
||||
const CODE: u8;
|
||||
const WIDTH: u16;
|
||||
const HEIGHT: u16;
|
||||
}
|
||||
|
||||
pub mod text_modes {
|
||||
pub struct VGAText1;
|
||||
|
||||
impl super::TextMode for VGAText1 {
|
||||
const CODE: u8 = 1; // and 0
|
||||
const WIDTH: u16 = 40;
|
||||
const HEIGHT: u16 = 25;
|
||||
}
|
||||
|
||||
pub struct VGAText2;
|
||||
|
||||
impl super::TextMode for VGAText2 {
|
||||
const CODE: u8 = 2; // and 3
|
||||
const WIDTH: u16 = 80;
|
||||
const HEIGHT: u16 = 25;
|
||||
}
|
||||
}
|
||||
83
members/oxide/src/vga/vt.rs
Normal file
83
members/oxide/src/vga/vt.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use crate::vga::{ColorNibble, TextMode, VgaBuffer, VgaColor};
|
||||
|
||||
pub struct Writer<M: TextMode> {
|
||||
vga: VgaBuffer<M>,
|
||||
cur_color: VgaColor,
|
||||
pos: (u16, u16),
|
||||
}
|
||||
|
||||
impl<M: TextMode> Writer<M>
|
||||
where
|
||||
[[(u8, u8); M::WIDTH as usize]; M::HEIGHT as usize]:,
|
||||
{
|
||||
pub const fn new(vga: VgaBuffer<M>) -> Self {
|
||||
Self {
|
||||
vga,
|
||||
cur_color: VgaColor::new(super::ColorNibble::WHITE, super::ColorNibble::BLACK),
|
||||
pos: (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn carry(&mut self) {
|
||||
self.pos.0 = 0;
|
||||
if self.pos.1 >= M::HEIGHT {
|
||||
self.slide();
|
||||
} else {
|
||||
self.pos.1 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_cell(&mut self) {
|
||||
if self.pos.0 >= M::WIDTH {
|
||||
self.carry();
|
||||
} else {
|
||||
self.pos.0 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_at(&mut self, col: u16, row: u16) {
|
||||
self.pos = (M::WIDTH.min(col), M::HEIGHT.min(row));
|
||||
}
|
||||
|
||||
pub fn write(&mut self, data: &[u8]) {
|
||||
const BAD_CHAR_COL: VgaColor = VgaColor::new(ColorNibble::WHITE, ColorNibble::RED);
|
||||
|
||||
for &b in data {
|
||||
let (col, row) = self.pos;
|
||||
|
||||
match b {
|
||||
b'\n' => self.carry(),
|
||||
b'\r' => self.pos.0 = 0,
|
||||
// b'\t' => todo!(),
|
||||
// backspace
|
||||
0x08 | 127 => {
|
||||
if self.pos.0 > 0 {
|
||||
self.pos.0 -= 1
|
||||
}
|
||||
}
|
||||
d if d.is_ascii_graphic() || d == b' ' => {
|
||||
self.vga.write_at(col, row, b);
|
||||
self.vga.color_at(col, row, self.cur_color);
|
||||
self.next_cell();
|
||||
}
|
||||
_ => {
|
||||
self.vga.write_at(col, row, b'?');
|
||||
self.vga.color_at(col, row, BAD_CHAR_COL);
|
||||
self.next_cell();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slide(&mut self) {
|
||||
let rows = self.vga.as_cell_array();
|
||||
rows.rotate_left(1);
|
||||
|
||||
if let Some(last) = rows.last_mut() {
|
||||
for cell in last {
|
||||
cell.0 = b' ';
|
||||
cell.1 = *self.cur_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user