From ef18911471b96e6dba49008c65ad0ae961576acc Mon Sep 17 00:00:00 2001 From: Eemeli <eemeli.o.lehtonen@utu.fi> Date: Wed, 28 Dec 2022 03:18:20 +0200 Subject: [PATCH] vga println --- Cargo.lock | 41 +++++++ Cargo.toml | 3 + src/arch/x86_64/start.asm | 15 +-- src/lib.rs | 10 +- src/vga.rs | 249 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 307 insertions(+), 11 deletions(-) create mode 100644 src/vga.rs diff --git a/Cargo.lock b/Cargo.lock index b3a59ea..5124ebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,47 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "hyperion" version = "0.1.0" +dependencies = [ + "spin", + "volatile", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "spin" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +dependencies = [ + "lock_api", +] + +[[package]] +name = "volatile" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c" diff --git a/Cargo.toml b/Cargo.toml index 0a1f4f7..793abe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,6 @@ edition = "2021" crate-type = ["staticlib"] [dependencies] +spin = "0.9.4" +volatile = "0.4.5" +#tracing = { version = "0.1.37", default-features = false } diff --git a/src/arch/x86_64/start.asm b/src/arch/x86_64/start.asm index e4b6d60..6bfa821 100644 --- a/src/arch/x86_64/start.asm +++ b/src/arch/x86_64/start.asm @@ -57,17 +57,17 @@ start: error: ;; print 'ERR: <err>' - mov dword [0xb8000], 0x4f524f45 - mov dword [0xb8004], 0x4f3a4f52 - mov dword [0xb8008], 0x4f204f20 + mov dword [0xb8000], 0x4F524F45 + mov dword [0xb8004], 0x4F3A4F52 + mov dword [0xb8008], 0x4F204F20 mov byte [0xb800a], al jmp halt halt: ;; print ZZZ - mov word [0xb8f00], 0x0f5a - mov word [0xb8f02], 0x0f5a - mov word [0xb8f04], 0x0f5a + mov word [0xb8f00], 0x0F5A + mov word [0xb8f02], 0x0F5A + mov word [0xb8f04], 0x0F5A hlt jmp halt @@ -199,8 +199,9 @@ long_mode_start: mov gs, ax ; print 'OK' - mov dword [0xb8000], 0x2f4b2f4f + mov dword [0xb8000], 0x2F4B2F4F + mov rdi, 42 call kernel_main .halt: hlt diff --git a/src/lib.rs b/src/lib.rs index eda8e74..ed0fe1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +pub mod vga; + #[panic_handler] fn panic_handler(_: &core::panic::PanicInfo) -> ! { loop {} @@ -8,10 +10,10 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! { #[no_mangle] #[link_section = ".boot"] -pub extern "C" fn kernel_main() -> ! { - unsafe { - *(0xB8000 as *mut u32) = 0x4f524f45; - } +pub extern "C" fn kernel_main(magic_num: u64) -> ! { + // null byte clears the VGA buffer + print!("\0"); + println!("Hello from Hyperion, magic_num = {magic_num}"); loop { unsafe { diff --git a/src/vga.rs b/src/vga.rs new file mode 100644 index 0000000..690d638 --- /dev/null +++ b/src/vga.rs @@ -0,0 +1,249 @@ +use core::{ + fmt::{Arguments, Write}, + ops::{Deref, DerefMut}, +}; +use spin::{Mutex, MutexGuard}; +use volatile::Volatile; + +// + +#[macro_export] +macro_rules! println { + () => { + println!(""); + }; + + ($($arg:tt)*) => { + $crate::vga::_println(format_args!($($arg)*)) + } +} + +#[macro_export] +macro_rules! print { + () => { + print!(""); + }; + + ($($arg:tt)*) => { + $crate::vga::_print(format_args!($($arg)*)) + }; +} + +// + +pub struct Writer { + cursor: [usize; 2], + color: ColorCode, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[repr(C)] +pub enum Color { + #[default] + Black = 0, + Blue = 1, + Green = 2, + Cyan = 3, + Red = 4, + Magenta = 5, + Brown = 6, + LightGrey = 7, + DarkGrey = 8, + LightBlue = 9, + LightGreen = 10, + LightCyan = 11, + LightRed = 12, + Pink = 13, + Yellow = 14, + White = 15, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct ColorCode(u8); + +// + +impl Writer { + pub fn lock() -> MutexGuard<'static, Self> { + WRITER.lock() + } + + pub fn write_str(&mut self, s: &str) { + for byte in s.bytes() { + self.write_byte(byte); + } + } + + pub fn write_char(&mut self, c: char) { + self.write_str(c.encode_utf8(&mut [0; 4])) + } + + pub fn write_byte(&mut self, byte: u8) { + match byte { + // 'special' ascii chars + b'\r' => self.cursor[0] = 0, + b'\n' => self.new_line(), + b'\0' => self.clear(), + + // 'normal' ascii chars + byte => { + // line wrapping + if self.cursor[0] >= WIDTH { + self.new_line(); + } + + // insert the byte + self.set_char( + self.cursor, + Char { + byte, + color: self.color, + }, + ); + + // move the cursor + self.cursor[0] += 1; + } + } + } + + pub fn clear(&mut self) { + self.cursor = [0, 0]; + for row in 0..HEIGHT { + self.clear_row(row); + } + } + + /// SAFETY: Only one [`Writer`] should ever exist + const unsafe fn new() -> Self { + Self { + cursor: [0, 0], + color: ColorCode::new(Color::White, Color::Black), + } + } + + fn buffer(&self) -> &'static [[Volatile<Char>; WIDTH]; HEIGHT] { + // SAFETY: Only one [`Writer`] should ever exist + // then multiple immutable refs are allowed + unsafe { &*(0xB8000 as *const _) } + } + + fn buffer_mut(&mut self) -> &'static mut [[Volatile<Char>; WIDTH]; HEIGHT] { + // SAFETY: Only one [`Writer`] should ever exist + // then one mutable ref is allowed + unsafe { &mut *(0xB8000 as *mut _) } + } + + fn new_line(&mut self) { + if self.cursor[1] + 1 >= HEIGHT { + // move all rows upwards + for row in 0..HEIGHT - 1 { + for col in 0..WIDTH { + self.set_char([col, row], self.get_char([col, row + 1])); + } + } + } else { + // next row + self.cursor[1] += 1; + } + self.clear_row(HEIGHT - 1); + self.cursor[0] = 0; + } + + fn clear_row(&mut self, row: usize) { + self.fill_row( + row, + Char { + byte: b' ', + color: ColorCode::default(), + }, + ) + } + + fn fill_row(&mut self, row: usize, fill: Char) { + for col in 0..WIDTH { + self.set_char([col, row], fill); + } + } + + fn get_char(&self, cursor: [usize; 2]) -> Char { + self.buffer()[cursor[1]][cursor[0]].read() + } + + fn set_char(&mut self, cursor: [usize; 2], ch: Char) { + self.buffer_mut()[cursor[1]][cursor[0]].write(ch); + } +} + +impl ColorCode { + pub const fn new(fg: Color, bg: Color) -> ColorCode { + ColorCode((bg as u8) << 4 | (fg as u8)) + } +} + +impl Default for ColorCode { + fn default() -> Self { + Self::new(Color::White, Color::Black) + } +} + +// + +const WIDTH: usize = 80; +const HEIGHT: usize = 25; + +// + +/// SAFETY: safe, because this is the only Writer +static WRITER: Mutex<Writer> = Mutex::new(unsafe { Writer::new() }); + +// + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +struct Char { + // ascii + byte: u8, + + // foreground and background + color: ColorCode, +} + +// + +impl Write for Writer { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.write_str(s); + Ok(()) + } +} + +impl Deref for Char { + type Target = Self; + + fn deref(&self) -> &Self::Target { + self + } +} + +impl DerefMut for Char { + fn deref_mut(&mut self) -> &mut Self::Target { + self + } +} + +// + +#[doc(hidden)] +pub fn _print(args: Arguments) { + let mut writer = WRITER.lock(); + writer.write_fmt(args).unwrap(); +} + +#[doc(hidden)] +pub fn _println(args: Arguments) { + let mut writer = WRITER.lock(); + writer.write_fmt(args).unwrap(); + writer.write_byte(b'\n'); +} -- GitLab