feat(renaser): Fase 12 — la bocina del PC como capacidad de host
La Fase 11 dio al userspace un reloj; la Fase 12, una voz. Hasta hoy renaser solo sabía dibujar para llamar la atención. - Driver `drivers/altavoz`: el canal 2 del PIT como generador de onda cuadrada + la compuerta del puerto 0x61. El canal 0 —latido del kernel— no se toca. `tono(hz)` es su única vía; un 0 la silencia. - Capacidad `sys_tono(frecuencia_hz)` — la undécima función del host. La bocina es un recurso único: pertenece a la ventana ENFOCADA, como el teclado desde la Fase 8c. Al cambiar el foco, el compositor la calla; la nueva dueña la reclama en su próximo fotograma. - App nueva `tonada` (`apps/tonada/`, wasm32): toca una escala de Do mayor y la dibuja como una escalera de barras. Junta el reloj (`sys_tiempo_mono`) y la bocina (`sys_tono`). - `GENESIS` crece de 6 a 7 apps; `tonada` es la maestra del escritorio. Verificado en QEMU. Visual: la escalera de `tonada` recorre la escala con el tiempo. Sonido: con la bocina enrutada a un WAV, el PCM capturado es una onda cuadrada oscilante de ~375 Hz — la frecuencia media de la escala de Do mayor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -873,3 +873,36 @@ un sentido del tiempo: el reloj monótono del sistema, como capacidad.
|
|||||||
capturas. Un `Alt+N` da a luz un segundo `pulso` ~15 s después del arranque;
|
capturas. Un `Alt+N` da a luz un segundo `pulso` ~15 s después del arranque;
|
||||||
flotado junto al primero, su barra está en la MISMA fase — la prueba de que
|
flotado junto al primero, su barra está en la MISMA fase — la prueba de que
|
||||||
el compás se rige por el reloj absoluto, no por una cuenta de fotogramas.
|
el compás se rige por el reloj absoluto, no por una cuenta de fotogramas.
|
||||||
|
|
||||||
|
## Fase 12 — La bocina del PC como capacidad de host — 2026-05-22
|
||||||
|
|
||||||
|
La Fase 11 le dio al userspace un reloj; la Fase 12, una voz. La bocina del PC
|
||||||
|
—el canal 2 del PIT como generador de onda cuadrada— se suma a la matriz de
|
||||||
|
capacidades. Hasta hoy renaser sólo sabía DIBUJAR para llamar la atención.
|
||||||
|
|
||||||
|
### Añadido
|
||||||
|
- **Driver `drivers/altavoz`.** Programa el canal 2 del PIT a una frecuencia y
|
||||||
|
abre la compuerta del puerto 0x61. El canal 0 del PIT es el latido del
|
||||||
|
kernel; el canal 2 es de la bocina y de nadie más — no se perturban—.
|
||||||
|
`tono(hz)` es su única vía; un `0` la silencia.
|
||||||
|
- **Capacidad `sys_tono(frecuencia_hz)`** — la undécima función del host. La
|
||||||
|
bocina es un recurso ÚNICO y global: para que dos apps no se la disputen,
|
||||||
|
pertenece —como el teclado desde la Fase 8c— a la ventana ENFOCADA. Una app
|
||||||
|
sin foco puede pedir un tono; sencillamente, no se oye. Al cambiar el foco,
|
||||||
|
el compositor calla la bocina (`mover_foco`, `cerrar`): la nueva dueña la
|
||||||
|
reclama en su próximo fotograma.
|
||||||
|
- **App `tonada`** (`apps/tonada/`, `wasm32`). Toca una escala de Do mayor en
|
||||||
|
bucle y la dibuja como una escalera de ocho barras, con la nota que suena
|
||||||
|
encendida. Junta las dos capacidades nuevas del kernel: el reloj
|
||||||
|
(`sys_tiempo_mono`) le marca el compás, la bocina (`sys_tono`) le da voz.
|
||||||
|
- `tonada` encabeza el userspace de génesis: `GENESIS` pasa de 6 a 7 apps, con
|
||||||
|
`tonada` como la ventana maestra —enfocada al arrancar, así suena de
|
||||||
|
inmediato—.
|
||||||
|
|
||||||
|
### Verificado
|
||||||
|
- QEMU. Visual: la escalera de `tonada` enciende la nota en curso y ésta
|
||||||
|
recorre la escala con el tiempo (capturas con la 6.ª y la 1.ª nota vivas).
|
||||||
|
Sonido: con la bocina enrutada a un WAV (`-audiodev wav` + `-machine
|
||||||
|
pcspk-audiodev`), el PCM capturado —50 s— es una onda cuadrada oscilante de
|
||||||
|
±24 000 de amplitud, con una frecuencia media de ~375 Hz: la de la escala de
|
||||||
|
Do mayor.
|
||||||
|
|||||||
+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
|
cd apps/<app> # hello_wasm, discola, glotona, cronista, memoriosa, pulso, tonada
|
||||||
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)
|
||||||
@@ -78,9 +78,11 @@ objetos—, la Fase 8 COMPLETA —el compositor teselante e interactivo: teselad
|
|||||||
con `mirada-layout` (8a), ciclado de layout (8b), foco y enrutamiento selectivo
|
con `mirada-layout` (8a), ciclado de layout (8b), foco y enrutamiento selectivo
|
||||||
del teclado (8c), promoción y reordenación de ventanas (8d)—, la Fase 9
|
del teclado (8c), promoción y reordenación de ventanas (8d)—, la Fase 9
|
||||||
COMPLETA —orden-Z y ventanas flotantes: composición con solapamiento (`Alt+F`)—
|
COMPLETA —orden-Z y ventanas flotantes: composición con solapamiento (`Alt+F`)—
|
||||||
la Fase 10 COMPLETA —alta y baja de aplicaciones en vivo (`Alt+N` / `Alt+Q`)— y
|
la Fase 10 COMPLETA —alta y baja de aplicaciones en vivo (`Alt+N` / `Alt+Q`)—,
|
||||||
la Fase 11 COMPLETA —el reloj del sistema como capacidad de host
|
la Fase 11 COMPLETA —el reloj del sistema como capacidad de host
|
||||||
(`sys_tiempo_mono`) + la app `pulso`—. Todo verificado en QEMU. Ver `ROADMAP.md`.
|
(`sys_tiempo_mono`) + la app `pulso`— y la Fase 12 COMPLETA —la bocina del PC
|
||||||
|
como capacidad de host (`sys_tono`) + la app `tonada`—. Todo verificado en
|
||||||
|
QEMU. Ver `ROADMAP.md`.
|
||||||
|
|
||||||
## Flujo de trabajo
|
## Flujo de trabajo
|
||||||
|
|
||||||
|
|||||||
@@ -454,6 +454,28 @@ incorpora justo donde va el primero, al instante, como dos bailarines que oyen
|
|||||||
la misma música—. Porque ninguno lleva su propia cuenta: ambos miran el mismo
|
la misma música—. Porque ninguno lleva su propia cuenta: ambos miran el mismo
|
||||||
reloj del recibidor.
|
reloj del recibidor.
|
||||||
|
|
||||||
|
## La voz — la casa aprende a sonar
|
||||||
|
|
||||||
|
La casa sabía mostrarse, recordar, medir el tiempo. Pero era, hasta hoy, una
|
||||||
|
casa muda. Lo único que sabía hacer para llamar la atención era pintar —una
|
||||||
|
franja roja, un borde de color—. Nunca un sonido.
|
||||||
|
|
||||||
|
Hoy estrenó su voz. No una voz rica, de orquesta: la más pobre que existe, un
|
||||||
|
único hilo de sonido, el viejo altavoz que todo PC lleva escondido. Pero basta.
|
||||||
|
Con él la casa puede ya emitir un tono, agudo o grave, o callar.
|
||||||
|
|
||||||
|
Y, como con el reloj, hubo una regla de cortesía. La voz es una sola, y si
|
||||||
|
todos los inquilinos hablaran a la vez no se entendería nada. Así que la voz,
|
||||||
|
igual que el oído —el teclado—, pertenece al inquilino del cuarto que se está
|
||||||
|
mirando. Los demás pueden mover los labios; sólo se oye al que tiene la
|
||||||
|
atención de la casa. Al cambiar la mirada de cuarto, la casa calla un instante,
|
||||||
|
y el nuevo elegido toma la palabra.
|
||||||
|
|
||||||
|
Para estrenarla llegó «tonada», que no sabe hablar pero sí cantar: toca una
|
||||||
|
escala, una y otra vez, y la dibuja como una escalera de luces que sube al
|
||||||
|
compás de la música. Míralo cantar en silencio desde cualquier rincón;
|
||||||
|
acércate —dale el foco— y lo oirás.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*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.*
|
||||||
|
|||||||
+18
-2
@@ -195,8 +195,24 @@ tiempo. Verificada en QEMU (`sendkey`).
|
|||||||
de que el tiempo es absoluto, no una cuenta de fotogramas.
|
de que el tiempo es absoluto, no una cuenta de fotogramas.
|
||||||
- El userspace de génesis crece de 5 a 6 apps.
|
- El userspace de génesis crece de 5 a 6 apps.
|
||||||
|
|
||||||
Líneas abiertas posteriores: audio como capacidad de host; reciclado de las
|
## Fase 12 — la bocina del PC como capacidad de host (completada)
|
||||||
ranuras de ventana cerradas.
|
|
||||||
|
La Fase 11 le dio al userspace un reloj; la Fase 12, una voz. La bocina del PC
|
||||||
|
se suma a la matriz de capacidades. Verificada en QEMU.
|
||||||
|
|
||||||
|
- Driver `drivers/altavoz`: el canal 2 del PIT como generador de onda cuadrada
|
||||||
|
+ la compuerta del puerto 0x61. El canal 0 (latido del kernel) no se toca.
|
||||||
|
- Capacidad `sys_tono(frecuencia_hz)` —la undécima—. La bocina es un recurso
|
||||||
|
único: pertenece a la ventana ENFOCADA, como el teclado desde la Fase 8c. Al
|
||||||
|
cambiar el foco, el compositor la calla.
|
||||||
|
- App nueva `tonada`: toca una escala de Do mayor y la dibuja como una
|
||||||
|
escalera de barras. Junta el reloj (`sys_tiempo_mono`) y la bocina
|
||||||
|
(`sys_tono`). El userspace de génesis crece de 6 a 7 apps.
|
||||||
|
- Verificado por captura: la onda cuadrada de la bocina, enrutada a un WAV,
|
||||||
|
late a la frecuencia media de la escala.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## Principios que persisten entre fases
|
## Principios que persisten entre fases
|
||||||
|
|
||||||
|
|||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tonada"
|
||||||
|
version = "0.1.0"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# renaser :: apps/tonada — Fase 12 :: una melodia visual para estrenar la bocina
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Un modulo WebAssembly puro que toca una escala —y la dibuja— gobernandose por
|
||||||
|
# dos capacidades del host: el reloj monotono (`sys_tiempo_mono`, Fase 11) que
|
||||||
|
# le marca el compas, y la bocina (`sys_tono`, Fase 12) por la que suena. Tiene
|
||||||
|
# su propio `[workspace]`: queda fuera del espacio de trabajo del kernel.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "tonada"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "renaser :: app WASM — una melodia visual: reloj del host + bocina"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
# `cdylib` produce un modulo `.wasm` que exporta funciones — el formato que
|
||||||
|
# wasmi instancia. La aplicacion solo habla con el kernel por funciones del host.
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
opt-level = "s"
|
||||||
|
lto = true
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// renaser :: apps/tonada — Fase 12 :: una melodia visual para estrenar la voz
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Con la Fase 11 el userspace gano un reloj; con la Fase 12, una voz: la
|
||||||
|
// bocina del PC, capacidad `sys_tono`. `tonada` las junta. Toca una escala de
|
||||||
|
// Do mayor, una y otra vez, y la dibuja como una escalera de barras — la nota
|
||||||
|
// que suena, encendida.
|
||||||
|
//
|
||||||
|
// Su escena y su sonido son una FUNCION PURA del reloj del host: no guarda
|
||||||
|
// estado entre fotogramas. Y la bocina pertenece a la ventana ENFOCADA —como
|
||||||
|
// el teclado—: `tonada` pide su tono en cada fotograma, pero solo se oye
|
||||||
|
// cuando tiene el foco. Mira la melodia siempre; escuchala cuando la enfocas.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
// --- Las capacidades que el kernel `renaser` inyecta a esta aplicacion. ---
|
||||||
|
#[link(wasm_import_module = "renaser")]
|
||||||
|
extern "C" {
|
||||||
|
/// Compone un bufer de pixeles (de ESTA memoria lineal) en la region que el
|
||||||
|
/// kernel asigno a esta aplicacion.
|
||||||
|
fn sys_render_frame(ptr: u32, len: u32);
|
||||||
|
/// El reloj monotono del sistema: milisegundos desde el arranque.
|
||||||
|
fn sys_tiempo_mono() -> u64;
|
||||||
|
/// Hace sonar la bocina del PC a una frecuencia (un 0 la silencia). Solo se
|
||||||
|
/// oye si esta ventana tiene el foco — lo decide el host.
|
||||||
|
fn sys_tono(frecuencia_hz: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sin sistema operativo bajo nosotros, un panico solo puede detenerse en seco.
|
||||||
|
#[panic_handler]
|
||||||
|
fn al_fallar(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Geometria de la escena. El ancho y el alto DEBEN coincidir con la region
|
||||||
|
// que el kernel asigna a esta app. ---
|
||||||
|
const ANCHO: usize = 360;
|
||||||
|
const ALTO: usize = 120;
|
||||||
|
|
||||||
|
/// La melodia: una escala de Do mayor ascendente. Frecuencias en Hz —de Do4 a
|
||||||
|
/// Do5—. El indice de la nota es, ademas, el indice de su barra en pantalla.
|
||||||
|
const MELODIA: [u32; 8] = [262, 294, 330, 349, 392, 440, 494, 523];
|
||||||
|
|
||||||
|
/// Duracion de cada nota, en milisegundos.
|
||||||
|
const NOTA_MS: u64 = 420;
|
||||||
|
|
||||||
|
/// Azul nocturno: el fondo del lienzo.
|
||||||
|
const FONDO: u32 = 0x0A_18_30;
|
||||||
|
/// Una nota en reposo — una barra de la escalera, apagada.
|
||||||
|
const BARRA: u32 = 0x24_30_4E;
|
||||||
|
/// La nota que suena AHORA — el indigo brillante del foco del compositor.
|
||||||
|
const BARRA_VIVA: u32 = 0x8B_5C_F6;
|
||||||
|
|
||||||
|
/// Margen lateral, en pixeles.
|
||||||
|
const MARGEN: usize = 22;
|
||||||
|
/// La linea base sobre la que se alzan las barras.
|
||||||
|
const BASE_Y: usize = 100;
|
||||||
|
/// Altura de la barra mas grave, y cuanto crece cada peldaño de la escala.
|
||||||
|
const ALTO_MIN: usize = 13;
|
||||||
|
const ALTO_PASO: usize = 11;
|
||||||
|
|
||||||
|
/// El lienzo de la aplicacion, en SU propia memoria lineal. El kernel jamas lo
|
||||||
|
/// ve directamente: solo recibe el (ptr, len) que cada fotograma le entrega.
|
||||||
|
static mut LIENZO: [u32; ANCHO * ALTO] = [0; ANCHO * ALTO];
|
||||||
|
|
||||||
|
/// Preparacion: el kernel la invoca UNA sola vez, al cargar el modulo.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn init() {
|
||||||
|
pintar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Un fotograma de trabajo: pide el tono de la nota actual, redibuja la
|
||||||
|
/// escalera y RETORNA, cediendo la CPU al kernel y a las apps vecinas.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn tick() {
|
||||||
|
pintar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pinta —y suena— la melodia en el instante ACTUAL. No guarda estado alguno:
|
||||||
|
/// la nota viva es una funcion pura del reloj del host.
|
||||||
|
fn pintar() {
|
||||||
|
// SEGURIDAD: durante `init` y `tick` esta es la unica via de acceso a
|
||||||
|
// LIENZO, y el kernel jamas reentra el modulo mientras una de ellas corre.
|
||||||
|
let lienzo: &mut [u32] = unsafe { &mut *core::ptr::addr_of_mut!(LIENZO) };
|
||||||
|
|
||||||
|
// La nota actual: se deriva SOLO del reloj del host.
|
||||||
|
// SEGURIDAD: `sys_tiempo_mono` es una capacidad del host; no toca memoria.
|
||||||
|
let tiempo = unsafe { sys_tiempo_mono() };
|
||||||
|
let ciclo = NOTA_MS * MELODIA.len() as u64;
|
||||||
|
let actual = ((tiempo % ciclo) / NOTA_MS) as usize;
|
||||||
|
|
||||||
|
// Pedir el tono de la nota actual. El host solo lo hara sonar si esta
|
||||||
|
// ventana tiene el foco — la app pide sin saberlo.
|
||||||
|
// SEGURIDAD: `sys_tono` es una capacidad del host; no toca memoria.
|
||||||
|
unsafe {
|
||||||
|
sys_tono(MELODIA[actual]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fondo limpio.
|
||||||
|
for pixel in lienzo.iter_mut() {
|
||||||
|
*pixel = FONDO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Una barra por nota: la escalera de la melodia. La que suena, encendida.
|
||||||
|
let util = ANCHO - 2 * MARGEN;
|
||||||
|
let ranura = util / MELODIA.len();
|
||||||
|
let barra_ancho = ranura * 3 / 4;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < MELODIA.len() {
|
||||||
|
let altura = ALTO_MIN + i * ALTO_PASO;
|
||||||
|
let x0 = MARGEN + i * ranura + (ranura - barra_ancho) / 2;
|
||||||
|
let color = if i == actual { BARRA_VIVA } else { BARRA };
|
||||||
|
columna(lienzo, x0, barra_ancho, altura, color);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
volcar(lienzo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pinta una barra vertical: `ancho` pixeles desde la columna `x0`, `altura`
|
||||||
|
/// pixeles alzandose sobre la linea base `BASE_Y`. Se recorta al lienzo.
|
||||||
|
fn columna(lienzo: &mut [u32], x0: usize, ancho: usize, altura: usize, color: u32) {
|
||||||
|
let x1 = (x0 + ancho).min(ANCHO);
|
||||||
|
let y1 = BASE_Y.min(ALTO);
|
||||||
|
let y0 = y1.saturating_sub(altura);
|
||||||
|
let mut fila = y0;
|
||||||
|
while fila < y1 {
|
||||||
|
let base = fila * ANCHO;
|
||||||
|
let mut col = x0;
|
||||||
|
while col < x1 {
|
||||||
|
lienzo[base + col] = color;
|
||||||
|
col += 1;
|
||||||
|
}
|
||||||
|
fila += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Entrega el lienzo completo al kernel. El (ptr, len) apunta SIEMPRE dentro de
|
||||||
|
/// nuestra memoria lineal, y su tamaño es, exactamente, el de la region.
|
||||||
|
fn volcar(lienzo: &[u32]) {
|
||||||
|
// SEGURIDAD: `sys_render_frame` es una capacidad del host; el (ptr, len)
|
||||||
|
// describe nuestra propia memoria lineal y el host lo verifica sin piedad.
|
||||||
|
unsafe {
|
||||||
|
sys_render_frame(lienzo.as_ptr() as u32, (ANCHO * ALTO * 4) as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,12 +107,13 @@ struct AppGenesis {
|
|||||||
region: (u32, u32, u32, u32),
|
region: (u32, u32, u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// El userspace de genesis — las seis aplicaciones que pueblan un disco recien
|
/// El userspace de genesis — las siete aplicaciones que pueblan un disco recien
|
||||||
/// forjado. El compas visual `pulso` (Fase 11), un saludo (`hola`), la
|
/// forjado. La melodia visual `tonada` (Fase 12), el compas visual `pulso`
|
||||||
/// `memoriosa` interactiva que recuerda entre sesiones (Fase 7c), y tres demos
|
/// (Fase 11), un saludo (`hola`), la `memoriosa` interactiva que recuerda entre
|
||||||
/// de los guardarrailes del kernel: `discola` (combustible), `glotona`
|
/// sesiones (Fase 7c), y tres demos de los guardarrailes del kernel: `discola`
|
||||||
/// (memoria) y `cronista` (la cronica de los arranques).
|
/// (combustible), `glotona` (memoria) y `cronista` (la cronica de los arranques).
|
||||||
const GENESIS: [AppGenesis; 6] = [
|
const GENESIS: [AppGenesis; 7] = [
|
||||||
|
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) },
|
||||||
AppGenesis { nombre: "memoriosa", archivo: "memoriosa.wasm", region: (700, 120, 360, 80) },
|
AppGenesis { nombre: "memoriosa", archivo: "memoriosa.wasm", region: (700, 120, 360, 80) },
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -410,6 +410,9 @@ fn mover_foco(adelante: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FOCO.store(nuevo, Ordering::Relaxed);
|
FOCO.store(nuevo, Ordering::Relaxed);
|
||||||
|
// La bocina pertenece a la ventana enfocada (Fase 12): al cambiar el foco,
|
||||||
|
// callar — la nueva dueña la reclamara en su proximo fotograma si quiere.
|
||||||
|
crate::drivers::altavoz::tono(0);
|
||||||
// La ventana recien enfocada, si flota, al frente del orden-Z.
|
// La ventana recien enfocada, si flota, al frente del orden-Z.
|
||||||
alzar_si_flota(&mut escritorio, nuevo);
|
alzar_si_flota(&mut escritorio, nuevo);
|
||||||
recomponer(&escritorio);
|
recomponer(&escritorio);
|
||||||
@@ -599,6 +602,8 @@ fn cerrar() {
|
|||||||
})
|
})
|
||||||
.unwrap_or(foco);
|
.unwrap_or(foco);
|
||||||
FOCO.store(nuevo, Ordering::Relaxed);
|
FOCO.store(nuevo, Ordering::Relaxed);
|
||||||
|
// El foco cambia: callar la bocina (Fase 12) — ver `mover_foco`.
|
||||||
|
crate::drivers::altavoz::tono(0);
|
||||||
alzar_si_flota(&mut escritorio, nuevo);
|
alzar_si_flota(&mut escritorio, nuevo);
|
||||||
aplicar_teselado(&mut escritorio);
|
aplicar_teselado(&mut escritorio);
|
||||||
recomponer(&escritorio);
|
recomponer(&escritorio);
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// renaser :: kernel/src/drivers/altavoz.rs — Fase 12 :: la bocina del PC
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// La bocina del PC es el instrumento mas humilde del hardware: un solo bit que,
|
||||||
|
// conmutado a la frecuencia justa, hace vibrar una membrana. No hay PCM, ni
|
||||||
|
// DMA, ni mezclador — solo el canal 2 del PIT generando una onda cuadrada y
|
||||||
|
// una compuerta en el puerto 0x61 que la deja pasar al altavoz, o no.
|
||||||
|
//
|
||||||
|
// El canal 0 del PIT es el latido del kernel (ver `pic`); el canal 2 es de la
|
||||||
|
// bocina y de nadie mas — programarlo aqui no perturba el temporizador—. Esta
|
||||||
|
// es la unica via del kernel hacia el sonido; la capacidad `sys_tono` la
|
||||||
|
// ofrece al userspace, gobernada por el foco del compositor.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
|
/// Frecuencia del cristal del PIT, en Hz — el divisor se calcula contra ella.
|
||||||
|
const PIT_BASE_HZ: u32 = 1_193_182;
|
||||||
|
|
||||||
|
/// Puerto de comando del PIT.
|
||||||
|
const PIT_COMANDO: u16 = 0x43;
|
||||||
|
/// Puerto de datos del canal 2 del PIT — el de la bocina.
|
||||||
|
const PIT_CANAL2: u16 = 0x42;
|
||||||
|
/// Puerto de control de la bocina (bits 0 y 1: compuerta del PIT y dato).
|
||||||
|
const CONTROL_BOCINA: u16 = 0x61;
|
||||||
|
|
||||||
|
/// Pone la bocina a sonar a `frecuencia_hz`. Un `0` —o una frecuencia que un
|
||||||
|
/// divisor de 16 bits no pueda representar (por debajo de ~19 Hz)— la SILENCIA.
|
||||||
|
/// Es la unica via del kernel hacia el sonido.
|
||||||
|
pub fn tono(frecuencia_hz: u32) {
|
||||||
|
if frecuencia_hz == 0 || PIT_BASE_HZ / frecuencia_hz > 0xFFFF {
|
||||||
|
silenciar();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// El divisor cabe en 16 bits; un `.max(1)` lo protege de una frecuencia
|
||||||
|
// disparatadamente alta que lo dejara en cero.
|
||||||
|
let divisor = (PIT_BASE_HZ / frecuencia_hz).max(1) as u16;
|
||||||
|
|
||||||
|
// SEGURIDAD: 0x43 y 0x42 son los puertos del PIT en la arquitectura PC;
|
||||||
|
// 0xB6 selecciona el canal 2, acceso lobyte+hibyte, modo 3 (onda cuadrada).
|
||||||
|
// El canal 2 es exclusivo de la bocina: no perturba el latido del kernel.
|
||||||
|
unsafe {
|
||||||
|
let mut comando = Port::<u8>::new(PIT_COMANDO);
|
||||||
|
let mut canal2 = Port::<u8>::new(PIT_CANAL2);
|
||||||
|
comando.write(0xB6u8);
|
||||||
|
canal2.write((divisor & 0xFF) as u8);
|
||||||
|
canal2.write((divisor >> 8) as u8);
|
||||||
|
}
|
||||||
|
abrir_compuerta();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Abre la compuerta del puerto 0x61: deja pasar la onda del canal 2 al altavoz.
|
||||||
|
fn abrir_compuerta() {
|
||||||
|
// SEGURIDAD: 0x61 es el puerto de control de la bocina; sus bits 0 y 1
|
||||||
|
// —compuerta del PIT y dato del altavoz— se tocan con leer-modificar-
|
||||||
|
// escribir para no perturbar los demas bits del chipset.
|
||||||
|
unsafe {
|
||||||
|
let mut control = Port::<u8>::new(CONTROL_BOCINA);
|
||||||
|
let estado = control.read();
|
||||||
|
control.write(estado | 0b11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Silencia la bocina: cierra la compuerta del puerto 0x61. La onda del canal 2
|
||||||
|
/// sigue generandose, pero ya no alcanza la membrana.
|
||||||
|
fn silenciar() {
|
||||||
|
// SEGURIDAD: ver `abrir_compuerta`. Limpiar los bits 0 y 1 corta el sonido.
|
||||||
|
unsafe {
|
||||||
|
let mut control = Port::<u8>::new(CONTROL_BOCINA);
|
||||||
|
let estado = control.read();
|
||||||
|
control.write(estado & !0b11);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,10 +6,13 @@
|
|||||||
// abren la primera via hacia hardware que el kernel debe DESCUBRIR y reclamar
|
// abren la primera via hacia hardware que el kernel debe DESCUBRIR y reclamar
|
||||||
// por si mismo:
|
// por si mismo:
|
||||||
//
|
//
|
||||||
// * `pci` — acceso al espacio de configuracion del bus PCI (0xCF8/0xCFC).
|
// * `pci` — acceso al espacio de configuracion del bus PCI (0xCF8/0xCFC).
|
||||||
// * `disco` — el disco virtio-blk: asignador de marcos DMA, `Hal` y la
|
// * `disco` — el disco virtio-blk: asignador de marcos DMA, `Hal` y la
|
||||||
// lectura, por sondeo, de su primer sector.
|
// lectura, por sondeo, de su primer sector.
|
||||||
|
// * `altavoz` — la bocina del PC: el canal 2 del PIT como generador de tono
|
||||||
|
// (Fase 12).
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
|
pub mod altavoz;
|
||||||
pub mod disco;
|
pub mod disco;
|
||||||
pub mod pci;
|
pub mod pci;
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
// * sys_object_fijar_raiz — coronar un objeto como raiz;
|
// * sys_object_fijar_raiz — coronar un objeto como raiz;
|
||||||
// * sys_estado_cargar — leer el estado persistido de la app (Fase 7c);
|
// * sys_estado_cargar — leer el estado persistido de la app (Fase 7c);
|
||||||
// * sys_estado_guardar — anclar el estado persistido de la app (Fase 7c);
|
// * sys_estado_guardar — anclar el estado persistido de la app (Fase 7c);
|
||||||
// * sys_tiempo_mono — leer el reloj monotono del sistema (Fase 11).
|
// * sys_tiempo_mono — leer el reloj monotono del sistema (Fase 11);
|
||||||
|
// * sys_tono — hacer sonar la bocina del PC (Fase 12).
|
||||||
//
|
//
|
||||||
// GUARDARRAIL: el kernel valida MATEMATICAMENTE todo puntero que el modulo le
|
// GUARDARRAIL: el kernel valida MATEMATICAMENTE todo puntero que el modulo le
|
||||||
// entrega contra los limites reales de su memoria lineal. No se confia en que
|
// entrega contra los limites reales de su memoria lineal. No se confia en que
|
||||||
@@ -457,5 +458,22 @@ pub(crate) fn enlazar_capacidades(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// --- CAPACIDAD 11 :: sys_tono(frecuencia_hz) ---
|
||||||
|
// Hace sonar la bocina del PC a `frecuencia_hz` (un 0 la silencia). La
|
||||||
|
// bocina es un recurso UNICO y global: para que dos apps no se la disputen,
|
||||||
|
// pertenece —como el teclado desde la Fase 8c— a la ventana ENFOCADA. Una
|
||||||
|
// app sin foco puede pedir un tono; sencillamente, no se oye. Y cuando el
|
||||||
|
// foco cambia, el compositor calla la bocina: la nueva dueña la reclamara
|
||||||
|
// en su proximo fotograma si quiere sonar.
|
||||||
|
enlazador.func_wrap(
|
||||||
|
"renaser",
|
||||||
|
"sys_tono",
|
||||||
|
|caller: Caller<'_, ContextoCapacidades>, frecuencia_hz: u32| {
|
||||||
|
if crate::compositor::foco() == caller.data().indice_app {
|
||||||
|
crate::drivers::altavoz::tono(frecuencia_hz);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user