From b75a7bc7ad401f38419e65b43fcc42b587fb486e Mon Sep 17 00:00:00 2001
From: c0repwn3r <core@coredoes.dev>
Date: Mon, 30 Jan 2023 20:59:44 -0500
Subject: [PATCH] partially working tty system

---
 Cargo.toml    |   1 +
 Makefile      |   4 +-
 src/lib.rs    |  60 ++++++++----------------
 src/macros.rs |   9 ++++
 src/tty.rs    | 126 ++++++++++++++++++++++++++++++++++++++++++++------
 5 files changed, 144 insertions(+), 56 deletions(-)
 create mode 100644 src/macros.rs

diff --git a/Cargo.toml b/Cargo.toml
index 924c98c..c22ca9a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ crate-type = ["staticlib"]
 
 [dependencies]
 lazy_static = {version = "1.4.0", features = ["spin_no_std"]}
+spin = "0.9.4"
 
 [profile.dev]
 panic = "abort"
diff --git a/Makefile b/Makefile
index 98f0678..594d353 100644
--- a/Makefile
+++ b/Makefile
@@ -4,8 +4,8 @@ ASMARGS := -f elf64
 LD := x86_64-elf-ld
 LDARGS := --nmagic
 
-CARGO_ENV := debug
-CARGO_EXTRA_ARG := #--release
+CARGO_ENV := release
+CARGO_EXTRA_ARG := --release
 
 .PHONY = target clean run rustc build debug
 default: build
diff --git a/src/lib.rs b/src/lib.rs
index 1c847cd..9c89f74 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,66 +2,44 @@
 #![feature(error_in_core)]
 #![feature(panic_info_message)]
 
+#[macro_use]
+pub mod macros;
 pub mod tty;
 pub mod drivers;
 pub mod unsafe_utils;
 
 
 use core::panic::PanicInfo;
-use crate::drivers::tty::vga::VGATTYDriver;
-use crate::tty::{Color, TTY, TTYCharacter, TTYDriver, TTYDriverError};
+use lazy_static::lazy_static;
+
+use crate::tty::{ROWS_IN_TTY, TTY};
 
 /// This is our kpanic function. This will eventually use any functioning video driver to dump the kernel, but for now it just hangs
 #[panic_handler]
 fn kpanic(_info: &PanicInfo) -> ! {
-    let panic_str = _info.location().unwrap();
+    // Completley reinitialize the tty system to ensure we can get the kpanic log out to vga.
+    println!();
+    println!("==== start kernel panic ====");
+    println!("{}", _info);
+    println!("==== end kernel panic ====");
     loop {}
 }
 
+use spin::Mutex;
+
+lazy_static! {
+    pub static ref ttys: Mutex<TTY<1>> = Mutex::new(TTY::new());
+}
+
 #[no_mangle]
 pub extern fn kmain() -> ! {
     // initialize ttys and the kernel buffer
     // just one for now
-    let vga_driver = VGATTYDriver {};
-    let mut ttys: TTY<2> = TTY::new();
-    ttys.select(0);
-    ttys.ttys[ttys.selected].screen_buffer[0][0] = TTYCharacter::new('s', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][1] = TTYCharacter::new('h', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][2] = TTYCharacter::new('a', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][3] = TTYCharacter::new('d', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][4] = TTYCharacter::new('e', Color::White, Color::Black);
+    ttys.lock().select(0);
 
-    ttys.select(1);
-    ttys.ttys[ttys.selected].screen_buffer[0][0] = TTYCharacter::new('o', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][1] = TTYCharacter::new('s', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][2] = TTYCharacter::new(' ', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][3] = TTYCharacter::new(':', Color::White, Color::Black);
-    ttys.ttys[ttys.selected].screen_buffer[0][4] = TTYCharacter::new(')', Color::White, Color::Black);
+    println!("hello from println");
 
-    match ttys.select_and_update(0, &vga_driver) {
-        Ok(_) => {}
-        Err(e) => {
-            match e {
-                TTYDriverError::ColOutOfBounds { got } => {
-                    vga_driver.set_char(0, 5, TTYCharacter::new('y', Color::Red, Color::Black)).unwrap();
-                    loop {}
-                },
-                TTYDriverError::RowOutOfBounds { got } => {
-                    vga_driver.set_char(0, 5, TTYCharacter::new('y', Color::Red, Color::Black)).unwrap();
-                    loop {}
-                }
-            }
-        }
-    }
-
-    for i in 0..2000000 {
-        continue;
-    }
-
-    ttys.select_and_update(1, &vga_driver).unwrap();
-
-    //vga_driver.set_char(0, 0, TTYCharacter::new('s', Color::White, Color::Black)).unwrap();
-    //vga_driver.set_char(0, 1, TTYCharacter::new('h', Color::White, Color::Black)).unwrap();
+    panic!("kpanic test");
 
     loop{}
 }
\ No newline at end of file
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..1fa14af
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,9 @@
+#[macro_export]
+macro_rules! print {
+    ($($arg:tt)*) => ($crate::tty::_print(format_args!($($arg)*)))
+}
+#[macro_export]
+macro_rules! println {
+    () => ($crate::print!("\n"));
+    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)))
+}
\ No newline at end of file
diff --git a/src/tty.rs b/src/tty.rs
index 435478a..e182840 100644
--- a/src/tty.rs
+++ b/src/tty.rs
@@ -1,9 +1,18 @@
 use core::error::Error;
-use core::fmt::{Display, Formatter};
+use core::fmt;
+use core::fmt::{Display, Formatter, Write};
+use crate::drivers::tty::vga::VGATTYDriver;
+use crate::ttys;
+
+pub fn _print(args: fmt::Arguments) {
+    ttys.lock().write_fmt(args).unwrap();
+    ttys.lock().update().unwrap();
+}
 
 pub struct TTY<const C: usize> {
     pub ttys: [VirtualTTY; C],
-    pub selected: usize
+    pub selected: usize,
+    driver: VGATTYDriver
 }
 impl<const C: usize> TTY<C> {
     pub fn new() -> TTY<C> {
@@ -11,25 +20,34 @@ impl<const C: usize> TTY<C> {
             ttys: [
                 VirtualTTY::new(); C
             ],
-            selected: 0
+            selected: 0,
+            driver: VGATTYDriver {}
         }
     }
 
     pub fn select(&mut self, new: usize) { self.selected = new; }
 
-    pub fn update(&self, driver: &impl TTYDriver) -> Result<(), TTYDriverError> {
+    pub fn update(&self) -> Result<(), TTYDriverError> {
         let selected_tty = &self.ttys[self.selected];
         for (row_num, row) in selected_tty.screen_buffer.iter().enumerate() {
             for (col_num, col) in row.iter().enumerate() {
-                driver.set_char(row_num, col_num, *col)?;
+                self.driver.set_char(row_num, col_num, *col)?;
             }
         }
         Ok(())
     }
 
-    pub fn select_and_update(&mut self, new: usize, driver: &impl TTYDriver) -> Result<(), TTYDriverError> {
+    pub fn handle(&self) -> &VirtualTTY {
+        &self.ttys[self.selected]
+    }
+
+    pub fn handle_mut(&mut self) -> &mut VirtualTTY {
+        &mut self.ttys[self.selected]
+    }
+
+    pub fn select_and_update(&mut self, new: usize) -> Result<(), TTYDriverError> {
         self.select(new);
-        self.update(driver)
+        self.update()
     }
 }
 
@@ -67,7 +85,7 @@ impl TTYCharacter {
     pub fn from(char: char) -> Self { Self { format: 0x0fu8, char }}
     pub fn new(char: char, fg: Color, bg: Color) -> Self {
         Self {
-            format: (fg as u8) << 4 | bg as u8,
+            format: (bg as u8) << 4 | fg as u8,
             char
         }
     }
@@ -80,16 +98,26 @@ pub const ROWS_IN_TTY: usize = 80;
 
 #[derive(Copy, Clone)]
 pub struct VirtualTTY {
-    pub full_buffer: VirtualTTYBuffer<ROWS_IN_TTY, COLS>,
-    pub screen_buffer: VirtualTTYBuffer<ROWS_ON_SCREEN, COLS>,
-    pub line_offset: usize
+    full_buffer: VirtualTTYBuffer<ROWS_IN_TTY, COLS>,
+    full_buffer_copy: VirtualTTYBuffer<ROWS_IN_TTY, COLS>,
+    screen_buffer: VirtualTTYBuffer<ROWS_ON_SCREEN, COLS>,
+    line_offset: usize,
+    cursor_col: usize,
+    cursor_row: usize,
+    current_color_fg: Color,
+    current_color_bg: Color
 }
 impl Default for VirtualTTY {
     fn default() -> Self {
         Self {
             full_buffer: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY],
+            full_buffer_copy: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY],
             screen_buffer: [[TTYCharacter::blank(); COLS]; ROWS_ON_SCREEN],
             line_offset: ROWS_IN_TTY - ROWS_ON_SCREEN, // assume we are scrolled all the way down
+            cursor_row: ROWS_IN_TTY - ROWS_ON_SCREEN,
+            current_color_fg: Color::White,
+            cursor_col: 0,
+            current_color_bg: Color::Black,
         }
     }
 }
@@ -97,6 +125,69 @@ impl VirtualTTY {
     pub fn new() -> Self {
         Self::default()
     }
+
+    pub fn set_color(&mut self, fg: Color, bg: Color) {
+        self.current_color_bg = bg;
+        self.current_color_fg = fg;
+    }
+
+    pub fn setcur(&mut self, cursor_x: usize, cursor_y: usize) {
+        self.cursor_row = cursor_y;
+        self.cursor_col = cursor_x;
+    }
+
+    pub fn putchar(&mut self, character: char) {
+        match character {
+            '\n' => {
+                self.print_newline();
+                self.cursor_col = 0;
+                self.cursor_row += 1;
+            },
+            _ => {
+                if self.cursor_col > COLS {
+                    self.print_newline();
+                    self.cursor_col = 0;
+                    self.cursor_row += 1;
+                }
+                self._putchar(character);
+                self.cursor_col += 1;
+            }
+        }
+    }
+
+    pub fn putstr(&mut self, str: &str) {
+        for char in str.chars() {
+            self.putchar(char);
+        }
+    }
+
+    fn _putchar(&mut self, character: char) {
+        self.full_buffer[self.cursor_row][self.cursor_col] = TTYCharacter::new(character, self.current_color_fg, self.current_color_bg);
+        self.update_screenbuf()
+    }
+
+    // shifts everything up one
+    fn print_newline(&mut self) {
+        for (line_no, line) in self.full_buffer.iter().skip(1).enumerate() {
+            self.full_buffer_copy[line_no] = *line;
+        }
+        self.full_buffer = self.full_buffer_copy;
+    }
+
+    pub fn set_screen_offset(&mut self, new_offset: usize) -> Result<(), ()> {
+        if new_offset > (ROWS_IN_TTY - ROWS_ON_SCREEN) {
+            return Err(())
+        }
+        self.line_offset = new_offset;
+        self.update_screenbuf();
+        Ok(())
+    }
+
+    fn update_screenbuf(&mut self) {
+        for (line_no, line) in self.full_buffer.iter().skip(self.line_offset).enumerate() {
+            self.screen_buffer[line_no] = *line;
+        }
+    }
 }
 
 type VirtualTTYBuffer<const R: usize, const C: usize> = [[TTYCharacter; C]; R];
@@ -104,13 +195,15 @@ type VirtualTTYBuffer<const R: usize, const C: usize> = [[TTYCharacter; C]; R];
 #[derive(Debug)]
 pub enum TTYDriverError {
     RowOutOfBounds { got: usize },
-    ColOutOfBounds { got: usize }
+    ColOutOfBounds { got: usize },
+    CannotScrollTooFar
 }
 impl Display for TTYDriverError {
     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
         match self {
             Self::RowOutOfBounds { got } => write!(f, "Character write out of bounds: row {} too high", got),
-            Self::ColOutOfBounds { got } => write!(f, "Character write out of bounds: col {} too high", got)
+            Self::ColOutOfBounds { got } => write!(f, "Character write out of bounds: col {} too high", got),
+            Self::CannotScrollTooFar => write!(f, "Cannot scroll outside line buffer")
         }
     }
 }
@@ -118,4 +211,11 @@ impl Error for TTYDriverError {}
 
 pub trait TTYDriver {
     fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError>;
+}
+
+impl<const C: usize> Write for TTY<C> {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        self.handle_mut().putstr(s);
+        Ok(())
+    }
 }
\ No newline at end of file