6176f6da3198474ebb7e089c912e4cab952fb4b3
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4e43890fce |
SprPoP: финал больше не убивает программу — прямой вызов в чужой банк
Пройденная игра доходила до таблицы рекордов и умирала: программа
исчезала, машина следом вставала намертво (di;halt на 0x0000) либо уходила
в reset. Одинаково из Flex Navigator и из голого DSS.
КОРЕНЬ. pop_ui.h объявлял группу pop_text_*_mapped БЕЗ __banked. Пока
pop_hof.c лежал в банке 9 рядом с pop_ui.c, прямой call был верен; после
переноса pop_hof/pop_config/pop_pal в банк 10 тот же call стал уходить в
пустой хвост чужого банка. Процессор полз по 0xFF до 0x0000, где ловушка
DSS ставит B=0x27 и сворачивает процесс — подмена страниц W1/W2/W3,
которую было видно на трупе, оказалась уборкой, а не причиной.
Точную инструкцию (call $E503 = _pop_text_map банка 9) дала трассировка
MAME на узком участке: trace включалась брейкпоинтом на входе в
pop_hof_show и выключалась на процедуре завершения процесса DSS (0x1E56).
ЧТО СДЕЛАНО
* pop_ui.h/.c — группа text_*_mapped помечена __banked.
* toolchain/check_bank_calls.py — две проверки банкового кода:
1) прямой call в чужой банк (доказательна, ВАЛИТ сборку — проверено
намеренной поломкой);
2) указатель на данные своего банка, отданный в чужой (эвристика по
форме кода, только предупреждает).
Встроена в app.mk, запускается сразу после линковки.
* pop_hof.c — курсор ввода строится на стеке: литерал "_" лежал в _BANK10
и после пометки __banked уезжал из-под ног чужому банку, заливая экран
знаками вопроса.
* libc: kbd_raw_keypad_as_ext() — kbd_raw_sync переносит голые коды
нумпада в EXT-половину карты. Лечит залипание стрелок (потерянный
префикс E0 сажал make в PLAIN как код нумпада, и снять его было нечем),
заодно нумпад стал управлением: 7/8/9, 4/6, 2 и 5 = вниз.
* pop_pace.c — цикл ожидания луча зовёт тот же idle-хук, что и
gfx_wait_vsync: без этого F10 в геймплее не работал вовсе.
* pop_hof.c — Esc в таблице рекордов отменяет запись (расхождение с
оригиналом записано в docs/impl_diff.md).
* Экран версии показывается только через Menu/Settings/About: стартовый
показ и Ctrl+V убраны, мёртвый код снят.
* sprpop_cold.c — pop_start_level зовёт pop_hp_invalidate: после Ctrl+A с
выросшим за уровень максимумом полоса HP моргала между страницами.
Разбор всех четырёх багов — в applications/PoP/roomtest/BUGS_CLOSED.md
(FINAL-BANKCALL, FINAL-HOF-GARBAGE, KBD-ARROW-PHANTOM, F10-GAMEPLAY),
правило про банки — в applications/SprPoP/CLAUDE.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
47a4b084c4 |
Меню, статус-строка и оболочка игры: title/intro/cutscene/HoF, POP.CFG, палитры
Эта сессия (меню + текст в служебных полосах):
* Меню: рамка выделения считается от силуэта текста (SEL_PAD сверху и
снизу), а не «на глаз»; все экраны центрируются в игровых 200 строках
(UI_CENTER_TOP/UI_CENTER_FIELD) и в них помещаются; Enter и Space —
равноправные клавиши действия (ui_action_down).
* Settings: убраны SHOW SPRINTER SCREEN и BACK, добавлены DEBUG BAR и
ABOUT. About показывает тот же текст, что стартовый Sprinter screen,
минус строка про клавиши — общий about_text(), чтобы экраны не
разъехались. CONTROLS собирается таблицей и центрируется по
фактическому числу строк.
* GAME PAUSED переехала в нижнюю статус-строку, как в оригинале
(SDLPoP rect_bottom_text = {193,70,202,250}): это состояние программы,
а не пункт меню. POP_HP_Y вынесен в pop_cdraw.h — полосу делят два
модуля.
* pop_status.c (банк 9) — текст в обеих служебных полосах. Нижняя:
порт display_text_bottom + таймера (QUICKSAVE/QUICKLOAD/SOUND ON/OFF,
24 тика). Верхняя отладочная переведена с палочек на текст
«Level ##, Room ##, Speed: …, Sound: …, Immortal #» малым шрифтом, с
своим форматированием чисел (без printf и без деления).
Заявка сообщения — запись одного байта pop_status_msg: резидент W1/W2
не растёт, весь рендер в банке. Бюджет после правок не изменился
(_CODE 23981, куча 267 Б).
* Цена вывода: блит глифа ~4,6 тыс. тактов независимо от размера, поэтому
всё change-driven, отладочная строка перерисовывается ПО ПОЛЯМ, пробелы
не блитятся вовсе, а вход в комнату заливает только игровое поле
(pop_screen_fill_field) — борта от комнаты к комнате не меняются.
* Интро: в PV-сцене зазвучали пропавшие эффекты оригинала — закрытие
ворот (4) и открытие двери покоев (51), из которой входит Джафар.
* docs/status_line_text.md — полная инвентаризация ВСЕХ текстов SDLPoP в
статус-строке: геометрия, семантика text_time_total как идентификатора
сообщения, мигание, рестарт по истечении 36/288.
Вместе с этим выкладывается накопленная работа по оболочке полной игры:
автомат состояний (pop_app), title, intro/PV и cutscene, attract-demo,
Hall of Fame, глобальный таймер, настройки и POP.CFG, модуль палитр и
fade, звуковой набор, host-тесты на новые швы.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
6673279cef |
PoP: скелет уровня 3, цвета стражей, окклюзия соперника; кэш соседних комнат
Скелет (L3-SKEL, ассеты + механика): - pop_pack_guard.py получил параметр набора (GUARD/SKEL): атлас скелета poc/res/skel/g0..g3.atl (28 кадров), палитра — из его res750.pal (на ур. 3 curr_guard_color = 0, оригинал палитру не подменяет); - pop_guard_load выбирает набор по tbl_guard_type и перезагружается ПРИ СМЕНЕ УРОВНЯ (load_lev_spr, seg000:1092) — без этого скелет рисовался атласом стража и был невидим; - load_frame: charid_4_skeleton идёт по таблице стража (seg006:529), тень — только в кадрах 150..189. Пока ветка была одна (charid_2_guard), скелет получал image из таблицы Кида (180 при 28 спрайтах) и не рисовался; - check_skel (seg002:1042), ветка charid_4 в enter_guard, возрождение в комнате 3 при падении (seg002:252), autocontrol_skeleton; - leveldoor_open (seg007:456) — новый флаг, сбрасывается стартом уровня. Цвета стражей (BUG-GUARD-COLOR-1, закрыт): - все 7 палитр res10.bin -> pop_guard_pal.h, заливка 16 слотов по guards_color комнаты перед отрисовкой (set_chtab_palette, seg003:257). Проверено в MAME: ур. 2 комн. 11 = цвет 1, комн. 7 = цвет 3, полоса HP меняется вместе со стражем. Грабля: gfx_pal_load отдаёт указатель в BIOS, а тот читает только #4000-#BFFF — таблицу из банка копируем в стек. Кэш соседних комнат (BUG-SWORD-GHOST-1, закрыт): - pop_map кэширует fg соседей слева/справа ЦЕЛИКОМ и резолвит col -10..19. Было -2..11, дальше мнимая стена: луч видимости упирался в неё (страж после follow_guard в col 12), Кид прятал меч посреди боя и не мог достать обратно. +48 байт W2. Окклюзия соперника: - pop_fore_over_char получил проход other_overlay_tile (порядок midtable, seg008:1B06) и расширение перебора объединённым прямоугольником «персонаж + клинок + брызги» — падающий скелет больше не рисуется поверх кладки и верхней грани пола; - клип полем 192 строк (reset_obj_clip, seg006:0507) для спрайта, клинка (общий pop_sword_draw) и брызг — спрайт не залезает на полосу HP; - ROOMNAV после смерти Кида делает честный pop_start_level: телепорт «оживлял» мёртвого мимо старта уровня, оставляя живого скелета рядом с вернувшейся кучей костей. Ассеты чомпера (под L3-CHOMP): весь набор кадров в атласе явным списком (101-105 низ, 111-113 верх, 106-110 фронт, 114-123 кровь mono-силуэтом) — render_room анимированные тайлы пропускает, и в атласе не было ни одного. Число EMM-страниц не изменилось. Тесты: tests-host все 5 наборов зелёные, t_char вырос до 65 проверок (резолв колонок за краем комнаты, возрождение скелета); в testkit добавлен гард «код наехал на данные» (DATA_LOC). Доски: TASKS.md разнесён на TASKS_OPEN/TASKS_CLOSED, закрытые баги с разбором корней — в bug_closed.md; заведены DRAW-CHAR (отрисовка одна на всех Char, как физика после GUARD-PHYS) и L3-COLOR (зелёная кладка уровня 3: level_var_palettes = ресурс 20, есть в MSDOS/PRINCE.DAT). В roomtest.c временно оставлен автостоп на падении соперника (отладка падений скелета) — помечен ВРЕМЕННО. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
030af74631 |
BUG-KBD-5: зажатый Shift снимался автоповтором стрелки
Симптом: Shift работал в одиночку и НЕ работал вместе со стрелками — прыжок с зацепом не выходил (BUG-GRAB-1), а осторожный шаг срывался в бег. Декодеры делали из «fake shift» два вывода, и второй был неверен: обёртка E0 F0 12 / E0 12 есть -> Shift зажат -> взвести бит — верно; расширенный make БЕЗ обёртки -> Shift отпущен -> снять бит — НЕТ. Замер потока байт (MAME, breakpoint на выходе из in a,($18)): клавиатура pc_kbd ms_naturl обёртку не шлёт вовсе — при зажатом Shift поток на ↑ ровно `E0 75 E0 75 …`, ни одного F0/12. А typematic-повторы идут непрерывно, пока стрелка зажата, значит каждый повтор снимал реально зажатый Shift. Короткий тап это маскировал: после отпускания стрелки Shift снова становился последней клавишей, и его собственный автоповтор `12` взводил бит обратно за ~30 мс. Фикс: обратный вывод убран, расширенная клавиша о Shift не судит. Состояние Shift ведут его собственные make/break 12 / F0 12 — они приходят всегда. Прямой вывод оставлен (дёшев и верен там, где обёртка есть). Ушла ставшая ненужной _kbdraw_fakesh; трамплин короче на 36 Б (0x150→0x12C), что важно — его клавиатурный блок упирается в диапазон jr. Плата: потерянный при overrun break Shift снять нечем, модификатор может залипнуть до перенажатия (BUG-KBD-3). Размен решён как и раньше в kbd_raw_sync: лучше залипание, чем отвал — сорванный посреди игры Shift в PoP стоит жизни. Проверено в MAME чтением _kbdraw_down: Shift+↑+→ зажаты 5 с (автоповтор идёт) -> LSh остаётся 04; отпускание Shift -> 00. Зацеп в игре подтверждён пользователем. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1f16e8fa70 |
KBD: состояние Shift по «fake shift» — ни залипания, ни отвала
Клавиатура PS/2 обёртывает КАЖДЫЙ расширенный код парой E0 F0 12 / E0 12,
пока реально зажат Shift (замер на железе/MAME: правый шифт обёртывается
своим кодом 0x59). Это прямое и непрерывное свидетельство состояния
Shift — единственное доступное, потому что опросить PS/2 нельзя, а
typematic повторяет последнюю нажатую клавишу, то есть стрелку.
Оба декодера (_irq_tramp.c, kbd_raw_poll.c) читают обёртку в обе стороны:
обёртка есть -> Shift взвести; расширенный make без обёртки -> Shift
снять. Бит взводит общий писатель — достаточно обнулить префиксы, и код
уходит в plain-половину карты как make.
Это снимает размен, между крайностями которого мы метались:
- исключать модификаторы из сброса по overrun -> Shift залипал навсегда
(BUG-KBD-3);
- сбрасывать всю карту, как DSS -> Shift сносился каждым overrun'ом, а
при зажатом Shift тап стрелки это 10 байт в 3-байтовый FIFO, то есть
overrun почти гарантирован (BUG-KBD-4).
Теперь kbd_raw_sync снова не трогает модификаторы, и это безопасно:
залипание снимается первым же нажатием стрелки.
Раскладка трамплина: клавиатурный блок перевалил за 127 байт, а jp внутри
запрещён (копия в W2). Префиксные обработчики переехали вплотную к своим
cp, посередине тела стоят ретрансляторы tr_kbd_hub/tr_hub_notkbd/
tr_hub_dss. В kbd_raw_poll такого ограничения нет — там три jp.
Проверено в MAME: Shift переживает пять тапов подряд; штатное отпускание
снимает; искусственно залипший бит снимается первым тапом.
make size-check — роста нет.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b56f2b4582 |
libc/kbd: kbd_raw_poll + замер потери нажатий при зажатом Shift (KBD-1)
Симптом: при удерживаемом Shift часть нажатий стрелок не отрабатывает
(~15 % по наблюдению пользователя), без Shift потерь нет.
Переведено в числа: нажимается Home — тоже расширенная клавиша (тот же
E0-префикс и тот же «fake shift»), но игрой игнорируется, поэтому рельеф
комнаты на результат не влияет. Счётчики — брейкпоинты MAME с действием
{ b@ADDR = b@ADDR+1 ; g } на входе клавиатурной ветки трамплина, на чтении
порта 0x18 и на установке make-бита.
Что измерено (10 нажатий Shift+Home, дошло make):
игра идёт, опрос ВКЛ 9/10 игра идёт, опрос ВЫКЛ 9/10
игра ЗАМОРОЖЕНА (блитов нет вообще, длинных DI нет) 8/10
Обе исходные гипотезы отпали:
- длина наших DI-окон ни при чём (в замороженном кадре потерь больше);
- снятие di в accel-ядрах libbgi УРОНИЛО машину — режим «акселератор при
EI» из docs/new/06-accel.md §6.6 в этой прошивке недоступен.
Байт теряется НИЖЕ нашего кода: на 49 прочитанных байт пришлось только 28
входов в клавиатурную ветку, то есть ~44 % импульсов запроса прерывания не
обслуживается и трёхбайтовый FIFO SIO переполняется.
Потолок приёма измерен отдельной программой tests/kbdpoll (ничего, кроме
kbd_raw_poll в цикле): 25 нажатий -> 25 make, 250 байт из 250, ноль потерь.
Значит опрос лечит полностью, вопрос только в плотности: нужно раз в
~0.5 мс, а шесть вызовов за 60-мс кадр давали раз в 10 мс.
Поэтому вызовы из roomtest.c УБРАНЫ (они стояли там, где прерывания и так
разрешены, и дублировали трамплин — 9/10 с ними и без). Сама функция
kbd_raw_poll оставлена в libc: она корректна и нужна как основа плотного
опроса. В заголовке и в libc-reference — честная оговорка, чтобы её не
ставили в игровой цикл «на всякий случай» без замера.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
0280b05933 |
libc/kbd: held-карта клавиш в биты (512 -> 64 Б); эталон размеров принят
Разгрузка W1/W2 под будущий ИИ стражей: _kbdraw_down был БАЙТОМ на
скан-код (512 Б в _DATA при 32-килобайтной раскладке). Теперь бит на
код: код>>3 = байт, код&7 = бит, расширенные (префикс 0xE0) — смещение
+32 байта вместо +256.
Трамплин прерывания строит маску СДВИГОМ, а не таблицей: таблица
потребовала бы `ld hl,#метка` внутри трамплина, а он копируется в W2
побайтно и обязан быть без абсолютных само-ссылок (см. его шапку).
Маска строится в BC, поэтому в клавиатурной ветке добавлен push/pop bc.
Трамплин вырос 244 -> 267 Б, буфер копии поднят 320 -> 336 (запас 69 Б).
Проверено в MAME на roomtest, все три класса клавиш:
- обычные: '=' (обход комнат) и 'K' (чит-убийство стража — читал
guardhp_curr/delta: 3/0 -> 0/-3);
- расширенные (E0): стрелка вправо — Kid добежал до края комнаты;
- модификаторы: удержание Shift ставит бит 2 байта 2 карты
(скан-код 0x12), отпускание снимает.
Скорость: кадр 334 716 -> 338 508 тактов (+1.1 %) на битовой арифметике
в kbd_raw_down (~15 вызовов за кадр); при бюджете 430 000 это 0.79
периода вместо 0.78 — регрессии нет.
Итог по roomtest: данные 4422 -> 4022 Б, куча W2 996 -> 1294 Б.
Эталон размеров принят заново (make size-baseline): _CODE десяти
программ вырос на 14-23 Б — это код битовой арифметики в трамплине и
kbd_raw_down, обмен на -448 Б данных, которые size_check не считает.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3d586af031 |
libc/kbd: raw-клавиатура — вычерпывание FIFO + селективный wipe модификаторов
- kbd_raw_sync: цикл вычерпывания SIO FIFO (не 1 байт/прерывание) — фикс залипания клавиш; overrun-wipe сбрасывает только пострадавшие клавиши, не модификаторы (typematic их не перечитывает). - Гайд docs/kbd-games.md; заметка о Rx-overrun в docs/TODO.md; справочник скан-кодов в docs/libc-reference.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
3c0baacbf6 |
libc/kbd: recovery по Rx-overrun SIO (залипание клавиш) + kbd_raw_sync
Симптом (интермиттентный): при отпускании shift+стрелка иногда стрелка залипает. Диагностика: на чистом одновременном release break-коды обрабатываются верно (проверено MCP) → drain-логика ISR корректна. Остаточное залипание = переполнение 3-байтного аппаратного FIFO SIO при пачке скан-кодов (F0 12 E0 F0 74 = 5 байт) во время длинных DI-окон → потерян break → залипание. Фикс: трамплин после drain читает RR1 SIO (бит5 = Rx Overrun), при overrun делает Error Reset (WR0=0x30) и взводит _kbdraw_overrun. Новый kbd_raw_sync() (звать раз в кадр) по флагу сбрасывает всё held-состояние _kbdraw_down (какой break потерян — неизвестно; реально зажатые перечитаются). pop_ctrl_tick зовёт kbd_raw_sync(). Буфер W2- трамплина 288→320 (трамплин 244 Б). ВНИМАНИЕ: путь overrun НЕ проверен детерминированно (баг интермиттентный, зависит от тайминга DI) — ТРЕБУЕТ ПРОВЕРКИ на железе/в длинной сессии. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
484b18d10c |
libc+libbgi: raw-клавиатура (held-state) + column-major блит спрайтов
libc/kbd: kbd_raw_open/close/down — сырой PS/2-канал клавиатуры с held-state (битовая карта _kbdraw_down[512], EXT-клавиши +256). Пока raw открыт, кадровый IRQ-трамплин перехватывает байт SIO у DSS и декодирует make/break (0xF0/0xE0-префиксы) сам. FIFO вычерпывается В ЦИКЛЕ (приёмный буфер SIO 3 байта; пачка break-кодов при одновременном отпускании иначе теряется → залипание клавиши). Буфер W2-трамплина поднят 224→288 Б под выросший обработчик. libc/conio: kbd_mod_state() — live-состояние модификаторов (ESTEX CTRLKEY $33h), Shift/Ctrl/Alt/Lock прямо сейчас, KBD_MOD_* маска. libbgi: gfx_blit_cols(x,y,img,flip) + _bgi_blit_cols_raw — блит column-major спрайта вертикальным accel-проходом, бесплатный горизонтальный флип (sstride<0), клип по экрану. Для персонажей. libbgi/atlas_load: восстанавливать W3 ДО записи a->count (atlas_t в --bank памяти резолвится через W3; count оставался мусором). tests/kbdraw — тест raw-клавиатуры; size-baseline +kbdraw. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |