Files
Sprinter-SDCC/libc/cbl/cbl_shutdown.c
T
snark13 8490288d79 libc/crt0: возврат из main завершает программу по-настоящему
Два бага одного пути завершения, оба видны только на железе.

1. ЗВУК ПРОДОЛЖАЛСЯ ПОСЛЕ ВЫХОДА.  cbl_close() закрывает СЕССИЮ, но не
гасит железо: bit7 порта 0x004E держит gfx_wait_vsync ради бита луча,
поэтому порт оставался включённым ("немой" режим), и CBL крутил свои 256
слотов уже под шеллом — тихо ровно до первой чужой записи в порт данных,
а дальше она зацикливалась.  Новый cbl_shutdown() гасит bit7 независимо
от держателей и центрует ЦАП обычного COVOX; pop_shutdown зовёт его
последним действием, а _cbl_open_raw регистрирует в atexit его, а не
cbl_close.

2. ЦЕПОЧКА atexit НЕ ВЫПОЛНЯЛАСЬ ПРИ ВОЗВРАТЕ ИЗ main.  crt0 уходил прямо
в ESTEX EXIT, то есть нарушал контракт C (возврат из main = exit(status)).
Молча терялись не только гашение звука и снятие vsync-ссылки, но и
_fclosall: буферизованная запись в файлы пропадала, если программа не
звала exit() явно.  Теперь crt0 после main дёргает _atexit_hook.

Косвенность обязательна: прямая ссылка crt0 на разматыватель притащила бы
его и стек хендлеров в КАЖДУЮ программу.  Указатель живёт в отдельном
data-модуле (два байта _DATA, ни байта кода), ставит его сам atexit() при
первой регистрации — нет регистраций, нет и кода.  Тот же приём, что у
_irq_cbl_hook.

Цена замерена: +14 Б всем программам (блок в crt0) и +64 Б тем
одиннадцати, что реально регистрируют хендлеры (CBL, файловые через
_fclosall, irqtest, gfx_dbuf, solidt) — у них раньше эти хендлеры были
мёртвым кодом.  Эталоны обновлены (кроме atlas: его +434 Б не отсюда,
замерен тот же и без этих правок).

Проверено в MAME: старт и звук как были, выход по F10 возвращает в шелл
чисто (текстовый режим восстановлен, зависания нет), повторный запуск
работает.  Пункт 1 проверяется только на железе.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DSYUpuaQpKr48kBav2iiV4
2026-09-02 14:15:48 +03:00

39 lines
2.7 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.
/*
* cbl_shutdown — ЖЁСТКО погасить звук перед возвратом в шелл.
*
* ЧЕМ ОТЛИЧАЕТСЯ ОТ cbl_close(). Тот закрывает СЕССИЮ: снимает насос и
* пересчитывает порт по текущим держателям. А держатель обычно есть —
* gfx_wait_vsync() занимает bit7 навсегда ради бита луча (см. _cbl_port.c),
* — и порт остаётся включённым в "немом" режиме. Для работающей программы
* это правильно, но при выходе означает, что железо продолжает крутить свои
* 256 слотов уже под шеллом: тихо ровно до первой чужой записи в порт
* данных, а дальше эта запись зацикливается навсегда.
*
* ПОЧЕМУ НЕЛЬЗЯ ПОЛОЖИТЬСЯ НА atexit. Цепочку atexit разматывает только
* exit() (libc/sys/exit.c), а crt0 при ВОЗВРАТЕ ИЗ main уходит прямо в
* ESTEX EXIT (runtime/crt0_banked.s) — ни один хендлер не выполняется.
* Значит и снятие vsync-ссылки, и cbl_close из atexit при обычном
* `return 0;` из main не происходят. Поэтому выключение должно быть
* ЯВНЫМ действием программы, а не следствием чужой уборки.
*
* Что делает: закрывает сессию (если была), обнуляет счётчик держателей —
* программа уходит, держать порт больше некому, — гасит bit7 и центрует
* ЦАП обычного COVOX (при выключенном CBL записи в порт данных идут прямо
* в него, см. _cbl_prime.c), чтобы вместо щелчка осталась ровная тишина.
*
* Идемпотентно; звать последним действием завершения.
*/
#include "_cbl.h"
#include "../irq/_irq.h" /* IRQ_DISABLE/IRQ_ENABLE */
void cbl_shutdown(void)
{
cbl_close(); /* снять насос и отпустить IM2-таблицу */
IRQ_DISABLE();
_cbl_port_refs = 0; /* держателей больше нет: программа уходит */
_cbl_ctrl(0); /* bit7 = 0 — CBL выключен целиком */
_cbl_prime(0x80); /* ...и ЦАП COVOX в центре, без щелчка */
IRQ_ENABLE();
}