8490288d79
Два бага одного пути завершения, оба видны только на железе.
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
27 lines
1.3 KiB
C
27 lines
1.3 KiB
C
/*
|
|
* atexit — зарегистрировать функцию, вызываемую при нормальном
|
|
* завершении (exit()), максимум 8, порядок LIFO. Возвращает 0 или -1
|
|
* при переполнении стека хендлеров.
|
|
*
|
|
* ВОЗВРАТ ИЗ main ТОЖЕ РАЗМАТЫВАЕТ ЦЕПОЧКУ (с 2026-09-02), как и требует
|
|
* стандарт C. Раньше crt0 уходил из main прямо в ESTEX EXIT, и хендлеры
|
|
* не выполнялись: программа теряла и сброс файловых буферов (_fclosall), и
|
|
* гашение звука, если не звала exit() явно. Теперь первая регистрация
|
|
* ставит _atexit_hook, а crt0 его дёргает — см. _atexit_hook.c.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "_atexit.h"
|
|
|
|
int atexit(void (*fn)(void))
|
|
{
|
|
if (_atexit_top >= ATEXIT_MAX) {
|
|
return -1;
|
|
}
|
|
_atexit_stack[_atexit_top++] = fn;
|
|
/* Хендлеры появились — значит crt0 обязан размотать цепочку и при
|
|
* простом `return` из main. Ставим каждый раз: проверка дороже. */
|
|
_atexit_hook = _atexit_run;
|
|
return 0;
|
|
}
|