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;
|
||||
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.
|
||||
|
||||
## 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:
|
||||
|
||||
```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
|
||||
cp target/wasm32-unknown-unknown/release/<app>.wasm ../../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
|
||||
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`)—
|
||||
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
|
||||
(`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
|
||||
|
||||
|
||||
@@ -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
|
||||
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.*
|
||||
|
||||
+18
-2
@@ -195,8 +195,24 @@ tiempo. Verificada en QEMU (`sendkey`).
|
||||
de que el tiempo es absoluto, no una cuenta de fotogramas.
|
||||
- El userspace de génesis crece de 5 a 6 apps.
|
||||
|
||||
Líneas abiertas posteriores: audio como capacidad de host; reciclado de las
|
||||
ranuras de ventana cerradas.
|
||||
## Fase 12 — la bocina del PC como capacidad de host (completada)
|
||||
|
||||
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
|
||||
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
/// El userspace de genesis — las seis aplicaciones que pueblan un disco recien
|
||||
/// forjado. El compas visual `pulso` (Fase 11), un saludo (`hola`), la
|
||||
/// `memoriosa` interactiva que recuerda entre sesiones (Fase 7c), y tres demos
|
||||
/// de los guardarrailes del kernel: `discola` (combustible), `glotona`
|
||||
/// (memoria) y `cronista` (la cronica de los arranques).
|
||||
const GENESIS: [AppGenesis; 6] = [
|
||||
/// El userspace de genesis — las siete aplicaciones que pueblan un disco recien
|
||||
/// forjado. La melodia visual `tonada` (Fase 12), el compas visual `pulso`
|
||||
/// (Fase 11), un saludo (`hola`), la `memoriosa` interactiva que recuerda entre
|
||||
/// sesiones (Fase 7c), y tres demos de los guardarrailes del kernel: `discola`
|
||||
/// (combustible), `glotona` (memoria) y `cronista` (la cronica de los arranques).
|
||||
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: "hola", archivo: "app.wasm", region: (100, 120, 480, 560) },
|
||||
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);
|
||||
// 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