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
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include "pop_ctrl.h" /* pop_ctrl_init/close — ввод поднимает и закрывает boot */
|
||||
#include "pop_shadow.h" /* запечённый набор Тени */
|
||||
#include "pop_sfx.h" /* звуковые эффекты через CBL */
|
||||
#include <cbl.h> /* cbl_shutdown — погасить железо звука на выходе */
|
||||
#include "pop_music.h" /* pop_music_busy/free — ждём трек заставки */
|
||||
#include "pop_vflip.h" /* сброс зеркальных страниц на смене уровня */
|
||||
#include "pop_cheat.h" /* pop_cheats — режим разработки включает pop_boot */
|
||||
@@ -1490,4 +1491,14 @@ void pop_shutdown(void) __banked
|
||||
pop_guard_free();
|
||||
pop_kid_data_free();
|
||||
pop_level_free();
|
||||
/* ЖЕЛЕЗО ЗВУКА ГАСИМ ПОСЛЕДНИМ ДЕЙСТВИЕМ, и pop_sfx_close выше для этого
|
||||
* НЕ ДОСТАТОЧНО: он закрывает сессию, а bit7 порта CBL остаётся включён,
|
||||
* пока его держит gfx_wait_vsync ради бита луча. Дальше main просто
|
||||
* возвращается, а crt0 при возврате уходит прямо в ESTEX EXIT, минуя
|
||||
* цепочку atexit (runtime/crt0_banked.s) — то есть vsync-ссылку снять
|
||||
* уже некому. В результате мы уходили в шелл с включённым CBL, и он
|
||||
* крутил свои 256 слотов: тихо до первой чужой записи в порт данных, а
|
||||
* дальше она зацикливалась (поймано пользователем на железе 2026-09-02;
|
||||
* в MAME не воспроизводится). */
|
||||
cbl_shutdown();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user