diff --git a/.idea/customTargets.xml b/.idea/customTargets.xml new file mode 100644 index 0000000..7e79cb4 --- /dev/null +++ b/.idea/customTargets.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..283b9b4 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/.idea/tools/External Tools.xml b/.idea/tools/External Tools.xml new file mode 100644 index 0000000..7d5fa72 --- /dev/null +++ b/.idea/tools/External Tools.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/.idea/windows.iml b/.idea/windows.iml index 00f776b..b0cef5f 100644 --- a/.idea/windows.iml +++ b/.idea/windows.iml @@ -4,6 +4,9 @@ + + + diff --git a/Cargo.toml b/Cargo.toml index 8fd850e..2f26c95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,8 @@ members = [ ] [dependencies] -bootloader = "0.10.12" \ No newline at end of file +bootloader = "0.10.12" +spin = "0.9.1" +[dependencies.lazy_static] +version = "1.4.0" +features = ["spin_no_std"] \ No newline at end of file diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs new file mode 100644 index 0000000..1083d78 --- /dev/null +++ b/src/allocator/mod.rs @@ -0,0 +1,55 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::UnsafeCell; +use core::ptr::null_mut; +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering::SeqCst; + +const ARENA_SIZE: usize = 128 * 1024; +const MAX_SUPPORTED_ALIGN: usize = 4096; +#[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN +struct SimpleAllocator { + arena: UnsafeCell<[u8; ARENA_SIZE]>, + remaining: AtomicUsize, // we allocate from the top, counting down +} + +#[global_allocator] +static ALLOCATOR: SimpleAllocator = SimpleAllocator { + arena: UnsafeCell::new([0x55; ARENA_SIZE]), + remaining: AtomicUsize::new(ARENA_SIZE), +}; + +unsafe impl Sync for SimpleAllocator {} + +unsafe impl GlobalAlloc for SimpleAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + + // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2. + // So we can safely use a mask to ensure alignment without worrying about UB. + let align_mask_to_round_down = !(align - 1); + + if align > MAX_SUPPORTED_ALIGN { + return null_mut(); + } + + let mut allocated = 0; + if self + .remaining + .fetch_update(SeqCst, SeqCst, |mut remaining| { + if size > remaining { + return None; + } + remaining -= size; + remaining &= align_mask_to_round_down; + allocated = remaining; + Some(remaining) + }) + .is_err() + { + return null_mut(); + }; + (self.arena.get() as *mut u8).add(allocated) + } + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} \ No newline at end of file diff --git a/src/internals/errors.rs b/src/internals/errors.rs new file mode 100644 index 0000000..1668d70 --- /dev/null +++ b/src/internals/errors.rs @@ -0,0 +1,160 @@ + +use core::borrow::{BorrowMut}; + + +use crate::{InterruptStackFrame, font, FACEBOOK}; +use crate::internals::WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood::{COMMUNIST_RED, CUM_WHITE, Colour}; + +fn draw_box(x: usize, y: usize, width: usize, height: usize, color: Colour, mut fb: &mut [u8]) { + let pixelwidth = FACEBOOK.fb_pixelwidth.lock(); + let pitch = FACEBOOK.fb_pitch.lock(); + let colourtype = FACEBOOK.fb_colourtype.lock(); + + for i in 0..width { + for j in 0..height { + let pixel = (y * &*pitch) + (x * &*pixelwidth) + (i * &*pixelwidth) + (j * &*pitch); + if *colourtype == 0 { //BGR + unsafe { + fb[pixel + 0] = color.b; + fb[pixel + 1] = color.g; + fb[pixel + 2] = color.r; + } + } else if *colourtype == 1 { //RGB + unsafe { + fb[pixel + 0] = color.r; + fb[pixel + 1] = color.g; + fb[pixel + 2] = color.b; + } + } else { + // average values + let avg = (color.r as u16 + color.g as u16 + color.b as u16) / 3; + unsafe {fb[pixel + 0] = avg as u8;} + } + } + } + + drop(pixelwidth); + drop(pitch); + drop(colourtype); +} + +fn put_pixel(x: usize, y: usize, color: Colour, mut fb: &mut [u8]) { + let pixelwidth = FACEBOOK.fb_pixelwidth.lock(); + let pitch = FACEBOOK.fb_pitch.lock(); + let colourtype = FACEBOOK.fb_colourtype.lock(); + + let pixel = (y * &*pitch) + (x * &*pixelwidth); + + if *colourtype == 0 { //BGR + unsafe { + fb[pixel + 0] = color.b; + fb[pixel + 1] = color.g; + fb[pixel + 2] = color.r; + } + } else if *colourtype == 1 { //RGB + unsafe { + fb[pixel + 0] = color.r; + fb[pixel + 1] = color.g; + fb[pixel + 2] = color.b; + } + } else { + // average values + let avg = (color.r as u16 + color.g as u16 + color.b as u16) / 3; + unsafe {fb[pixel + 0] = avg as u8;} + } + + drop(pixelwidth); + drop(pitch); + drop(colourtype); +} + + +fn draw_char(x: usize, y: usize, c: char, color: Colour, mut fb: &mut [u8]) { + let font = font::BASIC_LEGACY; + // font is 8x8, stored in a 2d array of bytes + let char_width = 8; + let char_height = 8; + + let char_index = c as usize; + let char_data = font[char_index]; + + for row in 0..char_height { + for col in 0..char_width { + let bit = (char_data[row] >> col) & 1; + if bit >= 1 { + put_pixel(x + col, y + row, color, fb); + } + } + } +} + +fn draw_horizcentre_string(y: usize, s: &str, color: Colour, fb: &mut [u8]) { + let fb_width = FACEBOOK.fb_width.lock(); + + let mut x_tmp = (*fb_width - s.len() * 8) / 2; + let mut y_tmp = y; + + for c in s.chars() { + if c == '\n' { + x_tmp = (*fb_width - s.len() * 8) / 2; + y_tmp += 8; + } else { + draw_char(x_tmp, y_tmp, c, color, fb.borrow_mut()); + x_tmp += 8; + } + } + + drop(fb_width); +} + +pub extern "x86-interrupt" fn generic_error(stack_frame: InterruptStackFrame) { + // cover the screen in a nice communist red (: + let mut fb = FACEBOOK.fb_mutex.lock(); + let fb_width = FACEBOOK.fb_width.lock(); + let fb_height = FACEBOOK.fb_height.lock(); + + draw_box(0,0,*fb_width,*fb_height, COMMUNIST_RED, fb.borrow_mut()); + // draw our funny text + draw_horizcentre_string((*fb_height / 2) - ((3 * 8)/2), "OOPSY WOOPSY, THE KERNEL HAD A FUCKY WUCKY UWU", CUM_WHITE, fb.borrow_mut()); + + loop {} +} + +pub extern "x86-interrupt" fn generic_error_with_code(stack_frame: InterruptStackFrame, error_code: u32) { + // cover the screen in a nice communist red (: + let mut fb = FACEBOOK.fb_mutex.lock(); + let fb_width = FACEBOOK.fb_width.lock(); + let fb_height = FACEBOOK.fb_height.lock(); + + draw_box(0,0,*fb_width,*fb_height, COMMUNIST_RED, fb.borrow_mut()); + // draw our funny text + draw_horizcentre_string((*fb_height / 2) - ((3 * 8)/2), "OOPSY WOOPSY, THE KERNEL HAD A FUCKY WUCKY UWU", CUM_WHITE, fb.borrow_mut()); + + loop {} +} + +pub extern "x86-interrupt" fn super_fuck_up_error(stack_frame: InterruptStackFrame) -> ! { + // cover the screen in a nice communist red (: + let mut fb = FACEBOOK.fb_mutex.lock(); + let fb_width = FACEBOOK.fb_width.lock(); + let fb_height = FACEBOOK.fb_height.lock(); + + draw_box(0,0,*fb_width,*fb_height, COMMUNIST_RED, fb.borrow_mut()); + // draw our funny text + draw_horizcentre_string((*fb_height / 2) - ((3 * 8)/2), "OOPSY WOOPSY, THE KERNEL HAD A FUCKY WUCKY UWU", CUM_WHITE, fb.borrow_mut()); + + loop {} +} + +pub extern "x86-interrupt" fn super_fuck_up_error_with_code(stack_frame: InterruptStackFrame, error_code: u32) -> ! { + // cover the screen in a nice communist red (: + let mut fb = FACEBOOK.fb_mutex.lock(); + let fb_width = FACEBOOK.fb_width.lock(); + let fb_height = FACEBOOK.fb_height.lock(); + + draw_box(0,0,*fb_width,*fb_height, COMMUNIST_RED, fb.borrow_mut()); + // draw our funny text + draw_horizcentre_string((*fb_height / 2) - ((3 * 8)/2), "OOPSY WOOPSY, THE KERNEL HAD A FUCKY WUCKY UWU", CUM_WHITE, fb.borrow_mut()); + + loop {} +} \ No newline at end of file diff --git a/src/internals/mod.rs b/src/internals/mod.rs index 941ed87..31c99d9 100644 --- a/src/internals/mod.rs +++ b/src/internals/mod.rs @@ -1,4 +1,7 @@ +pub mod errors; + pub mod WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood { + use core::arch::asm; use core::marker::PhantomData; #[derive(Clone, Copy)] @@ -19,35 +22,141 @@ pub mod WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood { pub const COMMUNIST_RED: Colour = Colour{r:245,g:77,b:30}; pub const CUM_WHITE: Colour = Colour{r:255,g:255,b:255}; + #[derive(Clone, Copy)] pub struct IDTEntry { based_offset: u16, code_selector: u16, ist_offset_wastes_6_bits: u8, attributes: u8, - mid_offset: u16, - offset_popping_off: u32, + offset_popping_off: u16, what: PhantomData } - pub struct InterruptDescriptorTable { - pub divide_error: IDTEntry, - pub debug: IDTEntry, - pub dream_mask_sus_version: IDTEntry, // non-maskable interrupt - pub breakpoint: IDTEntry, - pub into_detected_overflow: IDTEntry, - pub in_the_fortnite_storm: IDTEntry, // bound range exceeded - pub owo_whats_this: IDTEntry, // invalid opcode - pub device_not_available: IDTEntry, - pub fucky_wucky_twice: IDTEntry, // double fault - we_are_all_about_backwards_compatibility: IDTEntry, // coprocessor segment overrun - pub invalid_tss: IDTEntry, - pub segment_not_present: IDTEntry, - pub stack_segment_fault: IDTEntry, - pub uh_oh_we_gotta_hacker_here: IDTEntry, // general protection fault - + impl IDTEntry { + pub fn default() -> Self { + IDTEntry { + based_offset: 0, + code_selector: 0, + ist_offset_wastes_6_bits: 0, + attributes: 0, + offset_popping_off: 0, + what: PhantomData + } + } } - pub struct ErrorHandler(()); + macro_rules! impl_idtentry { + ($f:ty) => { + impl IDTEntry<$f> { + pub fn set_handler(&mut self, handler: $f) { + unsafe { // no shit this is unsafe, i wrote it (: + let handler_addr = handler as u32; + self.based_offset = handler_addr as u16; + self.offset_popping_off = (handler_addr >> 16) as u16; + let code_selector : u16; + asm!("mov {0:x}, cs", out(reg) code_selector, options(nomem, nostack, preserves_flags)); + self.code_selector = code_selector; + self.attributes = 0b10001110; + } + } + } + }; + } + + + pub struct InterruptDescriptorTable { + pub divide_error: IDTEntry, + pub debug: IDTEntry, + pub dream_mask_sus_version: IDTEntry, // non-maskable interrupt + pub breakpoint: IDTEntry, + pub into_detected_overflow: IDTEntry, + pub in_the_fortnite_storm: IDTEntry, // bound range exceeded + pub owo_whats_this: IDTEntry, // invalid opcode + pub device_not_available: IDTEntry, + pub fucky_wucky_twice: IDTEntry, // double fault + we_are_all_about_backwards_compatibility: IDTEntry, // coprocessor segment overrun + pub invalid_tss: IDTEntry, + pub segment_not_present: IDTEntry, + pub stack_segment_fault: IDTEntry, + pub uh_oh_we_gotta_hacker_here: IDTEntry, // general protection fault + pub page_fault: IDTEntry, + reserved_1: IDTEntry, // what the fuck is this?? the only comment is "vector nr.15" + pub x87_floating_point_exception: IDTEntry, // compatibility B) + pub alignment_check: IDTEntry, + pub machine_check: IDTEntry, + pub the_point_of_the_mask_float_exception: IDTEntry, // simd floating point exception + pub virtualization_exception: IDTEntry, // qemu WILL be added to windows 12 B) + reserved_2: IDTEntry, // another reserved + pub vmm_communication_exception: IDTEntry, // honestly too tired to check what this is + pub security_exception: IDTEntry, + reserved_3: IDTEntry, + + pub interrupts: [IDTEntry; 256 - 32], // the original one didn't make this public, but who care (: + } + + impl InterruptDescriptorTable { + pub fn new() -> InterruptDescriptorTable { + InterruptDescriptorTable { + divide_error: IDTEntry::default(), + debug: IDTEntry::default(), + dream_mask_sus_version: IDTEntry::default(), + breakpoint: IDTEntry::default(), + into_detected_overflow: IDTEntry::default(), + in_the_fortnite_storm: IDTEntry::default(), + owo_whats_this: IDTEntry::default(), + device_not_available: IDTEntry::default(), + fucky_wucky_twice: IDTEntry::default(), + we_are_all_about_backwards_compatibility: IDTEntry::default(), + invalid_tss: IDTEntry::default(), + segment_not_present: IDTEntry::default(), + stack_segment_fault: IDTEntry::default(), + uh_oh_we_gotta_hacker_here: IDTEntry::default(), + page_fault: IDTEntry::default(), + reserved_1: IDTEntry::default(), + x87_floating_point_exception: IDTEntry::default(), + alignment_check: IDTEntry::default(), + machine_check: IDTEntry::default(), + the_point_of_the_mask_float_exception: IDTEntry::default(), + virtualization_exception: IDTEntry::default(), + reserved_2: IDTEntry::default(), + vmm_communication_exception: IDTEntry::default(), + security_exception: IDTEntry::default(), + reserved_3: IDTEntry::default(), + interrupts: [IDTEntry::default(); 256 - 32] + } + } + + pub fn load(&'static self) { + unsafe { + // load the IDT + let idt_ptr = &self as *const _ as *const _; + asm!("lidt [{}]", in(reg) idt_ptr, options(readonly, nostack, preserves_flags)); + } + } + } + + pub type InterruptHandler = extern "x86-interrupt" fn(_: InterruptStackFrame); + pub type ErrorWithErrorCodeHandler = extern "x86-interrupt" fn(_: InterruptStackFrame, error_code: u32); + pub type SeriousFuckUpHandler = extern "x86-interrupt" fn(_: InterruptStackFrame) -> !; + pub type SeriousFuckUpWithErrorCodeHandler = extern "x86-interrupt" fn(_: InterruptStackFrame, error_code: u32) -> !; + + impl_idtentry!(InterruptHandler); + impl_idtentry!(ErrorWithErrorCodeHandler); + impl_idtentry!(SeriousFuckUpHandler); + impl_idtentry!(SeriousFuckUpWithErrorCodeHandler); + + // y'know the x86 crate has a point with doing it this way, so i'm gonna do the same (: + + pub struct InterruptStackFrame { + pub value: InterruptStackValues, + } + + pub struct InterruptStackValues { + pub instruction_pointer: u32, + pub code_segment: u32, + pub cpu_flags: u32, + pub stack_pointer: u32, + } pub enum ErrorKind { HardwareFuckUp, diff --git a/src/main.rs b/src/main.rs index 6fa17cb..12e368f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,16 @@ +#![feature(abi_x86_interrupt)] +#![feature(default_alloc_error_handler)] #![no_std] #![no_main] +use allocator::*; + +extern crate alloc; + +use alloc::vec::Vec; +use spin::Mutex; +use core::arch::asm; +use lazy_static::lazy_static; use bootloader::{entry_point, BootInfo, boot_info}; use core::panic::PanicInfo; use bootloader::boot_info::{FrameBuffer, FrameBufferInfo, PixelFormat}; @@ -10,7 +20,58 @@ use crate::serial::potential_serial_ports; mod font; mod serial; mod internals; +mod allocator; +pub struct FBInfo { + pub fb_mutex: Mutex>, + pub fb_pixelwidth: Mutex, + pub fb_colourtype: Mutex, + pub fb_pitch: Mutex, + pub fb_width: Mutex, + pub fb_height: Mutex, +} + +// THIS IS THE ONLY GLOBAL VARIABLE WE WILL EVER HAVE, MARK THIS ON MY FUCKING GRAVE +//pub static mut FRAMEBUFFER: Option = None; + +lazy_static! { + static ref IDT : InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + idt.divide_error.set_handler(internals::errors::generic_error); + idt.debug.set_handler(internals::errors::generic_error); + idt.dream_mask_sus_version.set_handler(internals::errors::generic_error); + idt.breakpoint.set_handler(internals::errors::generic_error); + idt.into_detected_overflow.set_handler(internals::errors::generic_error); + idt.in_the_fortnite_storm.set_handler(internals::errors::generic_error); + idt.owo_whats_this.set_handler(internals::errors::generic_error); + idt.device_not_available.set_handler(internals::errors::generic_error); + idt.fucky_wucky_twice.set_handler(internals::errors::super_fuck_up_error_with_code); + idt.invalid_tss.set_handler(internals::errors::generic_error_with_code); + idt.segment_not_present.set_handler(internals::errors::generic_error_with_code); + idt.stack_segment_fault.set_handler(internals::errors::generic_error_with_code); + idt.uh_oh_we_gotta_hacker_here.set_handler(internals::errors::generic_error_with_code); + idt.page_fault.set_handler(internals::errors::generic_error_with_code); + idt.x87_floating_point_exception.set_handler(internals::errors::generic_error); + idt.alignment_check.set_handler(internals::errors::generic_error_with_code); + idt.machine_check.set_handler(internals::errors::super_fuck_up_error); + idt.the_point_of_the_mask_float_exception.set_handler(internals::errors::generic_error); + idt.virtualization_exception.set_handler(internals::errors::generic_error); + idt.vmm_communication_exception.set_handler(internals::errors::generic_error_with_code); + idt.security_exception.set_handler(internals::errors::generic_error_with_code); + idt + }; + + static ref FACEBOOK : FBInfo = { + FBInfo { + fb_mutex: Mutex::new(Vec::new()), + fb_pixelwidth: Mutex::new(0), + fb_colourtype: Mutex::new(0), + fb_pitch: Mutex::new(0), + fb_width: Mutex::new(0), + fb_height: Mutex::new(0), + } + }; +} const RAINBOW : [Colour; 6] = [Colour{r:255,g:0,b:0}, Colour{r:255,g:127,b:0}, Colour{r:255,g:255,b:0}, Colour{r:0,g:255,b:0}, Colour{r:0,g:255,b:255}, Colour{r:0,g:0,b:255}]; @@ -28,7 +89,7 @@ fn KernelPanic(msg: KernelError, fb: &mut FrameBuffer) { } } -fn put_pixel(x: usize, y: usize, color: Colour, fb: &mut FrameBuffer) { +pub fn put_pixel(x: usize, y: usize, color: Colour, fb: &mut FrameBuffer) { let pixelwidth = fb.info().bytes_per_pixel; let pitch = fb.info().stride * pixelwidth; @@ -49,7 +110,7 @@ fn put_pixel(x: usize, y: usize, color: Colour, fb: &mut FrameBuffer) { } } -fn draw_box(x: usize, y: usize, width: usize, height: usize, color: Colour, fb: &mut FrameBuffer) { +pub fn draw_box(x: usize, y: usize, width: usize, height: usize, color: Colour, fb: &mut FrameBuffer) { let pixelwidth = fb.info().bytes_per_pixel; let pitch = fb.info().stride * pixelwidth; @@ -92,7 +153,7 @@ fn draw_char(x: usize, y: usize, c: char, color: Colour, fb: &mut FrameBuffer) { } } -fn draw_string(x: usize, y: usize, s: &str, color: Colour, fb: &mut FrameBuffer) { +pub fn draw_string(x: usize, y: usize, s: &str, color: Colour, fb: &mut FrameBuffer) { let mut x_tmp = x; let mut y_tmp = y; @@ -107,7 +168,7 @@ fn draw_string(x: usize, y: usize, s: &str, color: Colour, fb: &mut FrameBuffer) } } -fn draw_horizcentre_string(y: usize, s: &str, color: Colour, fb: &mut FrameBuffer) { +pub fn draw_horizcentre_string(y: usize, s: &str, color: Colour, fb: &mut FrameBuffer) { let mut x_tmp = (fb.info().horizontal_resolution - s.len() * 8) / 2; let mut y_tmp = y; @@ -122,7 +183,7 @@ fn draw_horizcentre_string(y: usize, s: &str, color: Colour, fb: &mut FrameBuffe } } -fn draw_rainbow_string(x: usize, y: usize, s: &str, fb: &mut FrameBuffer) { +pub fn draw_rainbow_string(x: usize, y: usize, s: &str, fb: &mut FrameBuffer) { let mut x_tmp = x; let mut y_tmp = y; @@ -141,23 +202,62 @@ fn draw_rainbow_string(x: usize, y: usize, s: &str, fb: &mut FrameBuffer) { } } + entry_point!(main); fn main(boot_info: &'static mut BootInfo) -> ! { if let Some(framebuffer) = boot_info.framebuffer.as_mut() { + // set up the framebuffer + unsafe { + let mut colourtype: u8; + if framebuffer.info().pixel_format == PixelFormat::RGB { + colourtype = 1; + } else { + colourtype = 0; + } + let mut temp_fb = FACEBOOK.fb_mutex.lock(); + *temp_fb = Vec::from_raw_parts(framebuffer.buffer_mut().as_mut_ptr(), framebuffer.buffer_mut().len(), framebuffer.buffer_mut().len()); + drop(temp_fb); + + let mut temp_fb_pixel_width = FACEBOOK.fb_pixelwidth.lock(); + *temp_fb_pixel_width = framebuffer.info().bytes_per_pixel; + drop(temp_fb_pixel_width); + + let mut temp_fb_fb_colourtype = FACEBOOK.fb_colourtype.lock(); + *temp_fb_fb_colourtype = colourtype; + drop(temp_fb_fb_colourtype); + + let mut temp_fb_fb_width = FACEBOOK.fb_width.lock(); + *temp_fb_fb_width = framebuffer.info().horizontal_resolution; + drop(temp_fb_fb_width); + + let mut temp_fb_fb_height = FACEBOOK.fb_height.lock(); + *temp_fb_fb_height = framebuffer.info().vertical_resolution; + drop(temp_fb_fb_height); + + let mut temp_fb_fb_pitch = FACEBOOK.fb_pitch.lock(); + *temp_fb_fb_pitch = framebuffer.info().stride; + drop(temp_fb_fb_pitch); + } // cover the screen in a nice blue draw_box(0, 0, framebuffer.info().horizontal_resolution, framebuffer.info().vertical_resolution, Colour{r:30,g:129,b:176}, framebuffer); let fb_width = framebuffer.info().horizontal_resolution; let fb_height = framebuffer.info().vertical_resolution; + IDT.load(); + // draw a test string //draw_string(20, 20, "i love drinking cum\nnewline test", Colour { r: 255, g: 0, b: 255 }, framebuffer); //draw_rainbow_string(20, 40, "gay sex", framebuffer); //draw_string(20,20, "),:\n\n\n\nuh oh! windows error! your computer is not compatible with windows 12\n\ncontact billgate@realmicrosoft.com to fix this issue!", Colour { r: 255, g: 255, b: 255}, framebuffer); + unsafe { + asm!("int3", options(nomem, nostack)); + } + draw_horizcentre_string(((fb_height/2)-4)-16, "welcome to windows 12! here is info:", CUM_WHITE, framebuffer); // time for some funny com port stuff