From fbb592369ba03a49d647c7fd817b0c3294f29d9b Mon Sep 17 00:00:00 2001 From: Yu Chen Date: Sun, 8 Jan 2023 22:41:41 +0800 Subject: [PATCH] Add snake gui app and update os/usr parts. Now snake can run! --- os/Cargo.toml | 3 +- os/src/boards/qemu.rs | 2 +- os/src/drivers/chardev/ns16550a.rs | 6 +- os/src/drivers/input/mod.rs | 5 +- os/src/syscall/fs.rs | 2 +- os/src/syscall/input.rs | 12 + os/src/syscall/mod.rs | 2 + user/Cargo.toml | 9 +- user/src/bin/{embed_graph.rs => gui_rect.rs} | 0 user/src/bin/gui_snake.rs | 403 ++++++++++++++++++ .../bin/{embed_graph_uart.rs => gui_uart.rs} | 0 user/src/bin/random_num.rs | 16 + user/src/lib.rs | 7 + user/src/syscall.rs | 5 + 14 files changed, 457 insertions(+), 15 deletions(-) rename user/src/bin/{embed_graph.rs => gui_rect.rs} (100%) create mode 100644 user/src/bin/gui_snake.rs rename user/src/bin/{embed_graph_uart.rs => gui_uart.rs} (100%) create mode 100644 user/src/bin/random_num.rs diff --git a/os/Cargo.toml b/os/Cargo.toml index 633ec1054..ded2765e8 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -15,9 +15,8 @@ xmas-elf = "0.7.0" volatile = "0.3" #virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "70b5850" } - easy-fs = { path = "../easy-fs" } -virtio-input-decoder = "0.1.4" +#virtio-input-decoder = "0.1.4" embedded-graphics = "0.7.1" tinybmp = "0.3.1" diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs index ffabff6ae..0bc0f4c27 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -12,7 +12,7 @@ pub type CharDeviceImpl = crate::drivers::chardev::NS16550a; pub const VIRT_PLIC: usize = 0xC00_0000; pub const VIRT_UART: usize = 0x1000_0000; - +#[allow(unused)] pub const VIRTGPU_XRES: u32 = 1280; #[allow(unused)] pub const VIRTGPU_YRES: u32 = 800; diff --git a/os/src/drivers/chardev/ns16550a.rs b/os/src/drivers/chardev/ns16550a.rs index d10dd9096..127a4ce19 100644 --- a/os/src/drivers/chardev/ns16550a.rs +++ b/os/src/drivers/chardev/ns16550a.rs @@ -131,7 +131,7 @@ pub struct NS16550a { impl NS16550a { pub fn new() -> Self { - let mut inner = NS16550aInner { + let inner = NS16550aInner { ns16550a: NS16550aRaw::new(BASE_ADDR), read_buffer: VecDeque::new(), }; @@ -141,6 +141,10 @@ impl NS16550a { condvar: Condvar::new(), } } + + pub fn read_buffer_is_empty(&self) -> bool { + self.inner.exclusive_session(|inner| inner.read_buffer.is_empty()) + } } impl CharDevice for NS16550a { diff --git a/os/src/drivers/input/mod.rs b/os/src/drivers/input/mod.rs index 76c64f7c1..857f12a3e 100644 --- a/os/src/drivers/input/mod.rs +++ b/os/src/drivers/input/mod.rs @@ -1,12 +1,10 @@ use crate::drivers::bus::virtio::VirtioHal; use crate::sync::{Condvar, UPIntrFreeCell}; use crate::task::schedule; -use alloc::collections::BTreeMap; use alloc::collections::VecDeque; use alloc::sync::Arc; use core::any::Any; use virtio_drivers::{VirtIOHeader, VirtIOInput}; -use virtio_input_decoder::{Decoder, Key, KeyType}; const VIRTIO5: usize = 0x10005000; const VIRTIO6: usize = 0x10006000; @@ -112,7 +110,8 @@ impl InputDevice for VirtIOInputWrapper { | (event.code as u64) << 32 | (event.value) as u64; inner.events.push_back(result); - println!("[KERN] inputdev_handle_irq: event: {:x}", result); + // for test + //println!("[KERN] inputdev_handle_irq: event: {:x}", result); } }); if count > 0 { diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index 275882570..af1585c16 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -96,4 +96,4 @@ pub fn sys_dup(fd: usize) -> isize { let new_fd = inner.alloc_fd(); inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap())); new_fd as isize -} +} \ No newline at end of file diff --git a/os/src/syscall/input.rs b/os/src/syscall/input.rs index 9f59710f2..7a2825981 100644 --- a/os/src/syscall/input.rs +++ b/os/src/syscall/input.rs @@ -14,4 +14,16 @@ pub fn sys_event_get() ->isize { 0 } +} + +use crate::drivers::chardev::UART; + +/// check UART's read-buffer is empty or not +pub fn sys_key_pressed() -> isize { + let res =!UART.read_buffer_is_empty(); + if res { + 1 + } else { + 0 + } } \ No newline at end of file diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 729caa059..d883d52dd 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -28,6 +28,7 @@ const SYSCALL_CONDVAR_WAIT: usize = 1032; const SYSCALL_FRAMEBUFFER: usize = 2000; const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; mod fs; mod process; @@ -75,6 +76,7 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_FRAMEBUFFER => sys_framebuffer(), SYSCALL_FRAMEBUFFER_FLUSH => sys_framebuffer_flush(), SYSCALL_EVENT_GET => sys_event_get(), + SYSCALL_KEY_PRESSED => sys_key_pressed(), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/user/Cargo.toml b/user/Cargo.toml index 6666a5a9c..35e06d5dd 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -11,11 +11,6 @@ buddy_system_allocator = "0.6" bitflags = "1.2.1" riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } embedded-graphics = "0.7.1" -# lazy_static = { version = "1.4.0", features = ["spin_no_std"] } - +oorandom ="11" [profile.release] -debug = true - -# [features] -# board_qemu = [] -# board_k210 = [] \ No newline at end of file +debug = true \ No newline at end of file diff --git a/user/src/bin/embed_graph.rs b/user/src/bin/gui_rect.rs similarity index 100% rename from user/src/bin/embed_graph.rs rename to user/src/bin/gui_rect.rs diff --git a/user/src/bin/gui_snake.rs b/user/src/bin/gui_snake.rs new file mode 100644 index 000000000..c0148725a --- /dev/null +++ b/user/src/bin/gui_snake.rs @@ -0,0 +1,403 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::console::getchar; +use user_lib::{framebuffer, framebuffer_flush, key_pressed, sleep}; + +use embedded_graphics::pixelcolor::*; +use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::Primitive; +use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; +use embedded_graphics::Pixel; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; +use oorandom; //random generator + +pub const VIRTGPU_XRES: usize = 1280; +pub const VIRTGPU_YRES: usize = 800; +pub const VIRTGPU_LEN: usize = VIRTGPU_XRES * VIRTGPU_YRES * 4; + +pub struct Display { + pub size: Size, + pub point: Point, + pub fb: &'static mut [u8], +} + +impl Display { + pub fn new(size: Size, point: Point) -> Self { + let fb_ptr = framebuffer() as *mut u8; + println!( + "Hello world from user mode program! 0x{:X} , len {}", + fb_ptr as usize, VIRTGPU_LEN + ); + let fb = + unsafe { core::slice::from_raw_parts_mut(fb_ptr as *mut u8, VIRTGPU_LEN as usize) }; + Self { size, point, fb } + } +} + +impl OriginDimensions for Display { + fn size(&self) -> Size { + self.size + } +} + +impl DrawTarget for Display { + type Color = Rgb888; + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + pixels.into_iter().for_each(|px| { + let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x) + as usize + * 4; + if idx + 2 >= self.fb.len() { + return; + } + self.fb[idx] = px.1.b(); + self.fb[idx + 1] = px.1.g(); + self.fb[idx + 2] = px.1.r(); + }); + framebuffer_flush(); + Ok(()) + } +} +struct Snake { + parts: [Pixel; MAX_SIZE], + len: usize, + direction: Direction, + size_x: u32, + size_y: u32, +} + +struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> { + snake: &'a Snake, + index: usize, +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake { + type Item = Pixel; + type IntoIter = SnakeIntoIterator<'a, T, MAX_SIZE>; + + fn into_iter(self) -> Self::IntoIter { + SnakeIntoIterator { + snake: self, + index: 0, + } + } +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> Iterator for SnakeIntoIterator<'a, T, MAX_SIZE> { + type Item = Pixel; + + fn next(&mut self) -> Option { + let cur = self.snake.parts[self.index]; + if self.index < self.snake.len { + self.index += 1; + return Some(cur); + } + None + } +} + +impl Snake { + fn new(color: T, size_x: u32, size_y: u32) -> Snake { + Snake { + parts: [Pixel::(Point { x: 0, y: 0 }, color); MAX_SIZE], + len: 1, + direction: Direction::None, + size_x, + size_y, + } + } + fn set_direction(&mut self, direction: Direction) { + self.direction = direction; + } + fn contains(&self, this: Point) -> bool { + for part in self.into_iter() { + if part.0 == this { + return true; + }; + } + false + } + fn grow(&mut self) { + if self.len < MAX_SIZE - 1 { + self.len += 1; + } + } + fn make_step(&mut self) { + let mut i = self.len; + while i > 0 { + self.parts[i] = self.parts[i - 1]; + i -= 1; + } + match self.direction { + Direction::Left => { + if self.parts[0].0.x == 0 { + self.parts[0].0.x = (self.size_x - 1) as i32; + } else { + self.parts[0].0.x -= 1; + } + } + Direction::Right => { + if self.parts[0].0.x == (self.size_x - 1) as i32 { + self.parts[0].0.x = 0; + } else { + self.parts[0].0.x += 1; + } + } + Direction::Up => { + if self.parts[0].0.y == 0 { + self.parts[0].0.y = (self.size_y - 1) as i32; + } else { + self.parts[0].0.y -= 1; + } + } + Direction::Down => { + if self.parts[0].0.y == (self.size_y - 1) as i32 { + self.parts[0].0.y = 0; + } else { + self.parts[0].0.y += 1; + } + } + Direction::None => {} + } + } +} + +struct Food { + size_x: u32, + size_y: u32, + place: Pixel, + rng: oorandom::Rand32, +} + +impl Food { + pub fn new(color: T, size_x: u32, size_y: u32) -> Self { + let seed = 4; + let rng = oorandom::Rand32::new(seed); + Food { + size_x, + size_y, + place: Pixel(Point { x: 0, y: 0 }, color), + rng, + } + } + fn replace<'a, const MAX_SIZE: usize>(&mut self, iter_source: &Snake) { + let mut p: Point; + 'outer: loop { + let random_number = self.rng.rand_u32(); + let blocked_positions = iter_source.into_iter(); + p = Point { + x: ((random_number >> 24) as u16 % self.size_x as u16).into(), + y: ((random_number >> 16) as u16 % self.size_y as u16).into(), + }; + for blocked_position in blocked_positions { + if p == blocked_position.0 { + continue 'outer; + } + } + break; + } + self.place = Pixel:: { + 0: p, + 1: self.place.1, + } + } + fn get_pixel(&self) -> Pixel { + self.place + } +} + +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum Direction { + Left, + Right, + Up, + Down, + None, +} + +pub struct SnakeGame { + snake: Snake, + food: Food, + food_age: u32, + food_lifetime: u32, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl SnakeGame { + pub fn new( + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, + snake_color: T, + food_color: T, + food_lifetime: u32, + ) -> Self { + let snake = Snake::::new(snake_color, size_x / scale_x, size_y / scale_y); + let mut food = Food::::new(food_color, size_x / scale_x, size_y / scale_y); + food.replace(&snake); + SnakeGame { + snake, + food, + food_age: 0, + food_lifetime, + size_x, + size_y, + scale_x, + scale_y, + } + } + pub fn set_direction(&mut self, direction: Direction) { + self.snake.set_direction(direction); + } + pub fn draw(&mut self, target: &mut D) -> () + where + D: DrawTarget, + { + self.snake.make_step(); + let hit = self.snake.contains(self.food.get_pixel().0); + if hit { + self.snake.grow(); + } + self.food_age += 1; + if self.food_age >= self.food_lifetime || hit { + self.food.replace(&self.snake); + self.food_age = 0; + } + + let mut scaled_display = ScaledDisplay:: { + real_display: target, + size_x: self.size_x / self.scale_x, + size_y: self.size_y / self.scale_y, + scale_x: self.scale_x, + scale_y: self.scale_y, + }; + + for part in self.snake.into_iter() { + _ = part.draw(&mut scaled_display); + } + _ = self.food.get_pixel().draw(&mut scaled_display); + } +} + +/// A dummy DrawTarget implementation that can magnify each pixel so the user code does not need to adapt for scaling things +struct ScaledDisplay<'a, T: DrawTarget> { + real_display: &'a mut T, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl<'a, T: DrawTarget> DrawTarget for ScaledDisplay<'a, T> { + type Color = T::Color; + type Error = T::Error; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels { + let style = PrimitiveStyle::with_fill(pixel.1); + Rectangle::new( + Point::new( + pixel.0.x * self.scale_x as i32, + pixel.0.y * self.scale_y as i32, + ), + Size::new(self.scale_x as u32, self.scale_y as u32), + ) + .into_styled(style) + .draw(self.real_display)?; + } + Ok(()) + } +} + +impl<'a, T: DrawTarget> OriginDimensions for ScaledDisplay<'a, T> { + fn size(&self) -> Size { + Size::new(self.size_x as u32, self.size_y as u32) + } +} + +#[cfg(test)] +mod tests { + + use crate::Snake; + use embedded_graphics::pixelcolor::*; + use embedded_graphics::prelude::*; + + #[test] + fn snake_basic() { + let mut snake = Snake::::new(Rgb888::RED, 8, 8); + snake.set_direction(crate::Direction::Right); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().next().unwrap() + ); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + snake.set_direction(crate::Direction::Down); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 1 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(2).unwrap() + ); + assert_eq!(true, snake.contains(Point { x: 0, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 1 })); + } +} + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +#[no_mangle] +pub fn main() -> i32 { + let mut disp = Display::new(Size::new(1280, 800), Point::new(0, 0)); + let mut game = SnakeGame::<20, Rgb888>::new(1280, 800, 20, 20, Rgb888::RED, Rgb888::YELLOW, 50); + loop { + if key_pressed() { + let c = getchar(); + match c { + LF => break, + CR => break, + b'w' => game.set_direction(Direction::Up), + b's' => game.set_direction(Direction::Down), + b'a' => game.set_direction(Direction::Left), + b'd' => game.set_direction(Direction::Right), + _ => (), + } + } + let _ = disp.clear(Rgb888::BLACK).unwrap(); + game.draw(&mut disp); + sleep(10); + } + 0 +} diff --git a/user/src/bin/embed_graph_uart.rs b/user/src/bin/gui_uart.rs similarity index 100% rename from user/src/bin/embed_graph_uart.rs rename to user/src/bin/gui_uart.rs diff --git a/user/src/bin/random_num.rs b/user/src/bin/random_num.rs new file mode 100644 index 000000000..dd9ac00df --- /dev/null +++ b/user/src/bin/random_num.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use oorandom; + +#[no_mangle] +pub fn main() -> i32 { + println!("random num program!"); + let seed = 4; + let mut rng = oorandom::Rand32::new(seed); + println!("OORandom: Random number 32bit: {}", rng.rand_i32()); + println!("OORandom: Random number range: {}", rng.rand_range(1..100)); + 0 +} \ No newline at end of file diff --git a/user/src/lib.rs b/user/src/lib.rs index dd7261eef..31ce9ba2c 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -209,6 +209,13 @@ pub fn event_get() -> isize { sys_event_get() } +pub fn key_pressed() -> bool { + if sys_key_pressed() == 1 { + true + } else { + false + } +} #[macro_export] macro_rules! vstore { diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 6d97b7e79..530ea2096 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -28,6 +28,7 @@ const SYSCALL_CONDVAR_WAIT: usize = 1032; const SYSCALL_FRAMEBUFFER: usize = 2000; const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -169,4 +170,8 @@ pub fn sys_framebuffer_flush() -> isize { pub fn sys_event_get() -> isize { syscall(SYSCALL_EVENT_GET, [0, 0, 0]) +} + +pub fn sys_key_pressed() -> isize { + syscall(SYSCALL_KEY_PRESSED, [0, 0, 0]) } \ No newline at end of file