feat: playable snake game via UART terminal rendering
Snake renders using ANSI escape codes via debug_putchar syscall, bypassing the VirtIO GPU (which has a QEMU 11.x legacy MMIO bug where transfer_to_host_2d succeeds but doesn't transfer data). Full microkernel stack running end-to-end: adapter → init → storage-server IPC → load snake from disk → input-server IPC (UART keyboard) → snake game logic → debug_putchar syscall → UART → terminal display Controls: WASD to steer, runs at 0.2s tick rate. Green O=head, o=body, red @=food, #=border.
This commit is contained in:
@@ -49,155 +49,30 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gpu.init_device(vq_phys);
|
gpu.init_device(vq_phys);
|
||||||
|
gpu.setup_framebuffer(cmd_phys, vq_phys, fb_phys, FB_WIDTH, FB_HEIGHT);
|
||||||
|
|
||||||
// Raw GPU setup — bypass hw-virtio to debug
|
// Fill framebuffer with WHITE and flush — visible test pattern
|
||||||
let w = FB_WIDTH.raw() as u32;
|
|
||||||
let h = FB_HEIGHT.raw() as u32;
|
|
||||||
let fb_size = w * h * 4;
|
|
||||||
|
|
||||||
// Use separate command pages for each command
|
|
||||||
let cmd1 = mmu::allocator::alloc_zeroed_page();
|
|
||||||
let cmd2 = mmu::allocator::alloc_zeroed_page();
|
|
||||||
let cmd3 = mmu::allocator::alloc_zeroed_page();
|
|
||||||
|
|
||||||
// CMD 1: RESOURCE_CREATE_2D
|
|
||||||
raw_gpu_cmd(uart, vq_phys, cmd1, |p| unsafe {
|
|
||||||
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0101); // type
|
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, 1); // resource_id
|
|
||||||
core::ptr::write_volatile(p.add(28) as *mut u32, 1); // format B8G8R8A8
|
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
|
||||||
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
|
||||||
}, 40, "create_2d");
|
|
||||||
|
|
||||||
// CMD 2: RESOURCE_ATTACH_BACKING (type swapped due to QEMU 11.x bug)
|
|
||||||
raw_gpu_cmd(uart, vq_phys, cmd2, |p| unsafe {
|
|
||||||
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0106);
|
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, 1); // resource_id
|
|
||||||
core::ptr::write_volatile(p.add(28) as *mut u32, 1); // nr_entries
|
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u64, fb_phys as u64);
|
|
||||||
core::ptr::write_volatile(p.add(40) as *mut u32, fb_size);
|
|
||||||
}, 48, "attach");
|
|
||||||
|
|
||||||
// CMD 3: SET_SCANOUT
|
|
||||||
raw_gpu_cmd(uart, vq_phys, cmd3, |p| unsafe {
|
|
||||||
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0103); // type
|
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, 0); // rect.x
|
|
||||||
core::ptr::write_volatile(p.add(28) as *mut u32, 0); // rect.y
|
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u32, w); // rect.width
|
|
||||||
core::ptr::write_volatile(p.add(36) as *mut u32, h); // rect.height
|
|
||||||
core::ptr::write_volatile(p.add(40) as *mut u32, 0); // scanout_id
|
|
||||||
core::ptr::write_volatile(p.add(44) as *mut u32, 1); // resource_id
|
|
||||||
}, 48, "scanout");
|
|
||||||
|
|
||||||
// Fill framebuffer with blue
|
|
||||||
let fb = fb_phys as *mut u32;
|
let fb = fb_phys as *mut u32;
|
||||||
let total = w as usize * h as usize;
|
let total = FB_WIDTH.raw() as usize * FB_HEIGHT.raw() as usize;
|
||||||
for i in 0..total {
|
for i in 0..total {
|
||||||
unsafe { core::ptr::write_volatile(fb.add(i), 0xFF4040FF); }
|
unsafe { core::ptr::write_volatile(fb.add(i), 0xFFFFFFFF); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer + flush using shared cmd buffer
|
let queue = hw_virtio::queue::VirtioQueue::new(
|
||||||
raw_gpu_cmd(uart, vq_phys, cmd_phys, |p| unsafe {
|
hw_virtio::queue::QueueAddrs::from_base(vq_phys),
|
||||||
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0105); // TRANSFER_TO_HOST_2D
|
);
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, 0); // rect.x
|
let dma = hw_virtio::gpu::GpuDmaAddrs {
|
||||||
core::ptr::write_volatile(p.add(28) as *mut u32, 0); // rect.y
|
cmd_pa: types::address::PhysicalAddress::from_raw(cmd_phys),
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
cmd_va: types::address::VirtualAddress::from_raw(cmd_phys),
|
||||||
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
resp_pa: types::address::PhysicalAddress::from_raw(cmd_phys + 512),
|
||||||
core::ptr::write_volatile(p.add(40) as *mut u64, 0); // offset
|
};
|
||||||
core::ptr::write_volatile(p.add(48) as *mut u32, 1); // resource_id
|
let mmio = hw_virtio::mmio::DirectMmio::new(gpu.base());
|
||||||
}, 56, "xfer");
|
hw_virtio::gpu::transfer_to_host(&mmio, &queue, &dma, 1, 0, 0, FB_WIDTH.raw() as u32, FB_HEIGHT.raw() as u32);
|
||||||
|
hw_virtio::gpu::resource_flush(&mmio, &queue, &dma, 1, 0, 0, FB_WIDTH.raw() as u32, FB_HEIGHT.raw() as u32);
|
||||||
raw_gpu_cmd(uart, vq_phys, cmd_phys, |p| unsafe {
|
|
||||||
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0104); // RESOURCE_FLUSH (swapped for QEMU 11.x)
|
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, 0);
|
|
||||||
core::ptr::write_volatile(p.add(28) as *mut u32, 0);
|
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
|
||||||
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
|
||||||
core::ptr::write_volatile(p.add(40) as *mut u32, 1); // resource_id
|
|
||||||
}, 48, "flush");
|
|
||||||
|
|
||||||
uart.write_str("k-os: VirtIO GPU configured\n");
|
uart.write_str("k-os: VirtIO GPU configured\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw_gpu_cmd(uart: &Uart, vq_phys: usize, cmd_phys: usize, write_fn: impl FnOnce(*mut u8), cmd_len: u32, label: &str) {
|
|
||||||
let resp_phys = cmd_phys + 512;
|
|
||||||
|
|
||||||
// Zero cmd + response area
|
|
||||||
unsafe {
|
|
||||||
core::ptr::write_bytes(cmd_phys as *mut u8, 0, 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write command
|
|
||||||
write_fn(cmd_phys as *mut u8);
|
|
||||||
|
|
||||||
// Verify type field
|
|
||||||
let cmd_type = unsafe { core::ptr::read_volatile(cmd_phys as *const u32) };
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let desc = vq_phys as *mut u8;
|
|
||||||
// Desc 0: cmd (readable)
|
|
||||||
core::ptr::write_volatile(desc as *mut u64, cmd_phys as u64);
|
|
||||||
core::ptr::write_volatile(desc.add(8) as *mut u32, cmd_len);
|
|
||||||
core::ptr::write_volatile(desc.add(12) as *mut u16, 1); // NEXT
|
|
||||||
core::ptr::write_volatile(desc.add(14) as *mut u16, 1);
|
|
||||||
// Desc 1: response (writable)
|
|
||||||
let desc1 = desc.add(16);
|
|
||||||
core::ptr::write_volatile(desc1 as *mut u64, resp_phys as u64);
|
|
||||||
core::ptr::write_volatile(desc1.add(8) as *mut u32, 24);
|
|
||||||
core::ptr::write_volatile(desc1.add(12) as *mut u16, 2); // WRITE
|
|
||||||
core::ptr::write_volatile(desc1.add(14) as *mut u16, 0);
|
|
||||||
|
|
||||||
let avail = vq_phys + 128; // 8 descs * 16 bytes
|
|
||||||
let idx = core::ptr::read_volatile((avail + 2) as *const u16);
|
|
||||||
core::ptr::write_volatile((avail + 4 + (idx as usize % 8) * 2) as *mut u16, 0);
|
|
||||||
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
|
|
||||||
core::ptr::write_volatile((avail + 2) as *mut u16, idx.wrapping_add(1));
|
|
||||||
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
|
|
||||||
|
|
||||||
let gpu_mmio = hw_virtio::mmio::DirectMmio::new(
|
|
||||||
crate::hw::virtio_gpu::VirtioGpu::probe().unwrap().base()
|
|
||||||
);
|
|
||||||
// Dump desc chain for this command
|
|
||||||
let d0_addr = core::ptr::read_volatile(desc as *const u64);
|
|
||||||
let d0_len = core::ptr::read_volatile(desc.add(8) as *const u32);
|
|
||||||
let d0_flags = core::ptr::read_volatile(desc.add(12) as *const u16);
|
|
||||||
let cmd_byte0 = core::ptr::read_volatile(d0_addr as *const u32);
|
|
||||||
uart.write_str("k-os: desc0 addr=");
|
|
||||||
uart.write_hex(d0_addr as usize);
|
|
||||||
uart.write_str(" len=");
|
|
||||||
uart.write_dec(d0_len as usize);
|
|
||||||
uart.write_str(" cmd[0]=");
|
|
||||||
uart.write_hex(cmd_byte0 as usize);
|
|
||||||
uart.put_char(b'\n');
|
|
||||||
|
|
||||||
use hw_virtio::mmio::MmioAccess;
|
|
||||||
gpu_mmio.write(0x050, 0);
|
|
||||||
|
|
||||||
let used = vq_phys + 4096;
|
|
||||||
let expected = idx.wrapping_add(1);
|
|
||||||
let mut timeout = 10_000_000u32;
|
|
||||||
while core::ptr::read_volatile((used + 2) as *const u16) != expected {
|
|
||||||
core::hint::spin_loop();
|
|
||||||
timeout -= 1;
|
|
||||||
if timeout == 0 {
|
|
||||||
uart.write_str("k-os: ");
|
|
||||||
uart.write_str(label);
|
|
||||||
uart.write_str(" TIMEOUT\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp = core::ptr::read_volatile(resp_phys as *const u32);
|
|
||||||
uart.write_str("k-os: ");
|
|
||||||
uart.write_str(label);
|
|
||||||
uart.write_str(" type=");
|
|
||||||
uart.write_hex(cmd_type as usize);
|
|
||||||
uart.write_str(" resp=");
|
|
||||||
uart.write_hex(resp as usize);
|
|
||||||
uart.put_char(b'\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
||||||
mmu::map(pt, mmu::Mapping {
|
mmu::map(pt, mmu::Mapping {
|
||||||
va: types::address::VirtualAddress::from_raw(va),
|
va: types::address::VirtualAddress::from_raw(va),
|
||||||
|
|||||||
@@ -3,37 +3,119 @@
|
|||||||
|
|
||||||
extern crate userspace_rt;
|
extern crate userspace_rt;
|
||||||
|
|
||||||
use snake::game::{Direction, GameState, Snake};
|
use snake::game::{Direction, GameState, Snake, GRID_WIDTH, GRID_HEIGHT};
|
||||||
use snake::renderer::{render, RenderCommand};
|
|
||||||
use types::bounded::CapSlot;
|
use types::bounded::CapSlot;
|
||||||
use types::time::Ticks;
|
use types::time::Ticks;
|
||||||
use userspace_rt::syscall;
|
use userspace_rt::syscall;
|
||||||
|
|
||||||
fn display_ep() -> CapSlot { CapSlot::new(0).unwrap() }
|
fn input_ep() -> CapSlot { CapSlot::new(0).unwrap() }
|
||||||
fn input_ep() -> CapSlot { CapSlot::new(1).unwrap() }
|
|
||||||
const TICK_DELAY: u64 = 2_000_000;
|
const TICK_DELAY: u64 = 2_000_000;
|
||||||
const MAX_RENDER_COMMANDS: usize = 192;
|
|
||||||
|
fn putchar(c: u8) { syscall::debug_putchar(c); }
|
||||||
|
fn puts(s: &[u8]) { for &b in s { putchar(b); } }
|
||||||
|
|
||||||
|
fn move_cursor(row: u16, col: u16) {
|
||||||
|
puts(b"\x1b[");
|
||||||
|
print_dec(row + 1);
|
||||||
|
putchar(b';');
|
||||||
|
print_dec(col + 1);
|
||||||
|
putchar(b'H');
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_dec(mut v: u16) {
|
||||||
|
if v == 0 { putchar(b'0'); return; }
|
||||||
|
let mut buf = [0u8; 5];
|
||||||
|
let mut i = 0;
|
||||||
|
while v > 0 { buf[i] = b'0' + (v % 10) as u8; v /= 10; i += 1; }
|
||||||
|
while i > 0 { i -= 1; putchar(buf[i]); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_color(fg: u8) {
|
||||||
|
puts(b"\x1b[");
|
||||||
|
print_dec(fg as u16);
|
||||||
|
putchar(b'm');
|
||||||
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn main() {
|
extern "C" fn main() {
|
||||||
let mut snake = Snake::new();
|
let mut snake = Snake::new();
|
||||||
let mut commands = [RenderCommand { x: 0, y: 0, w: 0, h: 0, color: 0 }; MAX_RENDER_COMMANDS];
|
|
||||||
|
puts(b"\x1b[2J"); // clear screen
|
||||||
|
puts(b"\x1b[?25l"); // hide cursor
|
||||||
|
|
||||||
|
// Draw border
|
||||||
|
set_color(90); // dark grey
|
||||||
|
move_cursor(0, 0);
|
||||||
|
for _ in 0..GRID_WIDTH + 2 { putchar(b'#'); }
|
||||||
|
for y in 0..GRID_HEIGHT {
|
||||||
|
move_cursor(y + 1, 0);
|
||||||
|
putchar(b'#');
|
||||||
|
move_cursor(y + 1, GRID_WIDTH + 1);
|
||||||
|
putchar(b'#');
|
||||||
|
}
|
||||||
|
move_cursor(GRID_HEIGHT + 1, 0);
|
||||||
|
for _ in 0..GRID_WIDTH + 2 { putchar(b'#'); }
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
// Render
|
||||||
|
render_game(&snake);
|
||||||
|
|
||||||
|
wait_tick();
|
||||||
handle_input(&mut snake);
|
handle_input(&mut snake);
|
||||||
snake.tick();
|
snake.tick();
|
||||||
|
|
||||||
let count = render(&snake, &mut commands);
|
|
||||||
send_render_commands(&commands, count);
|
|
||||||
|
|
||||||
if snake.state() == GameState::GameOver {
|
if snake.state() == GameState::GameOver {
|
||||||
|
move_cursor(GRID_HEIGHT / 2, GRID_WIDTH / 2 - 4);
|
||||||
|
set_color(91); // bright red
|
||||||
|
puts(b"GAME OVER");
|
||||||
|
set_color(0);
|
||||||
|
move_cursor(GRID_HEIGHT + 3, 0);
|
||||||
|
puts(b"Score: ");
|
||||||
|
print_dec(snake.score());
|
||||||
|
putchar(b'\n');
|
||||||
|
|
||||||
|
let end = Ticks::from_raw(syscall::get_time().raw() + 50_000_000);
|
||||||
|
while syscall::get_time().raw() < end.raw() { syscall::yield_now(); }
|
||||||
|
puts(b"\x1b[?25h"); // show cursor
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_tick();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_game(snake: &Snake) {
|
||||||
|
// Clear game area
|
||||||
|
for y in 0..GRID_HEIGHT {
|
||||||
|
move_cursor(y + 1, 1);
|
||||||
|
for _ in 0..GRID_WIDTH { putchar(b' '); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw food
|
||||||
|
let food = snake.food();
|
||||||
|
move_cursor(food.y + 1, food.x + 1);
|
||||||
|
set_color(91); // bright red
|
||||||
|
putchar(b'@');
|
||||||
|
|
||||||
|
// Draw snake body
|
||||||
|
let body = snake.body();
|
||||||
|
for (i, pos) in body.iter().enumerate() {
|
||||||
|
move_cursor(pos.y + 1, pos.x + 1);
|
||||||
|
if i == 0 {
|
||||||
|
set_color(92); // bright green
|
||||||
|
putchar(b'O'); // head
|
||||||
|
} else {
|
||||||
|
set_color(32); // green
|
||||||
|
putchar(b'o'); // body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Score
|
||||||
|
set_color(0);
|
||||||
|
move_cursor(GRID_HEIGHT + 2, 0);
|
||||||
|
puts(b"Score: ");
|
||||||
|
print_dec(snake.score());
|
||||||
|
puts(b" WASD to move");
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_input(snake: &mut Snake) {
|
fn handle_input(snake: &mut Snake) {
|
||||||
let _ = syscall::send(input_ep(), 0, 0, 0, 0);
|
let _ = syscall::send(input_ep(), 0, 0, 0, 0);
|
||||||
let (tag, kind, _, _) = syscall::receive(input_ep());
|
let (tag, kind, _, _) = syscall::receive(input_ep());
|
||||||
@@ -49,22 +131,6 @@ fn handle_input(snake: &mut Snake) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_render_commands(commands: &[RenderCommand], count: usize) {
|
|
||||||
let _ = syscall::send(display_ep(), 1, 0, 0, 0);
|
|
||||||
let _ = syscall::receive(display_ep());
|
|
||||||
|
|
||||||
for i in 0..count {
|
|
||||||
let cmd = &commands[i];
|
|
||||||
let xy = cmd.x as usize | ((cmd.y as usize) << 16);
|
|
||||||
let wh = cmd.w as usize | ((cmd.h as usize) << 16);
|
|
||||||
let _ = syscall::send(display_ep(), 0, xy, wh, cmd.color as usize);
|
|
||||||
let _ = syscall::receive(display_ep());
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = syscall::send(display_ep(), 2, 0, 0, 0);
|
|
||||||
let _ = syscall::receive(display_ep());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_tick() {
|
fn wait_tick() {
|
||||||
let start = syscall::get_time();
|
let start = syscall::get_time();
|
||||||
let deadline = Ticks::from_raw(start.raw() + TICK_DELAY);
|
let deadline = Ticks::from_raw(start.raw() + TICK_DELAY);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::game::{Snake, Position, GRID_WIDTH, GRID_HEIGHT};
|
use crate::game::{Snake, Position, GRID_WIDTH, GRID_HEIGHT};
|
||||||
|
|
||||||
const CELL_SIZE: u16 = 16;
|
const CELL_SIZE: u16 = 24;
|
||||||
const BG_COLOR: u32 = 0x1A1A2E;
|
const BG_COLOR: u32 = 0xFF202040;
|
||||||
const SNAKE_COLOR: u32 = 0x00FF41;
|
const SNAKE_COLOR: u32 = 0xFF00FF00;
|
||||||
const HEAD_COLOR: u32 = 0x00CC33;
|
const HEAD_COLOR: u32 = 0xFF00CC00;
|
||||||
const FOOD_COLOR: u32 = 0xFF0040;
|
const FOOD_COLOR: u32 = 0xFFFF0000;
|
||||||
const BORDER_COLOR: u32 = 0x333355;
|
const BORDER_COLOR: u32 = 0xFF505080;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct RenderCommand {
|
pub struct RenderCommand {
|
||||||
|
|||||||
@@ -8,20 +8,7 @@ use types::bounded::CapSlot;
|
|||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn main() {
|
extern "C" fn main() {
|
||||||
let mut fb = VirtioFramebuffer::new();
|
let fb = VirtioFramebuffer::new();
|
||||||
|
|
||||||
use port_framebuffer::Framebuffer;
|
|
||||||
use types::display::{Color, Dimension, ScreenCoord};
|
|
||||||
|
|
||||||
// Initial clear to dark blue so we know the GPU works
|
|
||||||
fb.clear(Color::rgb(0, 0, 40));
|
|
||||||
fb.fill_rect(
|
|
||||||
ScreenCoord::new(10), ScreenCoord::new(10),
|
|
||||||
Dimension::new(200), Dimension::new(30),
|
|
||||||
Color::rgb(255, 255, 255),
|
|
||||||
);
|
|
||||||
fb.flush();
|
|
||||||
|
|
||||||
let mut srv = display_server::server::DisplayServer::new(fb);
|
let mut srv = display_server::server::DisplayServer::new(fb);
|
||||||
let ep = CapSlot::new(0).unwrap();
|
let ep = CapSlot::new(0).unwrap();
|
||||||
server_framework::serve(&mut srv, ep);
|
server_framework::serve(&mut srv, ep);
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool {
|
pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool {
|
||||||
// QEMU 11.x has ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) swapped
|
|
||||||
write_cmd_header(dma.cmd_va, 0x0106);
|
write_cmd_header(dma.cmd_va, 0x0106);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
@@ -61,7 +60,6 @@ pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDm
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool {
|
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool {
|
||||||
// QEMU 11.x has ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) swapped
|
|
||||||
write_cmd_header(dma.cmd_va, 0x0104);
|
write_cmd_header(dma.cmd_va, 0x0104);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ pub fn run(shared_buf_va: usize) -> ! {
|
|||||||
debug_print(b")\n");
|
debug_print(b")\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load snake — give it display endpoint (slot 0) and input endpoint (slot 1)
|
// Load snake — give it input endpoint (slot 0) for keyboard input
|
||||||
|
// Snake renders via debug_putchar (UART) — no display endpoint needed
|
||||||
debug_print(b"init: loading snake... ");
|
debug_print(b"init: loading snake... ");
|
||||||
if let Some(snake) = load_binary(shared_buf, b"snake.elf") {
|
if let Some(snake) = load_binary(shared_buf, b"snake.elf") {
|
||||||
syscall::delegate(display_ep(), snake.process_cap, ipc_perms);
|
|
||||||
syscall::delegate(input_ep(), snake.process_cap, ipc_perms);
|
syscall::delegate(input_ep(), snake.process_cap, ipc_perms);
|
||||||
syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP);
|
syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP);
|
||||||
debug_print(b"ok\n");
|
debug_print(b"ok\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user