feat(userspace-rt): add runtime crate with entry point, panic handler, and allocator re-export

Snake and hello apps now use extern crate userspace_rt — no more boilerplate assembly or panic handlers in app code.
This commit is contained in:
2026-10-18 04:04:52 +02:00
parent e092fa4ae5
commit 29434805a6
9 changed files with 58 additions and 70 deletions

View File

@@ -1,48 +1,31 @@
#![no_std]
#![no_main]
extern crate userspace_rt;
use snake::game::{Direction, GameState, Snake};
use snake::renderer::{render, RenderCommand};
use userspace_rt::syscall;
const DISPLAY_EP: usize = 0;
const INPUT_EP: usize = 1;
const TICK_DELAY: usize = 2_000_000;
const MAX_RENDER_COMMANDS: usize = 192;
#[cfg(not(test))]
use core::arch::global_asm;
#[cfg(not(test))]
global_asm!(
r#"
.section .text.entry
.globl _start
_start:
call main
j _start
"#
);
#[cfg(not(test))]
#[unsafe(no_mangle)]
extern "C" fn main() -> ! {
run_game();
}
fn run_game() -> ! {
extern "C" fn main() {
let mut snake = Snake::new();
let mut commands = [RenderCommand { x: 0, y: 0, w: 0, h: 0, color: 0 }; MAX_RENDER_COMMANDS];
loop {
handle_input(&mut snake);
snake.tick();
let count = render(&snake, &mut commands);
send_render_commands(&commands, count);
if snake.state() == GameState::GameOver {
userspace::syscall::exit();
return;
}
wait_tick();
@@ -50,11 +33,9 @@ fn run_game() -> ! {
}
fn handle_input(snake: &mut Snake) {
// PollEvent request: tag=0
let _ = userspace::syscall::send(INPUT_EP, 0, 0, 0, 0);
let (tag, kind, _data, _) = userspace::syscall::receive(INPUT_EP);
let _ = syscall::send(INPUT_EP, 0, 0, 0, 0);
let (tag, kind, _, _) = syscall::receive(INPUT_EP);
// InputResponse::Event(KeyDown(key)): tag=1, key_kind in word 1
if tag == 1 {
match kind {
0 => snake.set_direction(Direction::Up),
@@ -67,34 +48,25 @@ fn handle_input(snake: &mut Snake) {
}
fn send_render_commands(commands: &[RenderCommand], count: usize) {
// Clear: DisplayRequest tag=1
let _ = userspace::syscall::send(DISPLAY_EP, 1, 0, 0, 0);
let _ = userspace::syscall::receive(DISPLAY_EP);
let _ = syscall::send(DISPLAY_EP, 1, 0, 0, 0);
let _ = syscall::receive(DISPLAY_EP);
for i in 0..count {
let cmd = &commands[i];
// DrawRect: DisplayRequest tag=0, xy packed, wh packed, color
let xy = cmd.x as usize | ((cmd.y as usize) << 16);
let wh = cmd.w as usize | ((cmd.h as usize) << 16);
let _ = userspace::syscall::send(DISPLAY_EP, 0, xy, wh, cmd.color as usize);
let _ = userspace::syscall::receive(DISPLAY_EP);
let _ = syscall::send(DISPLAY_EP, 0, xy, wh, cmd.color as usize);
let _ = syscall::receive(DISPLAY_EP);
}
// Flush: DisplayRequest tag=2
let _ = userspace::syscall::send(DISPLAY_EP, 2, 0, 0, 0);
let _ = userspace::syscall::receive(DISPLAY_EP);
let _ = syscall::send(DISPLAY_EP, 2, 0, 0, 0);
let _ = syscall::receive(DISPLAY_EP);
}
fn wait_tick() {
let start = userspace::syscall::get_time();
let start = syscall::get_time();
let deadline = start + TICK_DELAY;
while userspace::syscall::get_time() < deadline {
userspace::syscall::yield_now();
while syscall::get_time() < deadline {
syscall::yield_now();
}
}
#[cfg(not(test))]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
userspace::syscall::exit();
}