Files
Sprinter-SDCC/libbgi/common/gfx_pal_write.c
T
snark13 16d3262340 Звук: насос 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>
2026-08-25 19:36:47 +03:00

52 lines
2.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* gfx_pal_write — записать блок палитровых записей ПРЯМО в видеопамять,
* минуя BIOS $A4.
*
* КОГДА НУЖНА. Штатный gfx_pal_load идёт через BIOS, а вход в BIOS
* (`rst 8` = `out ($7C),a`) включает системное ПЗУ и перестраивает окно 0.
* Пока это так, любой код, читающий данные ИЗ W0 в прерывании, получит
* вместо них ПЗУ. В PoP на этом рвался звук, пока насос CBL брал взаймы
* W0; после переноса насоса на W3 (см. pop_sfx.c) BIOS снова безопасен, и
* в самой игре этот примитив не нужен.
*
* Остаётся полезен там, где BIOS звать нельзя по другим причинам, и просто
* как более быстрый путь: цикл ниже кладёт 64 цвета примерно за 2,5 тыс.
* тактов против 10,8 тыс. у BIOS (замеры в MAME, 2026-08-25).
*
* КАК АДРЕСУЕТСЯ ПАЛИТРА (исходник BIOS, FUNC_SCREEN.ASM, PIC_SET_PAL).
* Port_Y выбирает ИНДЕКС ЦВЕТА и инкрементируется на каждый цвет; адрес
* записи — 0xC3E0 + pal*4, внутри четвёрки порядок R, G, B, Y, а на входе
* данные лежат как B, G, R, Y. Окно на время работы держит страницу 0x50.
*/
#include "../_bgi.h"
#include <sprinter.h>
/* Port_Y (0x89) как SFR: у libc готового объявления нет, а держать здесь
* asm-вставку ради одного OUT дороже, чем одна строка. */
__sfr __at PORT_RGADR _io_port_y;
/* Палитровые байты начинаются в килобайтной строке со смещения 0x3E0. */
#define PAL_SLOT0 0xC3E0u
void gfx_pal_write(uint8_t pal_num, uint8_t start, uint8_t count,
const uint8_t *bgr0)
{
uint8_t *slot = (uint8_t *)(PAL_SLOT0 + ((uint16_t)pal_num << 2));
uint8_t saved_bank = _gfx_bank;
uint8_t y = start;
/* Писать обязательно ОБЫЧНЫМ банком: в прозрачном (0x5C) байт 0xFF не
* записывается, и белые компоненты палитры молча терялись бы. */
_gfx_bank = GFX_BANK_NORMAL;
_bgi_begin();
do { /* count == 0 означает 256 цветов */
_io_port_y = y++;
slot[2] = *bgr0++; /* B */
slot[1] = *bgr0++; /* G */
slot[0] = *bgr0++; /* R */
slot[3] = *bgr0++; /* Y */
} while (--count);
_bgi_end();
_gfx_bank = saved_bank;
}