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:
@@ -410,6 +410,9 @@ fn mover_foco(adelante: bool) {
|
||||
}
|
||||
}
|
||||
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.
|
||||
alzar_si_flota(&mut escritorio, nuevo);
|
||||
recomponer(&escritorio);
|
||||
@@ -599,6 +602,8 @@ fn cerrar() {
|
||||
})
|
||||
.unwrap_or(foco);
|
||||
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);
|
||||
aplicar_teselado(&mut 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
|
||||
// por si mismo:
|
||||
//
|
||||
// * `pci` — acceso al espacio de configuracion del bus PCI (0xCF8/0xCFC).
|
||||
// * `disco` — el disco virtio-blk: asignador de marcos DMA, `Hal` y la
|
||||
// lectura, por sondeo, de su primer sector.
|
||||
// * `pci` — acceso al espacio de configuracion del bus PCI (0xCF8/0xCFC).
|
||||
// * `disco` — el disco virtio-blk: asignador de marcos DMA, `Hal` y la
|
||||
// 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 pci;
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
// * sys_object_fijar_raiz — coronar un objeto como raiz;
|
||||
// * 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_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
|
||||
// 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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user