basic vga and vt driver

This commit is contained in:
2026-02-21 02:20:50 +01:00
parent c4c1a4350c
commit 24cfbc08fd
3 changed files with 256 additions and 17 deletions

View File

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

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

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