⎇ e2272c0ed3c87ef53fd5ea5c9e6fd52d4790dcb8
renaser/apps/hello_wasm/src/lib.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
feat: integra renaser (kernel SASOS bare-metal) al monorepo
1// =============================================================================
2// renaser :: apps/hello_wasm — Fase 4/5 :: el primer ciudadano del userspace
3// -----------------------------------------------------------------------------
4// Esta aplicacion vive DENTRO de su propia memoria lineal de WebAssembly. No
5// conoce la MMU, no conoce los anillos de privilegio de la CPU: su unica via
6// hacia el mundo son las dos capacidades que el kernel le inyecta. Lo que no
7// este importado, sencillamente, no tiene camino fisico que recorrer.
8//
9// FASE 5 :: el ABI deja de ser un `run()` que se queda dentro para siempre.
10// Ahora la app exporta `init()` —preparacion, una sola vez— y `tick()` —un
11// fotograma de trabajo, y RETORNA—. Ese retorno es el punto de cesion
12// cooperativa: el kernel recupera el control y atiende a las demas apps.
13// =============================================================================
14
15#![no_std]
16
17// --- Las dos UNICAS capacidades que el kernel `renaser` expone al modulo. ---
18#[link(wasm_import_module = "renaser")]
19extern "C" {
20 /// Compone un bufer de pixeles (de ESTA memoria lineal) en la region que el
21 /// kernel asigno a esta aplicacion.
22 fn sys_render_frame(ptr: u32, len: u32);
23 /// Devuelve el ultimo scancode crudo del teclado, o 0 si no hay ninguno.
24 fn sys_get_scancode() -> u32;
25}
26
27/// Sin sistema operativo bajo nosotros, un panico solo puede detenerse en seco.
28#[panic_handler]
29fn al_fallar(_: &core::panic::PanicInfo) -> ! {
30 loop {}
31}
32
33// --- Geometria de la escena. El ancho y el alto DEBEN coincidir con la region
34// que el kernel asigna a esta app: el host rechaza cualquier fotograma de
35// un tamaño que no sea, exactamente, el de su ventana. ---
36const ANCHO: usize = 480;
37const ALTO: usize = 560;
38const LADO: usize = 96;
39const PASO: i32 = 24;
40
41/// Azul nocturno: el fondo del lienzo de la aplicacion.
42const FONDO: u32 = 0x0A_18_30;
43/// Ambar: el cuadrado que el usuario gobierna.
44const CUADRO: u32 = 0xFF_B0_00;
45
46/// El lienzo de la aplicacion, en SU propia memoria lineal. El kernel jamas lo
47/// ve directamente: solo recibe el (ptr, len) que cada fotograma le entrega.
48static mut LIENZO: [u32; ANCHO * ALTO] = [0; ANCHO * ALTO];
49
50/// Posicion del cuadrado. Vive entre fotogramas en la memoria lineal del modulo
51/// — el estado persiste porque la instancia, en la Fase 5, ya no es efimera.
52static mut POS_X: i32 = 0;
53static mut POS_Y: i32 = 0;
54
55/// Preparacion: el kernel la invoca UNA sola vez, al cargar el modulo. Pinta el
56/// fondo, centra el cuadrado y vuelca el primer fotograma.
57#[no_mangle]
58pub extern "C" fn init() {
59 // SEGURIDAD: durante `init` y `tick` esta es la unica via de acceso a
60 // LIENZO, y el kernel jamas reentra el modulo mientras una de ellas corre.
61 let lienzo: &mut [u32] = unsafe { &mut *core::ptr::addr_of_mut!(LIENZO) };
62 for pixel in lienzo.iter_mut() {
63 *pixel = FONDO;
64 }
65
66 let x = (ANCHO / 2 - LADO / 2) as i32;
67 let y = (ALTO / 2 - LADO / 2) as i32;
68 rellenar(lienzo, x, y, CUADRO);
69 // SEGURIDAD: escritura de escalares `Copy`; no se crea referencia alguna.
70 unsafe {
71 POS_X = x;
72 POS_Y = y;
73 }
74
75 volcar(lienzo);
76}
77
78/// Un fotograma de trabajo: escucha el teclado, mueve el cuadrado, vuelca la
79/// imagen y RETORNA. El retorno cede la CPU al kernel y a las apps vecinas.
80#[no_mangle]
81pub extern "C" fn tick() {
82 let lienzo: &mut [u32] = unsafe { &mut *core::ptr::addr_of_mut!(LIENZO) };
83 let (mut x, mut y) = unsafe { (POS_X, POS_Y) };
84
85 // 1. Escuchar al teclado a traves de la capacidad del host.
86 let (dx, dy) = match unsafe { sys_get_scancode() } {
87 0x11 => (0, -PASO), // tecla W -> arriba
88 0x1F => (0, PASO), // tecla S -> abajo
89 0x1E => (-PASO, 0), // tecla A -> izquierda
90 0x20 => (PASO, 0), // tecla D -> derecha
91 _ => (0, 0),
92 };
93
94 // 2. Borrar el cuadrado anterior repintando su hueco con el fondo.
95 rellenar(lienzo, x, y, FONDO);
96
97 // 3. Moverlo, manteniendolo siempre dentro del lienzo.
98 x = (x + dx).clamp(0, (ANCHO - LADO) as i32);
99 y = (y + dy).clamp(0, (ALTO - LADO) as i32);
100
101 // 4. Dibujar el cuadrado en su nueva posicion y guardar el estado.
102 rellenar(lienzo, x, y, CUADRO);
103 unsafe {
104 POS_X = x;
105 POS_Y = y;
106 }
107
108 // 5. Volcar el fotograma: el host lo compondra dentro de nuestra region.
109 volcar(lienzo);
110}
111
112/// Entrega el lienzo completo al kernel. El (ptr, len) apunta SIEMPRE dentro de
113/// nuestra memoria lineal, y su tamaño es, exactamente, el de la region.
114fn volcar(lienzo: &[u32]) {
115 // SEGURIDAD: `sys_render_frame` es una capacidad del host; el (ptr, len)
116 // describe nuestra propia memoria lineal y el host lo verifica sin piedad.
117 unsafe {
118 sys_render_frame(lienzo.as_ptr() as u32, (ANCHO * ALTO * 4) as u32);
119 }
120}
121
122/// Rellena un cuadrado de lado `LADO`, con su esquina en (x, y), recortado con
123/// firmeza a los limites del lienzo.
124fn rellenar(lienzo: &mut [u32], x: i32, y: i32, color: u32) {
125 let x0 = x.max(0) as usize;
126 let y0 = y.max(0) as usize;
127 let x1 = (x0 + LADO).min(ANCHO);
128 let y1 = (y0 + LADO).min(ALTO);
129
130 let mut fila = y0;
131 while fila < y1 {
132 let base = fila * ANCHO;
133 let mut col = x0;
134 while col < x1 {
135 lienzo[base + col] = color;
136 col += 1;
137 }
138 fila += 1;
139 }
140}