From 9ca98b77971d41c8414ef2d3ff35e91865ecc760 Mon Sep 17 00:00:00 2001 From: Emilia Daelman Date: Thu, 13 Feb 2025 16:08:59 +0100 Subject: [PATCH 1/4] start snake implementation --- src/lib.rs | 1 + src/snake_game.rs | 2 ++ src/snake_game/game_main.rs | 1 + src/snake_game/snake.rs | 54 +++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 src/snake_game.rs create mode 100644 src/snake_game/game_main.rs create mode 100644 src/snake_game/snake.rs diff --git a/src/lib.rs b/src/lib.rs index eca6bfd..5e18338 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod serial; pub mod vga_buffer; pub mod interrupts; pub mod gdt; +pub mod snake_game; pub trait Testable { fn run(&self) -> (); diff --git a/src/snake_game.rs b/src/snake_game.rs new file mode 100644 index 0000000..b4a8de7 --- /dev/null +++ b/src/snake_game.rs @@ -0,0 +1,2 @@ +mod game_main; +mod snake; diff --git a/src/snake_game/game_main.rs b/src/snake_game/game_main.rs new file mode 100644 index 0000000..6e34b18 --- /dev/null +++ b/src/snake_game/game_main.rs @@ -0,0 +1 @@ +use crate::snake_game::snake; diff --git a/src/snake_game/snake.rs b/src/snake_game/snake.rs new file mode 100644 index 0000000..cec06ed --- /dev/null +++ b/src/snake_game/snake.rs @@ -0,0 +1,54 @@ +use core::alloc; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Direction { + Up, + Right, + Down, + Left, +} + +pub enum Command { + Quit, + Turn(Direction), +} + +pub struct Point { + x: u16, + y: u16, +} + +impl Point { + pub fn new(x: u16, y: u16) -> Self { + Self { x, y } + } + + pub fn transform(&self, direction: Direction, times: u16) -> Self { + let times = times as i16; + let transformation = match direction { + Direction::Up => (0, -times), + Direction::Right => (times, 0), + Direction::Down => (0, times), + Direction::Left => (-times, 0), + }; + + Self::new( + Self::transform_value(self.x, transformation.0), + Self::transform_value(self.y, transformation.1), + ) + } + + fn transform_value(value: u16, by: i16) -> u16 { + if by.is_negative() && by.abs() as u16 > value { + panic!("transforming vlaue {} by {} would result in a negative number", value, by) + } else { + (value as i16 + by) as u16 + } + } +} + +#[derive(Debug)] +pub struct Snake { + direction: Direction, + digesting: bool, +} From 395535ac106a20e6d3ac01567fdf68bd94afb9cf Mon Sep 17 00:00:00 2001 From: Emilia Daelman Date: Sat, 15 Feb 2025 14:33:16 +0100 Subject: [PATCH 2/4] WIP snake --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/snake_game/game_main.rs | 135 +++++++++++++++++++++++++++++++++++- src/snake_game/snake.rs | 57 ++++++++++++++- src/vga_buffer.rs | 4 +- 5 files changed, 200 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90b544a..ff72ff9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,7 @@ dependencies = [ "bootloader", "lazy_static", "linked_list_allocator", + "nanorand", "pc-keyboard", "pic8259", "spin 0.5.2", @@ -75,6 +76,12 @@ dependencies = [ "x86_64", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + [[package]] name = "pc-keyboard" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index db497f3..8f65c5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ uart_16550 = "0.2.0" pic8259 = "0.10.1" pc-keyboard = "0.7.0" linked_list_allocator = "0.9.0" +nanorand = { version = "0.7.0", default-features = false, features = ["alloc", "wyrand"] } [dependencies.lazy_static] version = "1.0" diff --git a/src/snake_game/game_main.rs b/src/snake_game/game_main.rs index 6e34b18..3bcc1eb 100644 --- a/src/snake_game/game_main.rs +++ b/src/snake_game/game_main.rs @@ -1 +1,134 @@ -use crate::snake_game::snake; +use crate::println; +use crate::snake_game::snake::Command; +use crate::snake_game::snake::Direction; +use crate::snake_game::snake::Point; +use crate::snake_game::snake::Snake; +use crate::vga_buffer; +use nanorand::{Rng, WyRand}; + +const MAX_INTERVAL: u16 = 700; +const MIN_INTERVAL: u16 = 200; +const MAX_SPEED: u16 = 20; + +#[derive(Debug)] +pub struct Game { + buffer_size: (u16, u16), + width: u16, + height: u16, + food: Option, + snake: Snake, + speed: u16, + score: u16, +} + +impl Game { + pub fn new(width: u16, height: u16) -> Self { + let buffer_size = ( + vga_buffer::BUFFER_WIDTH as u16, + vga_buffer::BUFFER_HEIGHT as u16, + ); + Self { + buffer_size, + width, + height, + food: None, + snake: Snake::new( + Point::new(width / 2, height / 2), + 3, + match WyRand::new().generate_range(0..4) { + 0 => Direction::Up, + 1 => Direction::Right, + 2 => Direction::Down, + _ => Direction::Left, + }, + ), + speed: 0, + score: 0, + } + } + + pub fn run(&mut self) { + self.place_food(); + self.prepare_ui(); + self.render(); + + let mut done = false; + while !done { + let interval = self.calculate_interval(); + let direction = self.snake.get_direction(); + let now = Instant::now(); + + while now.elapsed() < interval { + if let Some(command) = self.get_command(interval - now.elapsed()) { + match command { + Command::Quit => { + done = true; + break; + } + Command::Turn(towards) => { + if direction != towards && direction.opposite() != towards { + self.snake.set_direction(towards); + } + } + } + } + } + + if self.has_collided_with_wall() || self.has_bitten_itself() { + done = true; + } else { + self.snake.slither(); + + if let Some(food_point) = self.food { + if self.snake.get_head_point() == food_point { + self.snake.grow(); + self.place_food(); + self.score += 1; + + if self.score % ((self.width * self.height) / MAX_SPEED) == 0 { + self.speed += 1; + } + } + } + + self.render(); + } + } + + self.restore_ui(); + + println!("Game Over! Your score is {}", self.score); + } + + fn place_food(&mut self) { + loop { + let random_x = WyRand::new().generate_range(0..self.width); + let random_y = WyRand::new().generate_range(0..self.height); + let point = Point::new(random_x, random_y); + if !self.snake.contains_point(&point) { + self.food = Some(point); + break; + } + } + } + + fn prepare_ui(&mut self) { + enable_raw_mode().unwrap(); + self.stdout + .execute(SetSize(self.width + 3, self.height + 3)) + .unwrap() + .execute(Clear(ClearType::All)) + .unwrap() + .execute(Hide) + .unwrap(); + } + + fn calculate_interval(&self) -> Duration { + let speed = MAX_SPEED - self.speed; + Duration::from_millis( + (MIN_INTERVAL + (((MAX_INTERVAL - MIN_INTERVAL) / MAX_SPEED) * speed)) as u64, + ) + } + + +} diff --git a/src/snake_game/snake.rs b/src/snake_game/snake.rs index cec06ed..d1b10f9 100644 --- a/src/snake_game/snake.rs +++ b/src/snake_game/snake.rs @@ -1,4 +1,4 @@ -use core::alloc; +use ::alloc::vec::Vec; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Direction { @@ -8,11 +8,23 @@ pub enum Direction { Left, } +impl Direction { + pub fn opposite(&self) -> Self { + match self { + Self::Up => Self::Down, + Self::Right => Self::Left, + Self::Down => Self::Up, + Self::Left => Self::Right, + } + } +} + pub enum Command { Quit, Turn(Direction), } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Point { x: u16, y: u16, @@ -49,6 +61,49 @@ impl Point { #[derive(Debug)] pub struct Snake { + body: Vec, direction: Direction, digesting: bool, } + +impl Snake { + pub fn new(start: Point, length: u16, direction: Direction) -> Self { + let opposite = direction.opposite(); + let body: Vec = (0..length).into_iter().map(|i| start.transform(opposite, i)).collect(); + + Self { body, direction, digesting: false } + } + + pub fn get_head_point(&self) -> Point { + self.body.first().unwrap().clone() + } + + pub fn get_body_points(&self) -> Vec { + self.body.clone() + } + + pub fn get_direction(&self) -> Direction { + self.direction.clone() + } + + pub fn contains_point(&self, point: &Point) -> bool { + self.body.contains(point) + } + + pub fn slither(&mut self) { + self.body.insert(0, self.body.first().unwrap().transform(self.direction, 1)); + if !self.digesting { + self.body.remove(self.body.len() - 1); + } else { + self.digesting = false; + } + } + + pub fn set_direction(&mut self, direction: Direction) { + self.direction = direction; + } + + pub fn grow(&mut self) { + self.digesting = true; + } +} diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 6b3788d..4592928 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -42,8 +42,8 @@ struct ScreenChar { color_code: ColorCode, } -const BUFFER_HEIGHT: usize = 25; -const BUFFER_WIDTH: usize = 80; +pub const BUFFER_HEIGHT: usize = 25; +pub const BUFFER_WIDTH: usize = 80; #[repr(transparent)] struct Buffer { From 77b2b26781a04ceadd040d45ccec54d291c306c8 Mon Sep 17 00:00:00 2001 From: Emilia Daelman Date: Sat, 15 Feb 2025 15:46:43 +0100 Subject: [PATCH 3/4] WIP snake --- src/lib.rs | 1 + src/os_mode.rs | 4 +++ src/snake_game/game_main.rs | 49 +++++++++++++++++++++++++++++-------- src/snake_game/snake.rs | 4 +-- src/vga_buffer.rs | 22 +++++++++++++---- 5 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 src/os_mode.rs diff --git a/src/lib.rs b/src/lib.rs index 42b43e2..1784787 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ pub mod gdt; pub mod memory; pub mod allocator; pub mod snake_game; +pub mod os_mode; pub trait Testable { fn run(&self) -> (); diff --git a/src/os_mode.rs b/src/os_mode.rs new file mode 100644 index 0000000..f4e3938 --- /dev/null +++ b/src/os_mode.rs @@ -0,0 +1,4 @@ +pub enum OsMode { + KernelMode, + SnakeMode, +} diff --git a/src/snake_game/game_main.rs b/src/snake_game/game_main.rs index 3bcc1eb..4b5d35c 100644 --- a/src/snake_game/game_main.rs +++ b/src/snake_game/game_main.rs @@ -4,7 +4,10 @@ use crate::snake_game::snake::Direction; use crate::snake_game::snake::Point; use crate::snake_game::snake::Snake; use crate::vga_buffer; +use crate::vga_buffer::BUFFER_HEIGHT; +use crate::vga_buffer::BUFFER_WIDTH; use nanorand::{Rng, WyRand}; +use core::time::Duration; const MAX_INTERVAL: u16 = 700; const MIN_INTERVAL: u16 = 200; @@ -95,8 +98,6 @@ impl Game { } } - self.restore_ui(); - println!("Game Over! Your score is {}", self.score); } @@ -113,14 +114,6 @@ impl Game { } fn prepare_ui(&mut self) { - enable_raw_mode().unwrap(); - self.stdout - .execute(SetSize(self.width + 3, self.height + 3)) - .unwrap() - .execute(Clear(ClearType::All)) - .unwrap() - .execute(Hide) - .unwrap(); } fn calculate_interval(&self) -> Duration { @@ -130,5 +123,41 @@ impl Game { ) } + fn get_command(&self, wait_for: Duration) -> Option { + + None + } + + fn wait_for_key_event(&self, wait_for: Duration) -> ! { + loop {} + } + + fn has_collided_with_wall(&self) -> bool { + let head_point = self.snake.get_head_point(); + + match self.snake.get_direction() { + Direction::Up => head_point.y == 0, + Direction::Right => head_point.x == self.width - 1, + Direction::Down => head_point.y == self.height - 1, + Direction::Left => head_point.x == 0, + } + } + + fn has_bitten_itself(&self) -> bool { + let next_head_point = self.snake.get_head_point().transform(self.snake.get_direction(), 1); + let mut next_body_points = self.snake.get_body_points().clone(); + next_body_points.remove(next_body_points.len() - 1); + next_body_points.remove(0); + next_body_points.contains(&next_head_point) + } + + fn render(&self) { + use volatile::Volatile; + use vga_buffer::ScreenChar; + use vga_buffer::{Color, ColorCode}; + + vga_buffer::print_buffer(core::array::from_fn::<_, BUFFER_HEIGHT, _>(|_| core::array::from_fn::<_, BUFFER_WIDTH, _>(|_| Volatile::new(ScreenChar { ascii_character: 0, color_code: ColorCode::new(Color::Red, Color::White) })))); + todo!() + } } diff --git a/src/snake_game/snake.rs b/src/snake_game/snake.rs index d1b10f9..f6159ba 100644 --- a/src/snake_game/snake.rs +++ b/src/snake_game/snake.rs @@ -26,8 +26,8 @@ pub enum Command { #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Point { - x: u16, - y: u16, + pub x: u16, + pub y: u16, } impl Point { diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 4592928..2f55b0c 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -27,19 +27,19 @@ pub enum Color { #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] -struct ColorCode(u8); +pub struct ColorCode(u8); impl ColorCode { - fn new(foreground: Color, background: Color) -> ColorCode { + pub fn new(foreground: Color, background: Color) -> ColorCode { ColorCode((background as u8) << 4 | (foreground as u8)) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] -struct ScreenChar { - ascii_character: u8, - color_code: ColorCode, +pub struct ScreenChar { + pub ascii_character: u8, + pub color_code: ColorCode, } pub const BUFFER_HEIGHT: usize = 25; @@ -64,6 +64,10 @@ impl fmt::Write for Writer { } impl Writer { + pub fn print_buffer(&mut self, buffer: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT]) { + self.buffer.chars = buffer; + } + pub fn write_byte(&mut self, byte: u8) { match byte { b'\n' => self.new_line(), @@ -130,6 +134,14 @@ macro_rules! println { ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } +pub fn print_buffer(buffer: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT]) { + use x86_64::instructions::interrupts; + + interrupts::without_interrupts(|| { + WRITER.lock().print_buffer(buffer); + }) +} + #[doc(hidden)] pub fn _print(args: fmt::Arguments) { use core::fmt::Write; From c9effdd1a1c20ad5a6a59310839933066ef45fb3 Mon Sep 17 00:00:00 2001 From: Emilia Daelman Date: Sun, 16 Feb 2025 23:14:24 +0100 Subject: [PATCH 4/4] implement clock --- Cargo.lock | 30 ++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/clock.rs | 16 ++++++++++++++++ src/interrupts.rs | 18 +++++++++++++++++- src/lib.rs | 1 + src/main.rs | 4 +++- src/snake_game/game_main.rs | 33 +++++++++++++++++++++++---------- 7 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 src/clock.rs diff --git a/Cargo.lock b/Cargo.lock index 7993cb4..7347b02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,23 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-timers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b13fd4109a4b9b49b1c747c64b2d813f33639b723e2c3b7b36190edd051bc06" +dependencies = [ + "embedded-hal", + "nb", + "void", +] + [[package]] name = "futures-core" version = "0.3.31" @@ -121,6 +138,7 @@ dependencies = [ "bootloader", "conquer-once", "crossbeam-queue", + "embedded-timers", "futures-util", "lazy_static", "linked_list_allocator", @@ -139,6 +157,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + [[package]] name = "pc-keyboard" version = "0.7.0" @@ -210,6 +234,12 @@ dependencies = [ "x86_64", ] +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "volatile" version = "0.2.7" diff --git a/Cargo.toml b/Cargo.toml index 316e43b..8224cc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ pic8259 = "0.10.1" pc-keyboard = "0.7.0" nanorand = { version = "0.7.0", default-features = false, features = ["alloc", "wyrand"] } linked_list_allocator = "0.10.0" +embedded-timers = "0.4.0" [dependencies.lazy_static] version = "1.0" diff --git a/src/clock.rs b/src/clock.rs new file mode 100644 index 0000000..f4d7885 --- /dev/null +++ b/src/clock.rs @@ -0,0 +1,16 @@ +static TICKS: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); + +pub fn tick_handler() { + TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed); +} + +pub struct MilliSecondClock32; + +impl embedded_timers::clock::Clock for MilliSecondClock32 { + type Instant = embedded_timers::instant::Instant32<1000>; + + fn now(&self) -> Self::Instant { + let ticks = TICKS.load(core::sync::atomic::Ordering::Relaxed); + embedded_timers::instant::Instant32::<1000>::new(ticks) + } +} diff --git a/src/interrupts.rs b/src/interrupts.rs index 32f14b8..2f8eaff 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -47,6 +47,21 @@ lazy_static! { pub fn init_idt() { IDT.load(); + set_timer_speed(); +} + +fn set_timer_speed() { + use x86_64::instructions::port::Port; + + let mut port = Port::new(0x43); + + unsafe { port.write(0x36 as u8); } + port = Port::new(0x40); + unsafe { + port.write(4 as u8); + port.write(169 as u8); + } + } extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { @@ -70,7 +85,8 @@ extern "x86-interrupt" fn page_fault_handler(stack_frame: InterruptStackFrame, e } extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { - print!("."); +// print!("."); + crate::clock::tick_handler(); unsafe { PICS.lock() .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); diff --git a/src/lib.rs b/src/lib.rs index de34f71..71c16e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ pub mod memory; pub mod serial; pub mod task; pub mod vga_buffer; +pub mod clock; pub trait Testable { fn run(&self) -> (); diff --git a/src/main.rs b/src/main.rs index 59c1055..e8fe226 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ extern crate alloc; use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; -use milly_os::task::{executor::Executor, Task, keyboard}; +use milly_os::task::{executor::Executor, keyboard, Task}; use x86_64::VirtAddr; mod serial; @@ -31,7 +31,9 @@ entry_point!(kernel_main); fn kernel_main(boot_info: &'static BootInfo) -> ! { use milly_os::allocator; + use milly_os::clock::MilliSecondClock32; use milly_os::memory::{self, BootInfoFrameAllocator}; + use embedded_timers::delay::Delay; println!("Hello world{}", "!"); milly_os::init(); diff --git a/src/snake_game/game_main.rs b/src/snake_game/game_main.rs index 4b5d35c..adde2f6 100644 --- a/src/snake_game/game_main.rs +++ b/src/snake_game/game_main.rs @@ -1,3 +1,4 @@ +use crate::clock::MilliSecondClock32; use crate::println; use crate::snake_game::snake::Command; use crate::snake_game::snake::Direction; @@ -6,8 +7,10 @@ use crate::snake_game::snake::Snake; use crate::vga_buffer; use crate::vga_buffer::BUFFER_HEIGHT; use crate::vga_buffer::BUFFER_WIDTH; -use nanorand::{Rng, WyRand}; use core::time::Duration; +use embedded_timers::clock::Clock; +use embedded_timers::instant::Instant; +use nanorand::{Rng, WyRand}; const MAX_INTERVAL: u16 = 700; const MIN_INTERVAL: u16 = 200; @@ -55,14 +58,16 @@ impl Game { self.prepare_ui(); self.render(); + let clock = MilliSecondClock32; + let mut done = false; while !done { let interval = self.calculate_interval(); let direction = self.snake.get_direction(); - let now = Instant::now(); + let now = clock.now(); - while now.elapsed() < interval { - if let Some(command) = self.get_command(interval - now.elapsed()) { + while clock.elapsed(now) < interval { + if let Some(command) = self.get_command(interval - clock.elapsed(now)) { match command { Command::Quit => { done = true; @@ -113,8 +118,7 @@ impl Game { } } - fn prepare_ui(&mut self) { - } + fn prepare_ui(&mut self) {} fn calculate_interval(&self) -> Duration { let speed = MAX_SPEED - self.speed; @@ -124,7 +128,6 @@ impl Game { } fn get_command(&self, wait_for: Duration) -> Option { - None } @@ -144,7 +147,10 @@ impl Game { } fn has_bitten_itself(&self) -> bool { - let next_head_point = self.snake.get_head_point().transform(self.snake.get_direction(), 1); + let next_head_point = self + .snake + .get_head_point() + .transform(self.snake.get_direction(), 1); let mut next_body_points = self.snake.get_body_points().clone(); next_body_points.remove(next_body_points.len() - 1); next_body_points.remove(0); @@ -153,11 +159,18 @@ impl Game { } fn render(&self) { - use volatile::Volatile; use vga_buffer::ScreenChar; use vga_buffer::{Color, ColorCode}; + use volatile::Volatile; - vga_buffer::print_buffer(core::array::from_fn::<_, BUFFER_HEIGHT, _>(|_| core::array::from_fn::<_, BUFFER_WIDTH, _>(|_| Volatile::new(ScreenChar { ascii_character: 0, color_code: ColorCode::new(Color::Red, Color::White) })))); + vga_buffer::print_buffer(core::array::from_fn::<_, BUFFER_HEIGHT, _>(|_| { + core::array::from_fn::<_, BUFFER_WIDTH, _>(|_| { + Volatile::new(ScreenChar { + ascii_character: 0, + color_code: ColorCode::new(Color::Red, Color::White), + }) + }) + })); todo!() } }