diff --git a/src/devices/graphical_debug/debug.rs b/src/devices/graphical_debug/debug.rs new file mode 100644 index 0000000..afa08dc --- /dev/null +++ b/src/devices/graphical_debug/debug.rs @@ -0,0 +1,63 @@ +use super::Display; + +pub struct DebugDisplay { + pub (crate) display: Display, + x: usize, + y: usize, + w: usize, + h: usize, +} + +impl DebugDisplay { + pub fn new(display: Display) -> DebugDisplay { + let w = display.width/8; + let h = display.height/16; + DebugDisplay { + display, + x: 0, + y: 0, + w: w, + h: h, + } + } + + pub fn write_char(&mut self, c: char) { + if self.x >= self.w || c == '\n' { + self.x = 0; + self.y += 1; + } + + if self.y >= self.h { + let new_y = self.h - 1; + let d_y = self.y - new_y; + + self.display.scroll(d_y * 16); + + unsafe { + self.display.sync(0, 0, self.display.width, self.display.height); + } + + self.y = new_y; + } + + if c != '\n' { + self.display.char( + self.x * 8, self.y * 16, + c, + 0xFFFFFF + ); + + unsafe { + self.display.sync(self.x * 8, self.y * 16, 8, 16); + } + + self.x += 1; + } + } + + pub fn write(&mut self, buf: &[u8]) { + for &b in buf { + self.write_char(b as char); + } + } +} diff --git a/src/devices/graphical_debug/display.rs b/src/devices/graphical_debug/display.rs new file mode 100644 index 0000000..ab4a93e --- /dev/null +++ b/src/devices/graphical_debug/display.rs @@ -0,0 +1,80 @@ +use alloc::boxed::Box; +use core::{cmp, ptr, slice}; + +use super::FONT; + +/// A display +pub struct Display { + pub width: usize, + pub height: usize, + pub onscreen: &'static mut [u32], + pub offscreen: Option> +} + +impl Display { + pub fn new(width: usize, height: usize, onscreen_ptr: *mut u32) -> Display { + let size = width * height; + let onscreen = unsafe { + ptr::write_bytes(onscreen_ptr, 0, size); + slice::from_raw_parts_mut(onscreen_ptr, size) + }; + Display { + width, + height, + onscreen, + offscreen: None, + } + } + + pub fn data_mut(&mut self) -> &mut [u32] { + match &mut self.offscreen { + Some(offscreen) => offscreen, + None => self.onscreen, + } + } + + /// Draw a character + pub fn char(&mut self, x: usize, y: usize, character: char, color: u32) { + if x + 8 <= self.width && y + 16 <= self.height { + let mut dst = self.data_mut().as_mut_ptr() as usize + (y * self.width + x) * 4; + + let font_i = 16 * (character as usize); + if font_i + 16 <= FONT.len() { + for row in 0..16 { + let row_data = FONT[font_i + row]; + for col in 0..8 { + if (row_data >> (7 - col)) & 1 == 1 { + unsafe { *((dst + col * 4) as *mut u32) = color; } + } + } + dst += self.width * 4; + } + } + } + } + + /// Scroll the screen + pub fn scroll(&mut self, lines: usize) { + let offset = cmp::min(self.height, lines) * self.width; + let size = (self.width * self.height) - offset; + unsafe { + let ptr = self.data_mut().as_mut_ptr(); + ptr::copy(ptr.add(offset), ptr, size); + ptr::write_bytes(ptr.add(size), 0, offset); + } + } + + /// Sync from offscreen to onscreen, unsafe because it trusts provided x, y, w, h + pub unsafe fn sync(&mut self, x: usize, y: usize, w: usize, mut h: usize) { + if let Some(offscreen) = &self.offscreen { + let mut offset = y * self.width + x; + while h > 0 { + self.onscreen[offset..offset+w].copy_from_slice( + &offscreen[offset..offset+w] + ); + offset += self.width; + h -= 1; + } + } + } +} diff --git a/src/devices/graphical_debug/mod.rs b/src/devices/graphical_debug/mod.rs new file mode 100644 index 0000000..0bf2dcb --- /dev/null +++ b/src/devices/graphical_debug/mod.rs @@ -0,0 +1,70 @@ +use core::str; +use spin::Mutex; + +pub use self::debug::DebugDisplay; +use self::display::Display; + +pub mod debug; +pub mod display; + +pub static FONT: &'static [u8] = include_bytes!("../../../res/unifont.font"); + +pub static DEBUG_DISPLAY: Mutex> = Mutex::new(None); + +pub fn init(env: &[u8]) { + println!("Starting graphical debug"); + + let mut width = 0; + let mut height = 0; + let mut physbaseptr = 0; + + //TODO: should errors be reported? + for line in str::from_utf8(env).unwrap_or("").lines() { + let mut parts = line.splitn(2, '='); + let name = parts.next().unwrap_or(""); + let value = parts.next().unwrap_or(""); + + if name == "FRAMEBUFFER_ADDR" { + physbaseptr = usize::from_str_radix(value, 16).unwrap_or(0); + } + + if name == "FRAMEBUFFER_WIDTH" { + width = usize::from_str_radix(value, 16).unwrap_or(0); + } + + if name == "FRAMEBUFFER_HEIGHT" { + height = usize::from_str_radix(value, 16).unwrap_or(0); + } + } + + if physbaseptr == 0 || width == 0 || height == 0 { + println!("Framebuffer not found"); + return; + } + + println!("Framebuffer {}x{} at {:X}", width, height, physbaseptr); + + { + let size = width * height * 4; + + let virtbaseptr = physbaseptr + crate::PHYS_OFFSET; + + let display = Display::new(width, height, virtbaseptr as *mut u32); + let debug_display = DebugDisplay::new(display); + *DEBUG_DISPLAY.lock() = Some(debug_display); + } +} + +pub fn init_heap() { + if let Some(debug_display) = &mut *DEBUG_DISPLAY.lock() { + debug_display.display.offscreen = Some( + debug_display.display.onscreen.to_vec().into_boxed_slice() + ); + } +} + +pub fn fini() { + DEBUG_DISPLAY.lock().take(); + + println!("Finished graphical debug"); +}