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