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:
sergio
2026-05-22 20:54:48 +00:00
parent a388ab14b7
commit 922ad1f86b
13 changed files with 371 additions and 15 deletions
+33
View File
@@ -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
View File
@@ -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
+22
View File
@@ -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
View File
@@ -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
+7
View File
@@ -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"
+29
View File
@@ -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
+147
View File
@@ -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);
}
}
+7 -6
View File
@@ -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) },
BIN
View File
Binary file not shown.
+5
View File
@@ -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);
+73
View File
@@ -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 -3
View File
@@ -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;
+19 -1
View File
@@ -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(())
} }