feat(renaser): Fase 17 — bitácora, el editor que recuerda
memoriosa (Fase 7c) demostró que un app podía persistir su huella. Esta fase la lleva al gesto natural: un editor de texto. Tecleas, reinicias renaser, el texto sigue ahí. La huella vive en el grafo de objetos como todo lo demás. - Nuevo crate `apps/bitacora/`: lienzo 480×280, tipografía 8×8 embebida (`font8x8 = "0.3"`) escalada x2 a 16×16, render pixel a pixel desde la memoria del propio app. Buffer 512 bytes con wrap automático a 28 columnas; `Enter` salta línea, Backspace borra; al desbordar el buffer se descartan los 64 primeros para amortizar la mudanza. Cada cambio invoca `sys_estado_guardar`; al arrancar, `init` llama a `sys_estado_cargar` y reconstruye. - Mapeo de scancodes US a ASCII (letras, dígitos, puntuación básica, espacio). Sin shift ni mayúsculas — minimalismo. - `GENESIS` crece de 7 a 8 apps; `bitacora` es la PRIMERA — gana la celda maestra al arrancar y te invita a teclear. - `CELDA_TASKBAR_ANCHO` baja de 150 a 130 px para que las ocho pestañas + lanzador + reloj quepan holgadas en 1280 px. Verificado en QEMU: tras escribir "hola renaser" y reiniciar el kernel con el mismo disk.img, bitácora muestra el texto donde lo dejó. El `almacen` reporta 24 objetos en el grafo (frente a 9 antes de escribir) y `raiz presente` — cada `guardar` anexó una versión al log direccionado por contenido. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1085,3 +1085,37 @@ a la derecha, y le pone el reloj a latir cada segundo.
|
|||||||
reloj `0:17` a la derecha (el tiempo que el kernel lleva vivo al capturar).
|
reloj `0:17` a la derecha (el tiempo que el kernel lleva vivo al capturar).
|
||||||
Diez segundos después, el reloj marca `0:29` — la barra se ha refrescado
|
Diez segundos después, el reloj marca `0:29` — la barra se ha refrescado
|
||||||
doce veces sin intervención del ratón ni del teclado.
|
doce veces sin intervención del ratón ni del teclado.
|
||||||
|
|
||||||
|
## Fase 17 — `bitacora` :: el editor que recuerda — 2026-05-23
|
||||||
|
|
||||||
|
`memoriosa` (Fase 7c) demostró que un app podía persistir un contador a
|
||||||
|
través de los reinicios. `bitacora` extiende esa demostración a un EDITOR de
|
||||||
|
texto: tecleas, los caracteres aparecen; reinicias el kernel, el texto
|
||||||
|
sigue ahí. La huella vive en el grafo de objetos, como todo lo demás.
|
||||||
|
|
||||||
|
### Añadido — app `bitacora` (`apps/bitacora/`)
|
||||||
|
- **Lienzo 480×280** con título "bitacora :: el texto persiste" en índigo
|
||||||
|
arriba y, debajo, las últimas líneas del buffer. Wrap automático a 28
|
||||||
|
columnas; `Enter` salta de línea; Backspace borra el último carácter.
|
||||||
|
- **Tipografía 8×8** embebida (crate `font8x8 = "0.3"`), escalada x2 a 16×16.
|
||||||
|
El app rasteriza cada glifo pixel a pixel desde su propia memoria lineal.
|
||||||
|
- **Persistencia automática**: cada cambio invoca `sys_estado_guardar`. Al
|
||||||
|
arrancar, `init` llama a `sys_estado_cargar` y restaura el buffer.
|
||||||
|
- Buffer de 512 bytes; al desbordarse descarta los 64 primeros para hacer
|
||||||
|
hueco (amortiza el coste — no es una mudanza por cada pulsación).
|
||||||
|
- Mapeo de scancodes US a ASCII para letras, dígitos y puntuación común;
|
||||||
|
Enter genera `\n`, Backspace borra. Sin mayúsculas ni modificadores.
|
||||||
|
|
||||||
|
### Cambiado
|
||||||
|
- **`GENESIS` crece de 7 a 8 apps** con `bitacora` como la PRIMERA — gana
|
||||||
|
la celda maestra al arrancar, así que la primera ventana grande del
|
||||||
|
escritorio te invita a teclear.
|
||||||
|
- **`CELDA_TASKBAR_ANCHO`** baja de 150 a 130 píxeles para que las ocho
|
||||||
|
pestañas + el lanzador + el reloj quepan holgadas en 1280 px.
|
||||||
|
|
||||||
|
### Verificado
|
||||||
|
- QEMU (`sendkey` del monitor): tras escribir `hola renaser` y `quit` →
|
||||||
|
relanzar QEMU con el mismo `disk.img`, la `bitacora` muestra de nuevo el
|
||||||
|
texto justo donde quedó. El `almacen` reporta 24 objetos en el grafo
|
||||||
|
(frente a 9 antes de escribir) y `raiz presente`: cada `guardar` anexó
|
||||||
|
una versión al log direccionado por contenido.
|
||||||
|
|||||||
+5
-3
@@ -28,7 +28,7 @@ Reconstruir una app WASM del userspace tras tocarla. Los `.wasm` viven en
|
|||||||
modulo `hello_wasm` se copia como `app.wasm`, el resto conserva su nombre:
|
modulo `hello_wasm` se copia como `app.wasm`, el resto conserva su nombre:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd apps/<app> # hello_wasm, discola, glotona, cronista, memoriosa, pulso, tonada
|
cd apps/<app> # hello_wasm, discola, glotona, cronista, memoriosa, pulso, tonada, bitacora
|
||||||
cargo build --target wasm32-unknown-unknown --release
|
cargo build --target wasm32-unknown-unknown --release
|
||||||
cp target/wasm32-unknown-unknown/release/<app>.wasm ../../kernel/assets/<app>.wasm
|
cp target/wasm32-unknown-unknown/release/<app>.wasm ../../kernel/assets/<app>.wasm
|
||||||
# (hello_wasm es la excepcion: su destino es kernel/assets/app.wasm)
|
# (hello_wasm es la excepcion: su destino es kernel/assets/app.wasm)
|
||||||
@@ -87,9 +87,11 @@ infraestructura `memory::mmio` (mapeador propio de regiones MMIO en la tabla
|
|||||||
L4), la Fase 14 COMPLETA —nombres en cada ventana y barra de tareas con
|
L4), la Fase 14 COMPLETA —nombres en cada ventana y barra de tareas con
|
||||||
clic-para-enfocar—, la Fase 15 COMPLETA —la voz del sistema: acorde al
|
clic-para-enfocar—, la Fase 15 COMPLETA —la voz del sistema: acorde al
|
||||||
arrancar, repique al lanzar o cerrar, bajo al desalojar, con prioridad
|
arrancar, repique al lanzar o cerrar, bajo al desalojar, con prioridad
|
||||||
sobre `sys_tono`— y la Fase 16 COMPLETA —la barra viva: botón «+»
|
sobre `sys_tono`— la Fase 16 COMPLETA —la barra viva: botón «+»
|
||||||
lanzador a la izquierda y reloj `mm:ss` a la derecha que late cada
|
lanzador a la izquierda y reloj `mm:ss` a la derecha que late cada
|
||||||
segundo—. Todo verificado en QEMU. Ver `ROADMAP.md`.
|
segundo— y la Fase 17 COMPLETA —`bitacora`, editor de texto que persiste
|
||||||
|
entre arranques en el grafo de objetos (tipografía 8×8 embebida)—.
|
||||||
|
Todo verificado en QEMU. Ver `ROADMAP.md`.
|
||||||
|
|
||||||
## Flujo de trabajo
|
## Flujo de trabajo
|
||||||
|
|
||||||
|
|||||||
@@ -547,6 +547,27 @@ reloj LATE: cada vez que pasa un segundo nuevo —y sólo entonces, ni una vez
|
|||||||
de más—, la casa recompone el zócalo para mostrar la cifra siguiente. El
|
de más—, la casa recompone el zócalo para mostrar la cifra siguiente. El
|
||||||
resto del tiempo, el zócalo descansa.
|
resto del tiempo, el zócalo descansa.
|
||||||
|
|
||||||
|
## La bitácora — escribir y volver a encontrarlo
|
||||||
|
|
||||||
|
Hace tiempo que la casa permitía a sus inquilinos guardar pequeños recuerdos
|
||||||
|
en sus paredes —`memoriosa` lo había estrenado contando teclas a través de
|
||||||
|
los amaneceres—. Pero un cuaderno de notas, no había. Hoy llegó uno.
|
||||||
|
|
||||||
|
«bitácora» se asoma al despertar como el inquilino más importante: ocupa la
|
||||||
|
celda más grande del escritorio, con un título índigo y un papel limpio
|
||||||
|
debajo. Donde el cursor apuntaba, va apareciendo lo que se teclea, letra a
|
||||||
|
letra. Cuando llega al borde derecho del papel, salta de línea solo;
|
||||||
|
con Enter, también; con Backspace, retrocede y borra. Hasta aquí, nada
|
||||||
|
nuevo bajo el sol.
|
||||||
|
|
||||||
|
Lo nuevo es lo que ocurre al apagar la casa. Habitualmente, lo que se
|
||||||
|
escribe en un papel desaparece cuando el papel se quema. En la casa de
|
||||||
|
renaser no: cada letra que la bitácora recibe queda anclada en su mapa
|
||||||
|
secreto de objetos —el mismo árbol en el que viven los inquilinos—. Al
|
||||||
|
apagar y volver a encender, el papel vuelve a su sitio con cada palabra
|
||||||
|
intacta. Apaga, enciende, sigue escribiendo. La casa no olvida lo que se
|
||||||
|
le confía.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*El diario continúa. La próxima página la escribirá la próxima jornada.*
|
*El diario continúa. La próxima página la escribirá la próxima jornada.*
|
||||||
|
|||||||
@@ -289,6 +289,20 @@ tiempo: el reloj avanza de `0:17` a `0:29`.
|
|||||||
`pintar_taskbar` dibuja la cruz del lanzador como dos rectángulos
|
`pintar_taskbar` dibuja la cruz del lanzador como dos rectángulos
|
||||||
cruzados (sin depender de la tipografía) y rotula el reloj.
|
cruzados (sin depender de la tipografía) y rotula el reloj.
|
||||||
|
|
||||||
|
## Fase 17 — `bitacora`, el editor que recuerda (completada)
|
||||||
|
|
||||||
|
`memoriosa` (Fase 7c) demostró que un app podía persistir su huella. La Fase 17
|
||||||
|
lo lleva al gesto natural: un editor de texto. Tecleas, reinicias renaser, el
|
||||||
|
texto sigue ahí. Verificada en QEMU.
|
||||||
|
|
||||||
|
- Nuevo crate `apps/bitacora/`: lienzo 480×280, tipografía 8×8 (crate `font8x8`)
|
||||||
|
escalada x2 a 16×16, render pixel a pixel desde la memoria del propio app.
|
||||||
|
Buffer 512 bytes, wrap a 28 columnas, Enter / Backspace, persiste cada
|
||||||
|
cambio con `sys_estado_guardar`. Mapeo de scancodes US a ASCII (minúsculas).
|
||||||
|
- `GENESIS` crece de 7 a 8 apps; `bitacora` es la maestra al arrancar.
|
||||||
|
- `CELDA_TASKBAR_ANCHO` baja de 150 a 130 px para que las ocho pestañas
|
||||||
|
quepan holgadas con el lanzador y el reloj.
|
||||||
|
|
||||||
Líneas abiertas posteriores: reciclado de las ranuras de ventana cerradas;
|
Líneas abiertas posteriores: reciclado de las ranuras de ventana cerradas;
|
||||||
audio con varias voces (PCM) más allá del tono único de la bocina.
|
audio con varias voces (PCM) más allá del tono único de la bocina.
|
||||||
|
|
||||||
|
|||||||
Generated
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitacora"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"font8x8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "font8x8"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "875488b8711a968268c7cf5d139578713097ca4635a76044e8fe8eedf831d07e"
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# renaser :: apps/bitacora — Fase 17 :: un editor de texto que persiste
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Tecleas, los caracteres se quedan. Y al reiniciar siguen ahi, porque viven en
|
||||||
|
# el grafo de objetos (Fase 7c, `sys_estado_*`). Renderizado con la tipografia
|
||||||
|
# 8x8 clasica (font8x8), embebida en el binario WASM — no depende del kernel.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "bitacora"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "renaser :: app WASM — un editor de texto que persiste entre arranques"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Tipografia 8x8 pixel-perfect, public domain. La crate es `no_std` y compila
|
||||||
|
# limpiamente para `wasm32-unknown-unknown` con `default-features = false`.
|
||||||
|
font8x8 = { version = "0.3", default-features = false, features = ["unicode"] }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
opt-level = "s"
|
||||||
|
lto = true
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// renaser :: apps/bitacora — Fase 17 :: un editor que recuerda
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// La fase 7c le dio a las apps memoria mas alla del arranque: `sys_estado_*`
|
||||||
|
// ancla la huella de un app en el grafo, y al reiniciar el kernel se la
|
||||||
|
// devuelve. `memoriosa` lo demostro contando teclas. `bitacora` lo lleva al
|
||||||
|
// siguiente paso natural: ofrecer un editor de texto.
|
||||||
|
//
|
||||||
|
// La pantalla muestra un titulo en indigo y debajo el texto que el usuario va
|
||||||
|
// tecleando, con salto de linea automatico al llegar al margen y con `Enter`.
|
||||||
|
// Backspace borra el ultimo. Cada cambio se persiste de inmediato, asi que la
|
||||||
|
// apagada brusca no pierde nada — la proxima vida del kernel retoma exacto.
|
||||||
|
//
|
||||||
|
// Tipografia: la 8x8 clasica (font8x8), escalada x2 a 16x16. Cabe en su propia
|
||||||
|
// memoria lineal y se renderiza pixel a pixel — el app no toca el lienzo del
|
||||||
|
// kernel, solo entrega su propio fotograma.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use font8x8::legacy::BASIC_LEGACY;
|
||||||
|
|
||||||
|
#[link(wasm_import_module = "renaser")]
|
||||||
|
extern "C" {
|
||||||
|
fn sys_render_frame(ptr: u32, len: u32);
|
||||||
|
fn sys_get_scancode() -> u32;
|
||||||
|
fn sys_estado_cargar(salida: u32, capacidad: u32) -> i32;
|
||||||
|
fn sys_estado_guardar(datos: u32, datos_len: u32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn al_fallar(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Geometria ----------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Tamaño del lienzo natural — debe coincidir con `region` del manifiesto.
|
||||||
|
const ANCHO: usize = 480;
|
||||||
|
const ALTO: usize = 280;
|
||||||
|
/// Pixeles por celda de glifo (8x8 escalado x2).
|
||||||
|
const PASO: usize = 16;
|
||||||
|
/// Margen horizontal para el cuerpo del texto.
|
||||||
|
const MARGEN_X: usize = 16;
|
||||||
|
/// Y de la linea base del titulo.
|
||||||
|
const Y_LABEL: usize = 6;
|
||||||
|
/// Y de la linea base de la primera fila de texto.
|
||||||
|
const Y_TEXTO: usize = 38;
|
||||||
|
/// Cuantas columnas caben.
|
||||||
|
const COLUMNAS: usize = (ANCHO - 2 * MARGEN_X) / PASO;
|
||||||
|
/// Cuantas filas caben bajo el titulo.
|
||||||
|
const FILAS: usize = (ALTO - Y_TEXTO) / PASO;
|
||||||
|
|
||||||
|
// --- Estado -------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Capacidad del buffer de texto. Al desbordarse se descarta una porcion del
|
||||||
|
/// principio (el texto mas viejo) para dejar sitio al nuevo — un cuaderno con
|
||||||
|
/// memoria finita, no un agujero negro.
|
||||||
|
const CAPACIDAD: usize = 512;
|
||||||
|
|
||||||
|
const FONDO: u32 = 0x0A_18_30;
|
||||||
|
const TINTA: u32 = 0xE8_EC_F4;
|
||||||
|
const ETIQUETA: u32 = 0x8B_5C_F6;
|
||||||
|
|
||||||
|
static mut BUFFER: [u8; CAPACIDAD] = [0; CAPACIDAD];
|
||||||
|
static mut LEN: usize = 0;
|
||||||
|
static mut LIENZO: [u32; ANCHO * ALTO] = [0; ANCHO * ALTO];
|
||||||
|
|
||||||
|
// --- ABI del userspace --------------------------------------------------------
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn init() {
|
||||||
|
// Cargar el texto persistido — si no hay nada, `n` es 0 y empezamos vacios.
|
||||||
|
let buffer = unsafe { &mut *core::ptr::addr_of_mut!(BUFFER) };
|
||||||
|
// SEGURIDAD: `sys_estado_cargar` es una capacidad del host; (ptr, len) cae
|
||||||
|
// dentro de nuestra propia memoria lineal y el host lo valida sin piedad.
|
||||||
|
let n = unsafe { sys_estado_cargar(buffer.as_mut_ptr() as u32, CAPACIDAD as u32) };
|
||||||
|
if n > 0 {
|
||||||
|
// SEGURIDAD: lectura/escritura escalar; LEN es nuestro propio cursor.
|
||||||
|
unsafe {
|
||||||
|
LEN = (n as usize).min(CAPACIDAD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pintar();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn tick() {
|
||||||
|
let mut cambio = false;
|
||||||
|
// Drenar TODOS los scancodes acumulados desde el ultimo fotograma. La cola
|
||||||
|
// es propia de este app — la inscribio la fase 5 en la IRQ1 — asi que
|
||||||
|
// mirarla aqui no le quita nada a nadie.
|
||||||
|
loop {
|
||||||
|
let sc = unsafe { sys_get_scancode() } as u8;
|
||||||
|
if sc == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if sc & 0x80 != 0 {
|
||||||
|
// Codigo de KEY-UP (release). Lo ignoramos: tecleamos al pulsar.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match sc {
|
||||||
|
0x0E => {
|
||||||
|
// Backspace — borrar el ultimo caracter, si lo hay.
|
||||||
|
unsafe {
|
||||||
|
if LEN > 0 {
|
||||||
|
LEN -= 1;
|
||||||
|
cambio = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x1C => {
|
||||||
|
// Enter — salto de linea explicito.
|
||||||
|
anexar(b'\n');
|
||||||
|
cambio = true;
|
||||||
|
}
|
||||||
|
otro => {
|
||||||
|
let c = scancode_a_caracter(otro);
|
||||||
|
if c != 0 {
|
||||||
|
anexar(c);
|
||||||
|
cambio = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cambio {
|
||||||
|
guardar();
|
||||||
|
}
|
||||||
|
pintar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Estado: buffer -----------------------------------------------------------
|
||||||
|
|
||||||
|
/// Anexa un caracter al final del buffer. Si el buffer esta lleno descarta los
|
||||||
|
/// 64 primeros bytes para hacer hueco (amortiza el coste; no es una mudanza por
|
||||||
|
/// cada pulsacion).
|
||||||
|
fn anexar(c: u8) {
|
||||||
|
unsafe {
|
||||||
|
if LEN >= CAPACIDAD {
|
||||||
|
let buffer = &mut *core::ptr::addr_of_mut!(BUFFER);
|
||||||
|
buffer.copy_within(64.., 0);
|
||||||
|
LEN = CAPACIDAD - 64;
|
||||||
|
}
|
||||||
|
let buffer = &mut *core::ptr::addr_of_mut!(BUFFER);
|
||||||
|
buffer[LEN] = c;
|
||||||
|
LEN += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Persiste el buffer en el grafo. La huella sobrevive a la siguiente arrancada.
|
||||||
|
fn guardar() {
|
||||||
|
unsafe {
|
||||||
|
let buffer = &*core::ptr::addr_of!(BUFFER);
|
||||||
|
// SEGURIDAD: (ptr, len) describe nuestra propia memoria; el host lo
|
||||||
|
// verifica y nunca lee fuera del rango entregado.
|
||||||
|
let _ = sys_estado_guardar(buffer.as_ptr() as u32, LEN as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Renderizado --------------------------------------------------------------
|
||||||
|
|
||||||
|
fn pintar() {
|
||||||
|
let lienzo = unsafe { &mut *core::ptr::addr_of_mut!(LIENZO) };
|
||||||
|
// Fondo limpio.
|
||||||
|
for pixel in lienzo.iter_mut() {
|
||||||
|
*pixel = FONDO;
|
||||||
|
}
|
||||||
|
// Titulo.
|
||||||
|
pintar_texto(lienzo, b"bitacora :: el texto persiste", MARGEN_X, Y_LABEL, ETIQUETA);
|
||||||
|
// Linea sutil bajo el titulo.
|
||||||
|
let y_linea = Y_LABEL + PASO + 4;
|
||||||
|
for x in MARGEN_X..(ANCHO - MARGEN_X) {
|
||||||
|
lienzo[y_linea * ANCHO + x] = ETIQUETA;
|
||||||
|
lienzo[(y_linea + 1) * ANCHO + x] = ETIQUETA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cuerpo: mostrar las ultimas `FILAS` lineas del buffer, con wrap en
|
||||||
|
// `COLUMNAS`. Dos pasadas para saltarse las filas viejas con elegancia.
|
||||||
|
let buffer = unsafe { &*core::ptr::addr_of!(BUFFER) };
|
||||||
|
let len = unsafe { LEN };
|
||||||
|
|
||||||
|
// Pasada 1: contar filas totales (con wrap).
|
||||||
|
let mut filas_total = 1usize;
|
||||||
|
let mut col = 0usize;
|
||||||
|
for i in 0..len {
|
||||||
|
let c = buffer[i];
|
||||||
|
if c == b'\n' {
|
||||||
|
filas_total += 1;
|
||||||
|
col = 0;
|
||||||
|
} else {
|
||||||
|
if col >= COLUMNAS {
|
||||||
|
filas_total += 1;
|
||||||
|
col = 0;
|
||||||
|
}
|
||||||
|
col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let skip = filas_total.saturating_sub(FILAS);
|
||||||
|
|
||||||
|
// Pasada 2: renderizar solo a partir de la fila `skip`.
|
||||||
|
let mut fila_actual = 0usize;
|
||||||
|
let mut col2 = 0usize;
|
||||||
|
for i in 0..len {
|
||||||
|
let c = buffer[i];
|
||||||
|
if c == b'\n' {
|
||||||
|
fila_actual += 1;
|
||||||
|
col2 = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if col2 >= COLUMNAS {
|
||||||
|
fila_actual += 1;
|
||||||
|
col2 = 0;
|
||||||
|
}
|
||||||
|
if fila_actual >= skip {
|
||||||
|
let rfila = fila_actual - skip;
|
||||||
|
if rfila < FILAS {
|
||||||
|
let x = MARGEN_X + col2 * PASO;
|
||||||
|
let y = Y_TEXTO + rfila * PASO;
|
||||||
|
pintar_glifo(lienzo, c, x, y, TINTA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
col2 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEGURIDAD: `sys_render_frame` valida (ptr, len) contra nuestra memoria.
|
||||||
|
unsafe {
|
||||||
|
sys_render_frame(lienzo.as_ptr() as u32, (ANCHO * ALTO * 4) as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pinta una cadena ASCII en (x, y_base), avanzando un PASO por glifo. Se
|
||||||
|
/// detiene si el siguiente glifo no cabria dentro del lienzo.
|
||||||
|
fn pintar_texto(lienzo: &mut [u32], texto: &[u8], x: usize, y: usize, color: u32) {
|
||||||
|
let mut cx = x;
|
||||||
|
for &c in texto {
|
||||||
|
if cx + PASO > ANCHO {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pintar_glifo(lienzo, c, cx, y, color);
|
||||||
|
cx += PASO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pinta un solo glifo 8x8 escalado a 16x16 en (x, y). Los caracteres no ASCII
|
||||||
|
/// se renderizan como `?`.
|
||||||
|
fn pintar_glifo(lienzo: &mut [u32], c: u8, x: usize, y: usize, color: u32) {
|
||||||
|
let glifo = if (c as usize) < 128 {
|
||||||
|
BASIC_LEGACY[c as usize]
|
||||||
|
} else {
|
||||||
|
BASIC_LEGACY[b'?' as usize]
|
||||||
|
};
|
||||||
|
for row in 0..8 {
|
||||||
|
for col in 0..8 {
|
||||||
|
if glifo[row] & (1 << col) != 0 {
|
||||||
|
let px = x + col * 2;
|
||||||
|
let py = y + row * 2;
|
||||||
|
if px + 1 >= ANCHO || py + 1 >= ALTO {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lienzo[py * ANCHO + px] = color;
|
||||||
|
lienzo[py * ANCHO + px + 1] = color;
|
||||||
|
lienzo[(py + 1) * ANCHO + px] = color;
|
||||||
|
lienzo[(py + 1) * ANCHO + px + 1] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Teclado: scancode -> caracter --------------------------------------------
|
||||||
|
|
||||||
|
/// Traduce un MAKE-code del set 1 (US layout) a su caracter ASCII en minuscula.
|
||||||
|
/// Devuelve 0 para los scancodes que no producen texto — modificadores,
|
||||||
|
/// extendidos, etc.: el llamante los descarta sin gritar.
|
||||||
|
fn scancode_a_caracter(sc: u8) -> u8 {
|
||||||
|
match sc {
|
||||||
|
0x02 => b'1', 0x03 => b'2', 0x04 => b'3', 0x05 => b'4', 0x06 => b'5',
|
||||||
|
0x07 => b'6', 0x08 => b'7', 0x09 => b'8', 0x0A => b'9', 0x0B => b'0',
|
||||||
|
0x10 => b'q', 0x11 => b'w', 0x12 => b'e', 0x13 => b'r', 0x14 => b't',
|
||||||
|
0x15 => b'y', 0x16 => b'u', 0x17 => b'i', 0x18 => b'o', 0x19 => b'p',
|
||||||
|
0x1E => b'a', 0x1F => b's', 0x20 => b'd', 0x21 => b'f', 0x22 => b'g',
|
||||||
|
0x23 => b'h', 0x24 => b'j', 0x25 => b'k', 0x26 => b'l',
|
||||||
|
0x2C => b'z', 0x2D => b'x', 0x2E => b'c', 0x2F => b'v', 0x30 => b'b',
|
||||||
|
0x31 => b'n', 0x32 => b'm',
|
||||||
|
0x33 => b',', 0x34 => b'.', 0x35 => b'/',
|
||||||
|
0x27 => b';', 0x28 => b'\'',
|
||||||
|
0x39 => b' ',
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,12 +107,14 @@ struct AppGenesis {
|
|||||||
region: (u32, u32, u32, u32),
|
region: (u32, u32, u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// El userspace de genesis — las siete aplicaciones que pueblan un disco recien
|
/// El userspace de genesis — las ocho aplicaciones que pueblan un disco recien
|
||||||
/// forjado. La melodia visual `tonada` (Fase 12), el compas visual `pulso`
|
/// forjado. La `bitacora` (Fase 17, editor que persiste), la melodia visual
|
||||||
/// (Fase 11), un saludo (`hola`), la `memoriosa` interactiva que recuerda entre
|
/// `tonada` (Fase 12), el compas visual `pulso` (Fase 11), un saludo (`hola`),
|
||||||
/// sesiones (Fase 7c), y tres demos de los guardarrailes del kernel: `discola`
|
/// la `memoriosa` interactiva que recuerda entre sesiones (Fase 7c), y tres
|
||||||
/// (combustible), `glotona` (memoria) y `cronista` (la cronica de los arranques).
|
/// demos de los guardarrailes del kernel: `discola` (combustible), `glotona`
|
||||||
const GENESIS: [AppGenesis; 7] = [
|
/// (memoria) y `cronista` (la cronica de los arranques).
|
||||||
|
const GENESIS: [AppGenesis; 8] = [
|
||||||
|
AppGenesis { nombre: "bitacora", archivo: "bitacora.wasm", region: (100, 120, 480, 280) },
|
||||||
AppGenesis { nombre: "tonada", archivo: "tonada.wasm", region: (100, 120, 360, 120) },
|
AppGenesis { nombre: "tonada", archivo: "tonada.wasm", region: (100, 120, 360, 120) },
|
||||||
AppGenesis { nombre: "pulso", archivo: "pulso.wasm", region: (100, 120, 360, 120) },
|
AppGenesis { nombre: "pulso", archivo: "pulso.wasm", region: (100, 120, 360, 120) },
|
||||||
AppGenesis { nombre: "hola", archivo: "app.wasm", region: (100, 120, 480, 560) },
|
AppGenesis { nombre: "hola", archivo: "app.wasm", region: (100, 120, 480, 560) },
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -67,8 +67,10 @@ const FRANJA_CONSOLA: usize = 296;
|
|||||||
/// ahi una pestaña con su nombre, que el clic enfoca.
|
/// ahi una pestaña con su nombre, que el clic enfoca.
|
||||||
const FRANJA_TASKBAR: usize = 40;
|
const FRANJA_TASKBAR: usize = 40;
|
||||||
|
|
||||||
/// Anchura de cada celda de la barra de tareas, en pixeles.
|
/// Anchura de cada celda de la barra de tareas, en pixeles. Dimensionada para
|
||||||
const CELDA_TASKBAR_ANCHO: usize = 150;
|
/// que las ocho apps de genesis + el lanzador + el reloj caben holgados en una
|
||||||
|
/// pantalla de 1280 px.
|
||||||
|
const CELDA_TASKBAR_ANCHO: usize = 130;
|
||||||
/// Hueco entre celdas adyacentes de la barra.
|
/// Hueco entre celdas adyacentes de la barra.
|
||||||
const CELDA_TASKBAR_HUECO: usize = 6;
|
const CELDA_TASKBAR_HUECO: usize = 6;
|
||||||
/// Margen izquierdo y derecho de la barra de tareas.
|
/// Margen izquierdo y derecho de la barra de tareas.
|
||||||
|
|||||||
Reference in New Issue
Block a user