gioser
⎇ e2272c0ed3c87ef53fd5ea5c9e6fd52d4790dcb8 renaser/apps/hello_wasm/src/lib.rs Rust · 140 siq'ikuna · 5.3 KB Ñawpa willay Pi imata llamkarqan ch'uya

feat: integra renaser (kernel SASOS bare-metal) al monorepo e2272c0ed · sergio ·

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}