feat(snake): 8 steps/s on fixed-period clock (draw time no longer adds to step)

This commit is contained in:
2026-10-27 22:14:35 +01:00
parent c8056ddac6
commit ec3bc0d2a1

View File

@@ -13,7 +13,9 @@ use libkos::syscall;
use server_framework::Encode;
use display_server::protocol::DisplayRequest;
const TICK_DELAY: u64 = 5_000_000;
/// Steps a second; QEMU virt's timebase is 10 MHz.
const STEPS_PER_SECOND: u64 = 8;
const TICK_DELAY: u64 = 10_000_000 / STEPS_PER_SECOND;
const MAX_RENDER_CMDS: usize = 140;
/// The display server, which the shell hands over because snake's manifest
@@ -53,6 +55,7 @@ fn main() {
};
// Takes focus; keys are polled every tick, the signal only wakes it.
let _ = libkos::display::subscribe(display_ep(), alarm, types::badge::Badge::new(1 << 1));
let mut clock = Clock::new(alarm);
let mut snake = Snake::new();
let mut commands = [RenderCommand { x: 0, y: 0, w: 0, h: 0, color: 0 }; MAX_RENDER_CMDS];
@@ -71,12 +74,12 @@ fn main() {
flush();
// Any key leaves.
while next_input() == Input::None {
wait_tick(alarm);
clock.wait_tick();
}
return;
}
wait_tick(alarm);
clock.wait_tick();
if handle_input(&mut snake) == Input::Quit {
return;
}
@@ -126,17 +129,31 @@ fn handle_input(snake: &mut Snake) -> Input {
}
}
/// Sleeps until the next tick on `alarm`, instead of spinning. Keys
/// signal it too (snake has one notification), so it waits again until
/// the tick is really due.
fn wait_tick(alarm: CapSlot) {
let deadline = syscall::get_time().raw() + TICK_DELAY;
if syscall::set_timer(alarm, Ticks::from_raw(deadline)).is_err() {
/// The game's clock: a step every `TICK_DELAY`, counted from the last
/// step's deadline, so the time drawing takes does not slow it down.
struct Clock {
alarm: CapSlot,
next: u64,
}
impl Clock {
fn new(alarm: CapSlot) -> Self {
Self { alarm, next: syscall::get_time().raw() }
}
/// Sleeps until the next step is due, instead of spinning. Keys
/// signal `alarm` too (snake has one notification), so it waits again
/// until the step really is due.
fn wait_tick(&mut self) {
// After a stall, carry on from now rather than rushing to catch up.
self.next = (self.next + TICK_DELAY).max(syscall::get_time().raw());
if syscall::set_timer(self.alarm, Ticks::from_raw(self.next)).is_err() {
return;
}
while syscall::get_time().raw() < deadline {
if syscall::wait(alarm).is_err() {
while syscall::get_time().raw() < self.next {
if syscall::wait(self.alarm).is_err() {
return;
}
}
}
}