Add snake gui app and update os/usr parts. Now snake can run!

This commit is contained in:
Yu Chen 2023-01-08 22:41:41 +08:00
parent b40120f8ff
commit 2cbd237260
14 changed files with 457 additions and 15 deletions

View file

@ -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"

View file

@ -12,7 +12,7 @@ pub type CharDeviceImpl = crate::drivers::chardev::NS16550a<VIRT_UART>;
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;

View file

@ -131,7 +131,7 @@ pub struct NS16550a<const BASE_ADDR: usize> {
impl<const BASE_ADDR: usize> NS16550a<BASE_ADDR> {
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<const BASE_ADDR: usize> NS16550a<BASE_ADDR> {
condvar: Condvar::new(),
}
}
pub fn read_buffer_is_empty(&self) -> bool {
self.inner.exclusive_session(|inner| inner.read_buffer.is_empty())
}
}
impl<const BASE_ADDR: usize> CharDevice for NS16550a<BASE_ADDR> {

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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),
}
}

View file

@ -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 = []
debug = true

403
user/src/bin/gui_snake.rs Normal file
View file

@ -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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
{
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<T: PixelColor, const MAX_SIZE: usize> {
parts: [Pixel<T>; MAX_SIZE],
len: usize,
direction: Direction,
size_x: u32,
size_y: u32,
}
struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> {
snake: &'a Snake<T, MAX_SIZE>,
index: usize,
}
impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake<T, MAX_SIZE> {
type Item = Pixel<T>;
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<T>;
fn next(&mut self) -> Option<Self::Item> {
let cur = self.snake.parts[self.index];
if self.index < self.snake.len {
self.index += 1;
return Some(cur);
}
None
}
}
impl<T: PixelColor, const MAX_SIZE: usize> Snake<T, MAX_SIZE> {
fn new(color: T, size_x: u32, size_y: u32) -> Snake<T, MAX_SIZE> {
Snake {
parts: [Pixel::<T>(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<T: PixelColor> {
size_x: u32,
size_y: u32,
place: Pixel<T>,
rng: oorandom::Rand32,
}
impl<T: PixelColor> Food<T> {
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<T, MAX_SIZE>) {
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::<T> {
0: p,
1: self.place.1,
}
}
fn get_pixel(&self) -> Pixel<T> {
self.place
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Direction {
Left,
Right,
Up,
Down,
None,
}
pub struct SnakeGame<const MAX_SNAKE_SIZE: usize, T: PixelColor> {
snake: Snake<T, MAX_SNAKE_SIZE>,
food: Food<T>,
food_age: u32,
food_lifetime: u32,
size_x: u32,
size_y: u32,
scale_x: u32,
scale_y: u32,
}
impl<const MAX_SIZE: usize, T: PixelColor> SnakeGame<MAX_SIZE, T> {
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::<T, MAX_SIZE>::new(snake_color, size_x / scale_x, size_y / scale_y);
let mut food = Food::<T>::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<D>(&mut self, target: &mut D) -> ()
where
D: DrawTarget<Color = T>,
{
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::<D> {
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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
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::<Rgb888, 20>::new(Rgb888::RED, 8, 8);
snake.set_direction(crate::Direction::Right);
assert_eq!(
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
snake.into_iter().next().unwrap()
);
snake.make_step();
assert_eq!(
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
snake.into_iter().nth(0).unwrap()
);
assert_eq!(
Pixel::<Rgb888>(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::<Rgb888>(Point { x: 1, y: 1 }, Rgb888::RED),
snake.into_iter().nth(0).unwrap()
);
assert_eq!(
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
snake.into_iter().nth(1).unwrap()
);
assert_eq!(
Pixel::<Rgb888>(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
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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])
}