From 77f6a313810c302bb25b2fa03228b9d40760702c Mon Sep 17 00:00:00 2001 From: Julien CLEMENT Date: Thu, 2 Dec 2021 18:05:35 +0100 Subject: [PATCH] feat(vga): add simple vga interface Signed-off-by: Julien CLEMENT --- Cargo.lock | 26 +++++++++++ Cargo.toml | 8 ++++ src/lib.rs | 38 +++------------- src/vga.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 31 deletions(-) create mode 100644 src/vga.rs diff --git a/Cargo.lock b/Cargo.lock index 10a21f1..329d2c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,3 +5,29 @@ version = 3 [[package]] name = "julios" version = "0.1.0" +dependencies = [ + "lazy_static", + "spin", + "volatile", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "volatile" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945" diff --git a/Cargo.toml b/Cargo.toml index cd4a4ff..17eda98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,11 @@ edition = "2018" [lib] crate-type = ["staticlib"] + +[dependencies] +volatile = "0.2.6" +spin = "0.5.2" + +[dependencies.lazy_static] +version = "1.0" +features = ["spin_no_std"] diff --git a/src/lib.rs b/src/lib.rs index 2eb77d1..03571a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,40 +2,16 @@ #![no_main] #[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! -{ +fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } -static HELLO: &[u8] = b"Welcome to the JuliOS"; - -static STAR: &[u8] = b"|/-\\"; +use core::fmt::Write; +mod vga; #[no_mangle] -pub extern "C" fn julios_main() -> ! -{ - let vga_buffer: *mut u8 = 0xb8000 as *mut u8; - - let mut k: usize = 0; - for (i, &byte) in HELLO.iter().enumerate() - { - unsafe - { - *vga_buffer.offset(i as isize * 2) = byte; - *vga_buffer.offset(i as isize * 2 + 1) = 0xb; - } - k = i * 2 + 2; - } - - let mut j: usize = 0; - loop - { - unsafe - { - *vga_buffer.offset(k as isize) = STAR[j]; - *vga_buffer.offset(k as isize + 1) = 0xb; - j += 1; - j %= 4; - } - } +pub extern "C" fn julios_main() -> ! { + vga::WRITER.lock().write_str("Hello").unwrap(); + write!(vga::WRITER.lock(), " {}!\n{}", "World", "***JuliOS***").unwrap(); + loop {} } diff --git a/src/vga.rs b/src/vga.rs new file mode 100644 index 0000000..c69de7e --- /dev/null +++ b/src/vga.rs @@ -0,0 +1,125 @@ +use volatile::Volatile; +use core::fmt::{self, Write}; +use lazy_static::lazy_static; +use spin::Mutex; + +const BUFFER_HEIGHT: usize = 25; +const BUFFER_WIDTH: usize = 80; + +lazy_static! { + pub static ref WRITER: Mutex = Mutex::new(Writer { + column: 0, + color_code: ColorCode::new(Color::Green, Color::Black), + buffer: unsafe { &mut *(0xb8000 as *mut VgaBuffer) } + }); +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum Color { + Black = 0x0, + Blue = 0x1, + Green = 0x2, + Cyan = 0x3, + Red = 0x4, + Magenta = 0x5, + Brown = 0x6, + LightGray = 0x7, + DarkGray = 0x8, + LightBlue = 0x9, + LightGreen = 0xa, + LightCyan = 0xb, + LightRed = 0xc, + Pink = 0xd, + Yellow = 0xe, + White = 0xf, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +struct ColorCode(u8); + +impl ColorCode { + fn new(fg: Color, bg: Color) -> ColorCode { + ColorCode((bg as u8) << 4 | (fg as u8)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +struct VgaChar { + character: u8, + color_code: ColorCode, +} + +#[repr(transparent)] +struct VgaBuffer { + chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], +} + +pub struct Writer { + column: usize, + color_code: ColorCode, + buffer: &'static mut VgaBuffer, +} + +impl Writer { + pub 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 fn write_byte(&mut self, byte: u8) { + match byte { + b'\n' => self.new_line(), + byte => { + if self.column >= BUFFER_WIDTH { + self.new_line(); + } + + let row = BUFFER_HEIGHT - 1; + let col = self.column; + + let color_code = self.color_code; + self.buffer.chars[row][col].write(VgaChar { + character: byte, + color_code + }); + self.column += 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 = 0; + } + + fn clear_row(&mut self, row: usize) { + let blank = VgaChar { + character: b' ', + color_code: self.color_code + }; + for col in 0..BUFFER_WIDTH { + self.buffer.chars[row][col].write(blank); + } + } +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_string(s); + Ok(()) + } +}