From eeb1c6c701e58a10b20ef13c4066f6bbc465ebf8 Mon Sep 17 00:00:00 2001 From: csd4ni3l Date: Fri, 13 Mar 2026 23:00:27 +0100 Subject: [PATCH] Add qemu auto-run to makefile, separate utils into a directory, add graphics support with rectangles, circles and triangles, double buffering (kind of) , and make example shapes on startup with an orange background. --- GNUmakefile | 1 + kernel/Cargo.lock | 7 ++ kernel/Cargo.toml | 7 ++ kernel/src/graphics.rs | 25 ----- kernel/src/main.rs | 22 +++-- kernel/src/utils/graphics.rs | 175 +++++++++++++++++++++++++++++++++++ kernel/src/utils/math.rs | 1 + kernel/src/utils/mod.rs | 2 + 8 files changed, 207 insertions(+), 33 deletions(-) delete mode 100644 kernel/src/graphics.rs create mode 100644 kernel/src/utils/graphics.rs create mode 100644 kernel/src/utils/math.rs create mode 100644 kernel/src/utils/mod.rs diff --git a/GNUmakefile b/GNUmakefile index 0cecac4..b50d4ab 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -213,6 +213,7 @@ ifeq ($(KARCH),loongarch64) iso_root -o $(IMAGE_NAME).iso endif rm -rf iso_root + qemu-system-$(KARCH) -cdrom $(IMAGE_NAME).iso -m 512M -serial stdio $(IMAGE_NAME).hdd: limine/limine kernel rm -f $(IMAGE_NAME).hdd diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 7743811..80cab30 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -22,4 +22,11 @@ name = "limine-rust-template" version = "0.1.0" dependencies = [ "limine", + "micromath", ] + +[[package]] +name = "micromath" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index c7cdbe7..eb76d45 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,3 +5,10 @@ edition = "2024" [dependencies] limine = "0.5" +micromath = "2.1.0" + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" \ No newline at end of file diff --git a/kernel/src/graphics.rs b/kernel/src/graphics.rs deleted file mode 100644 index 4b85a65..0000000 --- a/kernel/src/graphics.rs +++ /dev/null @@ -1,25 +0,0 @@ -use limine::framebuffer::Framebuffer; - -pub fn rgb(r: u8, g: u8, b: u8) -> u32 { - ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) -} - -pub fn create_rect(framebuffer: &Framebuffer, x: u64, y: u64, width: u64, height: u64, color: u32) { - for fb_x in x..x+width { - for fb_y in y..y+height { - // Calculate the pixel offset using the framebuffer information we obtained above. - // We skip `i` scanlines (pitch is provided in bytes) and add `i * 4` to skip `i` pixels forward. - let bytes_per_pixel = framebuffer.bpp() as u64 / 8; - let pixel_offset = fb_y * framebuffer.pitch() + fb_x * bytes_per_pixel; - - // Write 0xFFFFFFFF to the provided pixel offset to fill it white. - unsafe { - framebuffer - .addr() - .add(pixel_offset as usize) - .cast::() - .write(color) - }; - } - } -} \ No newline at end of file diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 8a1faca..e871a17 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -6,9 +6,9 @@ use core::arch::asm; use limine::BaseRevision; use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker}; -mod graphics; -use crate::graphics::*; +pub mod utils; +use crate::utils::graphics::{Framebuffer, circle_filled, circle_outline, rectangle_filled, rectangle_outline, rgb, triangle_outline}; /// Sets the base revision to the latest revision supported by the crate. /// See specification for further info. @@ -35,22 +35,28 @@ unsafe extern "C" fn kmain() -> ! { // All limine requests must also be referenced in a called function, otherwise they may be // removed by the linker. assert!(BASE_REVISION.is_supported()); - + if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() { - if let Some(framebuffer) = framebuffer_response.framebuffers().next() { - graphics::create_rect(&framebuffer, 0, 0, framebuffer.width(), framebuffer.height(), graphics::rgb(253, 129, 0)); + if let Some(limine_framebuffer) = framebuffer_response.framebuffers().next() { + let mut fb = Framebuffer::new(&limine_framebuffer); + rectangle_filled(&mut fb, 0, 0, limine_framebuffer.width() as usize, limine_framebuffer.height() as usize, rgb(253, 129, 0)); + rectangle_filled(&mut fb, 700, 400, 200, 200, rgb(0, 0, 0)); + rectangle_outline(&mut fb, 400, 400, 100, 100, rgb(0, 0, 0)); + circle_filled(&mut fb, 200, 200, 100.0, rgb(0, 0, 0)); + circle_outline(&mut fb, 400, 200, 100.0, rgb(0, 0, 0)); + triangle_outline(&mut fb, 100, 400, 200, 400, 150, 600, rgb(0, 0, 0)); } } - hcf(); + idle(); } #[panic_handler] fn rust_panic(_info: &core::panic::PanicInfo) -> ! { - hcf(); + idle(); } -fn hcf() -> ! { +fn idle() -> ! { loop { unsafe { #[cfg(target_arch = "x86_64")] diff --git a/kernel/src/utils/graphics.rs b/kernel/src/utils/graphics.rs new file mode 100644 index 0000000..2bdb4bb --- /dev/null +++ b/kernel/src/utils/graphics.rs @@ -0,0 +1,175 @@ +use limine::framebuffer::Framebuffer as LimineFramebuffer; +use crate::utils::math::PI; +use micromath::F32Ext; + +const MAX_BACKBUFFER_PIXELS: usize = 1920 * 1080; +static mut BACK_BUFFER: [u32; MAX_BACKBUFFER_PIXELS] = [0; MAX_BACKBUFFER_PIXELS]; + +pub fn rgb(r: u8, g: u8, b: u8) -> u32 { + ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) +} + +pub struct Framebuffer { + addr: *mut u32, + back_buffer: *mut u32, + width: usize, + height: usize, + pitch: usize, + back_buffer_len: usize, +} + +impl Framebuffer { + pub fn new(limine_fb: &LimineFramebuffer) -> Framebuffer { + let width = limine_fb.width() as usize; + let height = limine_fb.height() as usize; + let pitch = limine_fb.pitch() as usize / 4; + let needed = pitch.saturating_mul(height); + let back_buffer_len = core::cmp::min(needed, MAX_BACKBUFFER_PIXELS); + + Framebuffer { + addr: limine_fb.addr().cast::(), + back_buffer: core::ptr::addr_of_mut!(BACK_BUFFER).cast::(), + width, + height, + pitch, + back_buffer_len, + } + } + + pub fn put_pixel(&mut self, x: usize, y: usize, color: u32) { + if x >= self.width || y >= self.height { + return; + } + + let idx = y.saturating_mul(self.pitch).saturating_add(x); + if idx >= self.back_buffer_len { + return; + } + + unsafe { + *self.back_buffer.add(idx) = color; + } + } + + pub fn swap(&mut self) { + unsafe { + core::ptr::copy_nonoverlapping(self.back_buffer, self.addr, self.back_buffer_len); + } + } +} + +pub fn line(framebuffer: &mut Framebuffer, mut x0: i64, mut y0: i64, x1: i64, y1: i64, color: u32) { + let mut dx = x1 - x0; + let mut dy = y1 - y0; + + if dx < 0 { + dx = -dx; + } + + if dy < 0 { + dy = -dy; + } + + let step_x = if x0 < x1 {1} else {-1}; + let step_y = if y0 < y1 {1} else {-1}; + + let mut error = dx - dy; + let mut e2: i64; + + loop { + framebuffer.put_pixel(x0 as usize, y0 as usize, color); + + if x0 == x1 && y0 == y1 { + break; + } + + e2 = 2 * error; + + if e2 > -dy { + error -= dy; + x0 += step_x; + } + + if e2 < dx { + error += dx ; + y0 += step_y; + } + + } +} + +pub fn triangle_outline(framebuffer: &mut Framebuffer, x1: usize, y1: usize, x2: usize, y2: usize, x3: usize, y3: usize, color: u32) { + line(framebuffer, x1 as i64, y1 as i64, x2 as i64, y2 as i64, color); + line(framebuffer, x1 as i64, y1 as i64, x3 as i64, y3 as i64, color); + line(framebuffer, x2 as i64, y2 as i64, x3 as i64, y3 as i64, color); + framebuffer.swap(); +} + +pub fn circle_outline(framebuffer: &mut Framebuffer, x: usize, y: usize, radius: f32, color: u32) { + let mut i: f32 = 0.0; + + loop { + i += 0.1; + + let x1: f32 = radius * (i * PI / 180.0).cos(); + let y1: f32 = radius * (i * PI / 180.0).sin(); + framebuffer.put_pixel((x as f32 + x1) as usize, (y as f32 + y1) as usize, color); + + if i >= 360.0 { + break + } + } + framebuffer.swap(); +} + +pub fn circle_filled(framebuffer: &mut Framebuffer, x0: usize, y0: usize, radius: f32, color: u32) { + let mut x = radius as i64; + let mut y: i64 = 0; + let mut x_change: i64 = 1 - (radius as i64 * 2); + let mut y_change: i64 = 0; + let mut radius_error: i64 = 0; + + while x >= y { + let mut i = x0 as i64 - x; + while i <= x0 as i64 + x { + framebuffer.put_pixel(i as usize, (y0 as i64 + y) as usize, color); + framebuffer.put_pixel(i as usize, (y0 as i64 - y) as usize, color); + i += 1; + } + + let mut i = x0 as i64 - y; + while i <= x0 as i64 + y { + framebuffer.put_pixel(i as usize, (y0 as i64 + x) as usize, color); + framebuffer.put_pixel(i as usize, (y0 as i64 - x) as usize, color); + i += 1; + } + + y += 1; + radius_error += y_change; + y_change += 2; + + if (radius_error * 2) + x_change > 0 { + x -= 1; + radius_error += x_change; + x_change += 2; + } + } + framebuffer.swap(); +} + +pub fn rectangle_filled(framebuffer: &mut Framebuffer, x: usize, y: usize, width: usize, height: usize, color: u32) { + for fb_x in x..x+width { + for fb_y in y..y+height { + framebuffer.put_pixel(fb_x, fb_y, color); + } + } + framebuffer.swap(); +} + +pub fn rectangle_outline(framebuffer: &mut Framebuffer, x: i64, y: i64, width: i64, height: i64, color: u32) { + line(framebuffer, x, y, x + width, y, color); // bottomleft -> bottomright + line(framebuffer, x, y + height, x + width, y + height, color); // topleft -> topright + line(framebuffer, x, y, x, y + height, color); // bottomleft -> topleft + line(framebuffer, x + width, y, x + width, y + height, color); // bottomright -> topright + framebuffer.swap(); +} \ No newline at end of file diff --git a/kernel/src/utils/math.rs b/kernel/src/utils/math.rs new file mode 100644 index 0000000..dee9e06 --- /dev/null +++ b/kernel/src/utils/math.rs @@ -0,0 +1 @@ +pub const PI: f32 = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; // we are just a bit accurate (the library definitely needs this btw) \ No newline at end of file diff --git a/kernel/src/utils/mod.rs b/kernel/src/utils/mod.rs new file mode 100644 index 0000000..1c96211 --- /dev/null +++ b/kernel/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod math; +pub mod graphics; \ No newline at end of file