Звук: насос CBL качает через W3 — вход в BIOS ломал W0

СИМПТОМ: затемнение (fade) хрипело — одинаково с играющей музыкой и в
тишине.  Ключевое наблюдение пользователя: повтор тишины обязан звучать
тишиной, значит дело не в недоливе буфера.

ПОИСК: отладочные клавиши, каждая делала ровно один кусок fade.  Ожидание
кадров — чисто; чтение палитры, запись палитры и 512 вызовов
bios_get_place() (видео вообще не трогает) — скрежет во всех трёх.
Последнее и решило: виновата не палитра, а ЛЮБОЙ вызов BIOS.

ПРИЧИНА: `rst 8` раскрывается в `out ($7C),a`, который включает системное
ПЗУ и перестраивает окно 0 (MAME sprinter.cpp, update_memory: m_pages[0] +
m_bank_view0.select).  ПЗУ ложится ПОВЕРХ страничного регистра, поэтому
запись в порт 0x82 из прерывания бесполезна — OTIR вычитывает ПЗУ и
отдаёт его в звук.  Отсюда же старое правило «глушить CBL на время
загрузки файлов»: причина была не в том, что ESTEX долго занимает CPU.

РЕШЕНИЕ (идея пользователя): качать через W3.  Он управляется только
портом 0xE2, подмену из прерывания никто не перекрывает, а BIOS во время
нашего ISR не исполняется — окно возвращается до выхода, и для него
подмена невидима.  После этого BIOS безопасен везде.

* pop_sfx.c — насос берёт взаймы W3 вместо W0, чтение по 0xC000 + смещение.
* pop_ui.c — буфер палитры по фиксированному 0x4000 (эти 256 байт DSS
  занимает только при загрузке программы): 256 байт со стека долой.
* libbgi/common/gfx_pal_write.c — запись палитры прямо в видеопамять,
  минуя BIOS.  Писалась как обход скрежета, после переноса насоса не
  нужна; оставлена как более быстрый примитив (2,5 тыс. тактов на 64
  цвета против 10,8 тыс. у BIOS) с честной шапкой.  Адресация разобрана
  по исходникам BIOS (FUNC_SCREEN.ASM): Port_Y = индекс цвета, адрес
  0xC3E0 + pal*4, порядок R/G/B/Y.
* libc/video/pal_get.c, pal_load.c — в шапках зафиксировано, что BIOS
  выбирает окно ПО АДРЕСУ БУФЕРА (`BIT 7,H`).
* pop_intro.c — при пропуске интро клавишей не глушился CBL, и следующая
  загрузка уровня шла с открытым буфером; добавлен pop_sfx_pause.
* pop_ctrl.c — убраны отладочные «осторожные шаги» на J/L (эмуляция
  Shift+стрелка для MAME), у них и стоял TODO.

Разбор целиком — docs/sound_plan.md §5.  Бюджет: _CODE 23966, куча 245 Б.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 19:36:47 +03:00
parent 4327ac88b9
commit 16d3262340
10 changed files with 190 additions and 25 deletions
+16 -5
View File
@@ -28,6 +28,12 @@ static const pop_font_desc_t font_desc[] = {
static atlas_t font_at;
static uint8_t font_ready;
/* БУФЕР ПАЛИТРЫ ПО ФИКСИРОВАННОМУ АДРЕСУ 0x4000. DSS занимает эти 256
* байт только при загрузке программы, дальше они наши (код начинается с
* 0x4100), и держать буфер здесь дешевле, чем класть 256 байт на стек в
* каждом вызове — идея пользователя, 2026-08-25. */
__at (0x4000) uint8_t pop_pal_buf[256];
static uint8_t text_index(const pop_font_desc_t *font, uint8_t ch)
{
if (ch < POP_FONT_FIRST || ch > POP_FONT_LAST) ch = '?';
@@ -127,14 +133,18 @@ static void snapshot_chunk(uint8_t chunk, uint8_t *buf, uint8_t store)
gfx_w0_unmap();
}
/* Снимок палитры целиком: дробить на мелкие порции больше не нужно —
* причина скрежета была не в длительности BIOS-вызова, а в том, какое окно
* он под себя забирал (см. pop_pal_buf выше). */
void pop_ui_palette_snapshot(void) __banked
{
uint8_t chunk, buf[256];
uint8_t chunk;
if (!font_ready) return;
for (chunk = 0; chunk < 4; chunk++) {
gfx_pal_get(0, (uint8_t)(chunk << 6), 64, buf);
snapshot_chunk(chunk, buf, 1);
gfx_pal_get(0, (uint8_t)(chunk << 6), 64, pop_pal_buf);
snapshot_chunk(chunk, pop_pal_buf, 1);
}
}
@@ -143,7 +153,8 @@ void pop_ui_palette_snapshot(void) __banked
* в постоянно вызываемый (во время fade) путь. */
void pop_ui_palette_dim(uint8_t dim_step, uint8_t keep_ui) __banked
{
uint8_t chunk, i, start, buf[256];
uint8_t chunk, i, start;
uint8_t *buf = pop_pal_buf;
if (!font_ready) return;
if (dim_step > 4) dim_step = 4;
@@ -184,7 +195,7 @@ void pop_ui_palette_black(void) __banked
{
uint8_t chunk;
uint16_t i;
uint8_t black[256];
uint8_t *black = pop_pal_buf;
/* BIOS принимает максимум 64 записи за вызов; одним буфером покрываем
* все четыре четверти палитры, не занимая постоянную память. */