diff --git a/.vscode/settings.json b/.vscode/settings.json index b0afe69..ce3e5be 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,6 @@ "editor.formatOnSave": true }, "rust-analyzer.check.command": "clippy", + "rust-analyzer.procMacro.attributes.enable": true, + "rust-analyzer.cargo.buildScripts.enable": true } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c99c228..4b9e29f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,9 +22,9 @@ checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "cc" -version = "1.2.15" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "shlex", ] @@ -75,6 +75,7 @@ name = "foundry_os" version = "0.1.0" dependencies = [ "cc", + "libm", "limine", "pc-keyboard", "pic8259", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 0d2999e..2663776 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -11,6 +11,7 @@ x86_64 = "0.15.2" spin = "0.9.8" pic8259 = "0.11.0" pc-keyboard = "0.8.0" +libm = { version = "0.1.0", path = "../libm" } [build-dependencies] cc = "1.2.14" diff --git a/resources/font/cp850-8x16.psf b/kernel/resources/font/cp850-8x16.psf similarity index 100% rename from resources/font/cp850-8x16.psf rename to kernel/resources/font/cp850-8x16.psf diff --git a/resources/font/spleen-8x16.psf b/kernel/resources/font/spleen-8x16.psf similarity index 100% rename from resources/font/spleen-8x16.psf rename to kernel/resources/font/spleen-8x16.psf diff --git a/kernel/src/arch/x86_64/apic/mod.rs b/kernel/src/arch/x86_64/apic/mod.rs index 3df580a..2679f19 100644 --- a/kernel/src/arch/x86_64/apic/mod.rs +++ b/kernel/src/arch/x86_64/apic/mod.rs @@ -1,7 +1,8 @@ #![expect(unused)] use core::arch::x86_64::__cpuid; -// use libk::drivers::memory::{FRAME_ALLOCATOR, FoundryOSFrameAllocator, OFFSET_PAGE_TABLE}; +// use libk::drivers::memory::{FRAME_ALLOCATOR, FoundryOSFrameAllocator, +// OFFSET_PAGE_TABLE}; use x86_64::{ PhysAddr, VirtAddr, structures::paging::{Page, PageTableFlags, PhysFrame, Size4KiB}, @@ -9,7 +10,7 @@ use x86_64::{ // use crate::serial_print; -use super::{cpu::model_specific_registers::*, memmap::PHYSICAL_MEMORY_OFFSET}; +use super::{cpu::msr::*, memmap::PHYSICAL_MEMORY_OFFSET}; const IA32_APIC_BASE_MSR: u32 = 0x1b; const IA32_APIC_BASE_MSR_BSP: u64 = 0x100; diff --git a/kernel/src/arch/x86_64/cpu.rs b/kernel/src/arch/x86_64/cpu.rs deleted file mode 100644 index b47a96d..0000000 --- a/kernel/src/arch/x86_64/cpu.rs +++ /dev/null @@ -1,27 +0,0 @@ -#[expect(unused)] -pub mod model_specific_registers { - use core::arch::x86_64::__cpuid; - use spin::Lazy; - use x86_64::registers::model_specific::Msr; - - const CPUID_FLAG_MSR: u32 = 1 << 5; - static EDX: Lazy = Lazy::new(|| unsafe { __cpuid(1).edx }); - - pub fn cpu_has_msr() -> bool { - *EDX & CPUID_FLAG_MSR != 0 - } - - pub fn cpu_get_msr(msr: u32, value: &mut u64) { - let msr = Msr::new(msr); - unsafe { - *value = msr.read(); - } - } - - pub fn cpu_set_msr(msr: u32, value: u64) { - let mut msr = Msr::new(msr); - unsafe { - msr.write(value); - } - } -} diff --git a/kernel/src/arch/x86_64/cpu/mod.rs b/kernel/src/arch/x86_64/cpu/mod.rs new file mode 100644 index 0000000..8284b79 --- /dev/null +++ b/kernel/src/arch/x86_64/cpu/mod.rs @@ -0,0 +1,2 @@ +pub mod msr; +pub mod port; diff --git a/kernel/src/arch/x86_64/cpu/msr.rs b/kernel/src/arch/x86_64/cpu/msr.rs new file mode 100644 index 0000000..0463b70 --- /dev/null +++ b/kernel/src/arch/x86_64/cpu/msr.rs @@ -0,0 +1,25 @@ +#![expect(unused)] +use core::arch::x86_64::__cpuid; +use spin::Lazy; +use x86_64::registers::model_specific::Msr; + +const CPUID_FLAG_MSR: u32 = 1 << 5; +static EDX: Lazy = Lazy::new(|| unsafe { __cpuid(1).edx }); + +pub fn cpu_has_msr() -> bool { + *EDX & CPUID_FLAG_MSR != 0 +} + +pub fn cpu_get_msr(msr: u32, value: &mut u64) { + let msr = Msr::new(msr); + unsafe { + *value = msr.read(); + } +} + +pub fn cpu_set_msr(msr: u32, value: u64) { + let mut msr = Msr::new(msr); + unsafe { + msr.write(value); + } +} diff --git a/kernel/src/io/port.rs b/kernel/src/arch/x86_64/cpu/port.rs similarity index 53% rename from kernel/src/io/port.rs rename to kernel/src/arch/x86_64/cpu/port.rs index 77eafa0..6abf31f 100644 --- a/kernel/src/io/port.rs +++ b/kernel/src/arch/x86_64/cpu/port.rs @@ -1,9 +1,14 @@ //! Functions for IO using ports. - +#![expect(unused)] use core::arch::asm; -#[inline] -pub fn inb(port: u16) -> u8 { +/// Take a byte in from a port. +/// +/// # Safety +/// +/// This might have side effects so it is marked unsafe just in case. +#[inline(always)] +pub unsafe fn inb(port: u16) -> u8 { let value: u8; unsafe { asm!( @@ -16,8 +21,13 @@ pub fn inb(port: u16) -> u8 { value } -#[inline] -pub fn outb(port: u16, value: u8) { +/// Take a byte in from a port. +/// +/// # Safety +/// +/// This might have side effects so it is marked unsafe just in case. +#[inline(always)] +pub unsafe fn outb(port: u16, value: u8) { unsafe { asm!( "out dx, al", diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 2cfb85b..f8be559 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -66,7 +66,9 @@ pub fn disable_pic() { } } -const extern "x86-interrupt" fn breakpoint_handler(_stack_frame: InterruptStackFrame) { +const extern "x86-interrupt" fn breakpoint_handler( + _stack_frame: InterruptStackFrame, +) { // serial_println!("Exception: Breakpoint\n{:#?}", stack_frame); // println_log!("Exception: Breakpoint\n{:#?}", stack_frame); } @@ -87,19 +89,22 @@ extern "x86-interrupt" fn double_fault_handler( panic!("Exception: Double Fault\n{:#?}", stack_frame); } -extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { +extern "x86-interrupt" fn keyboard_interrupt_handler( + _stack_frame: InterruptStackFrame, +) { use pc_keyboard::{HandleControl, Keyboard, ScancodeSet1, layouts}; // use pc_keyboard::DecodedKey; use spin::Mutex; // use x86_64::instructions::port::Port; - static KEYBOARD: Lazy>> = Lazy::new(|| { - Mutex::new(Keyboard::new( - ScancodeSet1::new(), - layouts::Uk105Key, - HandleControl::Ignore, - )) - }); + static KEYBOARD: Lazy>> = + Lazy::new(|| { + Mutex::new(Keyboard::new( + ScancodeSet1::new(), + layouts::Uk105Key, + HandleControl::Ignore, + )) + }); let _keyboard = KEYBOARD.lock(); // let mut port = Port::new(0x60); @@ -113,7 +118,9 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStac } } -extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { +extern "x86-interrupt" fn timer_interrupt_handler( + _stack_frame: InterruptStackFrame, +) { unsafe { PICS.lock() .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); diff --git a/kernel/src/io/ascii/mod.rs b/kernel/src/graphics/ascii.rs similarity index 100% rename from kernel/src/io/ascii/mod.rs rename to kernel/src/graphics/ascii.rs diff --git a/kernel/src/io/framebuffer/colour.rs b/kernel/src/graphics/colour.rs similarity index 68% rename from kernel/src/io/framebuffer/colour.rs rename to kernel/src/graphics/colour.rs index 2d6c0b6..43017e1 100644 --- a/kernel/src/io/framebuffer/colour.rs +++ b/kernel/src/graphics/colour.rs @@ -1,8 +1,11 @@ #[repr(u32)] #[derive(Copy, Clone, PartialEq, Eq, Debug)] +// Some predefined colours as well as custom colours in the proper format. This +// assumes that the framebuffer uses 32bpp, something which we will assert on +// initialisation. pub enum Colour { - ARGB(u8, u8, u8, u8), - RGB(u8, u8, u8), + Argb(u8, u8, u8, u8), + Rgb(u8, u8, u8), HexARGB(u32), Black = 0x000000FF, Blue = 0x0000FFFF, @@ -14,14 +17,19 @@ pub enum Colour { White = 0xFFFFFFFF, } -#[allow(clippy::use_self)] +#[expect( + clippy::use_self, + reason = "Self is uglier and more verbose than `u32`" +)] impl From for u32 { fn from(val: Colour) -> Self { match val { - Colour::ARGB(a, r, g, b) => { + Colour::Argb(a, r, g, b) => { (a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32) } - Colour::RGB(r, g, b) => ((r as u32) << 16) | (g as u32) << 8 | (b as u32), + Colour::Rgb(r, g, b) => { + ((r as u32) << 16) | (g as u32) << 8 | (b as u32) + } Colour::HexARGB(hex) => hex, Colour::Black => 0xFF000000, Colour::Blue => 0xFF0000FF, @@ -38,8 +46,10 @@ impl From for u32 { impl core::fmt::Display for Colour { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a), - Self::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b), + Self::Argb(r, g, b, a) => { + write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a) + } + Self::Rgb(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b), Self::HexARGB(hex) => write!(f, "Hex(#{:x})", hex), Self::Black => write!(f, "Black"), Self::Blue => write!(f, "Blue"), diff --git a/kernel/src/graphics/font.rs b/kernel/src/graphics/font.rs new file mode 100644 index 0000000..1cdf450 --- /dev/null +++ b/kernel/src/graphics/font.rs @@ -0,0 +1,46 @@ +#![allow(unused)] +use libm::include_font; + +pub const FONT_SPLEEN_8X16: Font = + Font::new(include_font!("../../resources/font/spleen-8x16.psf")); + +pub const FONT_CP850_8X16: Font = + Font::new(include_font!("../../resources/font/cp850-8x16.psf")); + +pub struct Font { + width: usize, + height: usize, + length: u16, + data: [[u8; 16]; 512], +} + +impl Font { + pub const fn new(data: [[u8; 16]; 512]) -> Self { + Self { + width: 8, + height: 16, + length: data.len() as u16, + data, + } + } + + pub const fn glyph_for(&self, c: u16) -> &[u8] { + if c > self.length { + return &self.data[0]; + } + + &self.data[c as usize] + } + + pub const fn width(&self) -> usize { + self.width + } + + pub const fn height(&self) -> usize { + self.height + } + + pub const fn default() -> &'static Self { + &FONT_SPLEEN_8X16 + } +} diff --git a/kernel/src/graphics/framebuffer.rs b/kernel/src/graphics/framebuffer.rs index 7ca9c6d..aaaf9c3 100644 --- a/kernel/src/graphics/framebuffer.rs +++ b/kernel/src/graphics/framebuffer.rs @@ -1 +1,158 @@ -//! Handles \ No newline at end of file +//! Handles setting up the framebuffer passed to us by Limine. +#![expect(unused)] +use crate::DEFAULT_FONT; + +use super::{colour::Colour, font::Font}; + +use limine::framebuffer::Framebuffer; +use limine::request::FramebufferRequest; +use spin::{Lazy, Mutex}; + +#[used] +#[unsafe(link_section = ".requests")] +/// The Limine framebuffer request. +static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); + +/// A mutex used for writing to the framebuffer, in most cases we can abstract +/// this behind calls to println! etc. +pub static FRAMEBUFFER_WRITER: Lazy>> = Lazy::new( + || { + Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else( + || { + unreachable!("Framebuffer request failed, got None."); + }, + |framebuffer_response| { + let framebuffer = + framebuffer_response.framebuffers().next() + .expect("Expected to find at least one framebuffer in response, got zero."); + Some(FramebufferWriter::new(framebuffer, Colour::Black)) + }, + )) + }, +); + +/// The updated writer stores necessary fields from the [Framebuffer]. +/// This ensures that the contained types are Send, as Framebuffer was +/// not marked as Send. +/// +/// It also avoids the requirement for lifetimes. +/// +/// Note this does not implement Writer as these functions only handle drawing +/// pixels. +pub struct FramebufferWriter { + pitch: u64, + /// Number of bits used per pixel, we expect 32 so this will be asserted + /// when we initialise the framebuffer. + bpp: u16, + /// A raw pointer to the framebuffer. + addr: *mut u8, + /// The width of the screen in pixels. + width: u64, + /// The height of the screen in pixels. + height: u64, + /// The default background colour of the Framebuffer. + background_colour: Colour, +} + +/// Marker trait required for sending the writer across threads, which will +/// probably come in handy later. +unsafe impl Send for FramebufferWriter {} +/// Marker trait required for sharing the writer across threads, which will +/// probably come in handy later. +/// +/// # Note +/// +/// We wrap this in a Mutex later so this should handle synchronising accesses. +unsafe impl Sync for FramebufferWriter {} + +impl FramebufferWriter { + /// Creates a new [`FramebufferWriter`] with the specified background + /// colour. + /// + /// # Panics + /// + /// Panics if the bits per pixel for the framebuffer is not 32 bits per + /// pixel, because we didn't exactly handle other cases. + pub fn new(framebuffer: Framebuffer, background_colour: Colour) -> Self { + // We don't handle other cases, this should not trigger on most newer + // hardware. + assert!(framebuffer.bpp() == 32); + + Self { + pitch: framebuffer.pitch(), + bpp: framebuffer.bpp(), + addr: framebuffer.addr(), + width: framebuffer.width(), + height: framebuffer.height(), + background_colour, + } + } + + /// Writes a pixel with `colour` to the screen at the given `(x, y)` + /// coordinates (in pixels). + pub fn write_pixel(&self, x: usize, y: usize, colour: Colour) { + let pitch = self.pitch as usize; + let bpp = (self.bpp / 8) as usize; + let pixel_offset = y * pitch + x * bpp; + + // Safety: We assume the pointer to the framebuffer is valid, which it likely + // is because we copy it from the Limine request. + unsafe { + *(self.addr.add(pixel_offset) as *mut u32) = colour.into(); + } + } + + /// Writes a 2D slice `buffer` to the screen. This should be less than the + /// size of the screen in dimensions, but does not neccessarily fill the + /// whole screen. + pub fn render_frame(&self, buffer: &[&[Colour]]) { + // TODO: this should return errors. + for (y, &row) in buffer.iter().enumerate() { + if y >= self.height() as usize { + break; + } + for (x, pixel) in row.iter().enumerate() { + if x >= self.width() as usize { + break; + } + self.write_pixel(x, y, *pixel); + } + } + } + + /// Returns the width of the framebuffer in pixels. + pub const fn width(&self) -> u32 { + self.width as u32 + } + + /// Returns the height of the framebuffer in pixels. + pub const fn height(&self) -> u32 { + self.height as u32 + } + + /// Sets the new default background colour. + pub const fn set_default_background_colour(&mut self, bg: Colour) { + self.background_colour = bg; + } + + /// Clears the screen and sets the background to the default colour. + pub fn clear(&self) { + let width = self.width as usize; + let height = self.height as usize; + + for y in 0..height { + for x in 0..width { + // Write the background in the preferred background colour. + self.write_pixel(x, y, self.background_colour); + } + } + } +} + +/// Returns the screen size in pixels or (0, 0) if something went wrong. +pub fn screensize_px() -> (u32, u32) { + FRAMEBUFFER_WRITER + .lock() + .as_mut() + .map_or_else(|| (0, 0), |writer| (writer.width(), writer.height())) +} diff --git a/kernel/src/graphics/mod.rs b/kernel/src/graphics/mod.rs index 0317445..8265f60 100644 --- a/kernel/src/graphics/mod.rs +++ b/kernel/src/graphics/mod.rs @@ -1 +1,5 @@ +pub mod ascii; +pub mod colour; +pub mod font; pub mod framebuffer; +pub mod writer; diff --git a/kernel/src/graphics/writer.rs b/kernel/src/graphics/writer.rs new file mode 100644 index 0000000..8005e8b --- /dev/null +++ b/kernel/src/graphics/writer.rs @@ -0,0 +1,243 @@ +#![expect(dead_code)] +use core::fmt; +use spin::{Lazy, Mutex}; +use x86_64::instructions::interrupts; + +use super::{colour::Colour, font::Font, framebuffer::FRAMEBUFFER_WRITER}; + +static FONT_WIDTH: u32 = 8; +static FONT_HEIGHT: u32 = 16; + +pub static WRITER: Lazy> = Lazy::new(|| Mutex::new(Writer::new())); + +pub fn screensize_chars() -> (u32, u32) { + let writer = WRITER.lock(); + (writer.screen_width, writer.screen_height) +} + +pub struct Writer { + font: &'static Font, + /// Measured in chars not pixels. + screen_width: u32, + /// Measured in chars not pixels. + screen_height: u32, + /// 16 pixels tall. + text_line: u32, + /// 8 pixels wide. + text_col: u32, + + fg_color: Colour, + bg_color: Colour, +} + +impl Default for Writer { + fn default() -> Self { + Self::new() + } +} + +impl Writer { + pub fn new() -> Self { + FRAMEBUFFER_WRITER.lock().as_mut().map_or_else( + || { + panic!("Framebuffer writer not initialized."); + }, + |writer| Self { + font: &crate::DEFAULT_FONT, + screen_width: writer.width() / 8, + screen_height: writer.height() / 16, + text_line: 0, + text_col: 0, + fg_color: Colour::White, + bg_color: Colour::Black, + }, + ) + } + + pub const fn set_font(&mut self, font: &'static Font) { + self.font = font; + } + + /// This is sent when the user types a backspace. + const BACKSPACE: u8 = 8; + + pub fn write_glyph(&mut self, c: u8) { + if c == b'\n' { + self.newline(); + return; + } else if c == Self::BACKSPACE { + self.backspace(); + return; + } + + // Get the character data from the font array. -- each byte is a row of + // pixels + let data: &[u8] = self.font.glyph_for(c as u16); + + if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { + for (row, line) in data.iter().enumerate().take(16) { + for col in 0..8 { + let pixel_x: u32 = self.text_col * FONT_WIDTH + col; + let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32; + + if line & (0x80 >> col) != 0 { + // Write the foreground color + writer.write_pixel( + pixel_x as usize, + pixel_y as usize, + self.fg_color, + ); + } else { + // Write the background color + writer.write_pixel( + pixel_x as usize, + pixel_y as usize, + self.bg_color, + ); + } + } + } + } + + // Go to next position + if self.text_col + 1 >= self.screen_width { + self.newline(); + } else { + self.text_col += 1; + } + } + + pub const fn dimensions(&self) -> (u32, u32) { + (self.screen_width, self.screen_height) + } + + pub const fn next_char(&mut self) { + self.text_col += 1; + } + + pub const fn newline(&mut self) { + self.text_col = 0; + + if self.text_line + 1 >= self.screen_height { + self.text_line = 0; + } else { + self.text_line += 1; + } + } + + /// Handles the backspace character. TODO: Implement VT-100 style terminal + /// control codes alongside a shell. Not simple. + pub fn backspace(&mut self) { + if self.text_col > 0 { + self.text_col -= 1; + // Blank out the previous char. + self.write_glyph(b' '); + self.text_col -= 1; + } + } + + pub fn write_string(&mut self, s: &str) { + for c in s.chars() { + self.write_glyph(c as u8); + } + } + + pub const fn set_colour(&mut self, fg: Colour, bg: Colour) { + self.fg_color = fg; + self.bg_color = bg; + } + + pub const fn reset_colour(&mut self) { + self.fg_color = Colour::White; + self.bg_color = Colour::Black; + } +} + +impl core::fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.write_string(s); + Ok(()) + } +} + +fn write(args: fmt::Arguments, fg: Colour, bg: Colour) { + use core::fmt::Write; + + interrupts::without_interrupts(|| { + let mut writer = WRITER.lock(); + writer.set_colour(fg, bg); + writer.write_fmt(args).unwrap(); + writer.reset_colour(); + }); +} + +pub fn _print(args: fmt::Arguments) { + x86_64::instructions::interrupts::without_interrupts(|| { + write(args, Colour::White, Colour::Black); + }) +} + +pub fn _print_err(args: fmt::Arguments) { + x86_64::instructions::interrupts::without_interrupts(|| { + write(args, Colour::Red, Colour::Black); + }) +} + +pub fn _print_log(args: fmt::Arguments) { + x86_64::instructions::interrupts::without_interrupts(|| { + write(args, Colour::Yellow, Colour::Black); + }) +} + +pub fn clear_screen() { + interrupts::without_interrupts(|| { + let mut writer = WRITER.lock(); + writer.text_line = 0; + writer.text_col = 0; + + if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { + writer.clear(); + } + }); +} + +pub fn reset_cursor() { + interrupts::without_interrupts(|| { + let mut writer = WRITER.lock(); + writer.text_line = 0; + writer.text_col = 0; + }); +} + +#[macro_export] +macro_rules! println_log { + () => ($crate::print_log!("\n")); + ($($arg:tt)*) => ($crate::print_log!("{}\n", format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! print_log { + ($($arg:tt)*) => ($crate::_print_log(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::_print(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! printlnerr { + () => ($crate::printerr!("\n")); + ($($arg:tt)*) => ($crate::printerr!("{}\n", format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! printerr { + ($($arg:tt)*) => ($crate::_print_err(format_args!($($arg)*))); +} diff --git a/kernel/src/io/framebuffer/display.rs b/kernel/src/io/framebuffer/display.rs deleted file mode 100644 index 576b77b..0000000 --- a/kernel/src/io/framebuffer/display.rs +++ /dev/null @@ -1,104 +0,0 @@ -use limine::request::FramebufferRequest; - -#[used] -#[unsafe(link_section = ".requests")] -static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); - -use super::colour::Colour; -use core::panic; - -use limine::framebuffer::Framebuffer; -use spin::{Lazy, Mutex}; - -pub static FRAMEBUFFER_WRITER: Lazy>> = Lazy::new(|| { - Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else( - || { - panic!("Framebuffer request failed"); - }, - |framebuffer_response| { - let framebuffer = framebuffer_response.framebuffers().next().unwrap(); - Some(FramebufferWriter::new(framebuffer)) - }, - )) -}); - -/// The updated writer stores necessary fields from the [Framebuffer]. -/// This ensures that the contained types are Send, as Framebuffer was -/// not marked as Send. -/// -/// It also avoids the requirement for lifetimes. -/// -/// Note this does not implement Writer as these functions only handle drawing pixels. -pub struct FramebufferWriter { - pitch: u64, - bpp: u16, - addr: *mut u8, - width: u64, - height: u64, -} - -unsafe impl Send for FramebufferWriter {} -unsafe impl Sync for FramebufferWriter {} - -impl FramebufferWriter { - pub fn new(framebuffer: Framebuffer) -> Self { - Self { - pitch: framebuffer.pitch(), - bpp: framebuffer.bpp(), - addr: framebuffer.addr(), - width: framebuffer.width(), - height: framebuffer.height(), - } - } - - pub fn write_pixel(&self, x: usize, y: usize, color: Colour) { - let pitch = self.pitch as usize; - let bpp = (self.bpp / 8) as usize; - let pixel_offset = y * pitch + x * bpp; - - unsafe { - *(self.addr.add(pixel_offset) as *mut u32) = color.into(); - } - } - - pub fn render_frame(&self, buffer: &[&[Colour]]) { - // TODO: this should return errors - for (y, &row) in buffer.iter().enumerate() { - if y >= self.height() as usize { - break; - } - for (x, pixel) in row.iter().enumerate() { - if x >= self.width() as usize { - break; - } - self.write_pixel(x, y, *pixel); - } - } - } - - pub const fn width(&self) -> u32 { - self.width as u32 - } - - pub const fn height(&self) -> u32 { - self.height as u32 - } - - pub fn clear(&self) { - let width = self.width as usize; - let height = self.height as usize; - - for y in 0..height { - for x in 0..width { - self.write_pixel(x, y, Colour::Black); - } - } - } -} - -pub fn screensize_px() -> (u32, u32) { - FRAMEBUFFER_WRITER - .lock() - .as_mut() - .map_or_else(|| (0, 0), |writer| (writer.width(), writer.height())) -} diff --git a/kernel/src/io/framebuffer/mod.rs b/kernel/src/io/framebuffer/mod.rs deleted file mode 100644 index cc5957a..0000000 --- a/kernel/src/io/framebuffer/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod colour; -pub mod display; diff --git a/kernel/src/io/mod.rs b/kernel/src/io/mod.rs index 0787808..75fe8fc 100644 --- a/kernel/src/io/mod.rs +++ b/kernel/src/io/mod.rs @@ -1,7 +1,4 @@ -pub mod ascii; -pub mod framebuffer; pub mod keyboard; -pub mod port; pub mod serial; // Re-exported macro definitions. diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 51b5388..b3c3b50 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -12,6 +12,7 @@ )] use core::arch::asm; +use graphics::font::{FONT_SPLEEN_8X16, Font}; use limine::BaseRevision; mod arch; @@ -20,12 +21,16 @@ mod io; /// Sets the base revision to the latest revision supported by the crate. /// See specification for further info. -/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler. +/// Be sure to mark all limine requests with #[used], otherwise they may be +/// removed by the compiler. #[used] // The .requests section allows limine to find the requests faster and more safely. #[unsafe(link_section = ".requests")] static BASE_REVISION: BaseRevision = BaseRevision::new(); +/// The default font used when setting up the framebuffer code. +pub const DEFAULT_FONT: Font = FONT_SPLEEN_8X16; + #[panic_handler] fn rust_panic(_info: &core::panic::PanicInfo) -> ! { hcf(); diff --git a/libm/src/lib.rs b/libm/src/lib.rs index 376fb0b..0c9103b 100644 --- a/libm/src/lib.rs +++ b/libm/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(proc_macro_span)] #![warn( clippy::correctness, clippy::nursery, @@ -12,18 +13,32 @@ use std::fs::File; use std::io::{Read, Seek, SeekFrom}; -use proc_macro::TokenStream; +use proc_macro::{Span, TokenStream}; use quote::quote; +use std::path::PathBuf; use syn::{LitStr, parse_macro_input}; - extern crate proc_macro; #[proc_macro] +/// Expects the file path to be relative to the current file so this works +/// similarly to the standard Rust include! macros. pub fn include_font(item: TokenStream) -> TokenStream { - let filename = parse_macro_input!(item as LitStr); - let file_path = filename.value(); + let span = Span::call_site(); + let source_file = span.source_file(); - println!("Loading font: [{}]", file_path); + if !source_file.is_real() { + panic!( + "We can't handle finding files if the source file does not exist. TODO: Can we?" + ) + } + + let filename = parse_macro_input!(item as LitStr); + let source_filepath: PathBuf = source_file.path(); + let file_path = format!( + "{}/{}", + source_filepath.parent().unwrap_or_else(|| panic!("Expected to find the calling source file in a folder like src! Got: {}", source_filepath.display())).display(), + filename.value() + ); let font_bytes = match load_file(file_path) { Ok(bytes) => bytes, diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..f0e43f1 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,5 @@ +max_width = 85 +format_code_in_doc_comments = true +comment_width = 80 +wrap_comments = true +inline_attribute_width = 80