basic vga and vt driver
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![deny(clippy::float_arithmetic)]
|
||||
#![feature(
|
||||
const_range,
|
||||
const_trait_impl,
|
||||
const_convert,
|
||||
generic_const_exprs,
|
||||
associated_type_defaults
|
||||
)]
|
||||
|
||||
/// Triggers a linker failure if this reaches that phase
|
||||
#[panic_handler]
|
||||
@@ -14,30 +21,32 @@ fn panic(_info: &PanicInfo) -> ! {
|
||||
|
||||
use core::{arch::asm, panic::PanicInfo};
|
||||
|
||||
use crate::vga::VgaBuffer;
|
||||
|
||||
pub mod multiboot2;
|
||||
pub mod vga;
|
||||
|
||||
#[used]
|
||||
#[unsafe(link_section = ".multiboot2_header")]
|
||||
static HEADER: multiboot2::Header = multiboot2::Header::new();
|
||||
|
||||
const VGA_BUFFER: *mut u8 = 0xb8000 as *mut u8;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
fn start() -> ! {
|
||||
// notes
|
||||
// 1st nibble bg
|
||||
// 2nd nibble fg
|
||||
//
|
||||
// 2: green 010
|
||||
// 4: red 100
|
||||
// F: white FFFF
|
||||
// 1: blue 001
|
||||
// conclussion: 4bit LRGB where L: Lightness
|
||||
unsafe {
|
||||
*VGA_BUFFER.offset(0) = b'O';
|
||||
*VGA_BUFFER.offset(2) = b'K';
|
||||
*VGA_BUFFER.offset(1) = 0x2F;
|
||||
*VGA_BUFFER.offset(3) = 0xAF;
|
||||
}
|
||||
let mut vga = unsafe { VgaBuffer::<vga::text_modes::VGAText2>::new_uninitialized() };
|
||||
|
||||
vga.write_at(0, 1, b'H');
|
||||
vga.write_at(1, 2, b'E');
|
||||
vga.write_at(2, 3, b'Y');
|
||||
|
||||
let mut vt = vga::vt::Writer::new(vga);
|
||||
vt.put_at(10, 5);
|
||||
vt.slide();
|
||||
vt.write(b"testinggg");
|
||||
// loop {
|
||||
// for x in b'a'..b'z' {
|
||||
// vt.write(&[x]);
|
||||
// }
|
||||
// }
|
||||
|
||||
unsafe { asm!("hlt", "ud2", options(noreturn)) };
|
||||
}
|
||||
|
||||
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