Резидент: pop_redraw + холодная половина pop_level в банки (902 -> 3525 Б кучи)

MEM-COLD3, шаг 1-2 из плана разгрузки резидента:

* pop_redraw.c целиком -> банк 7 (к pop_room.c: он и есть единственный
  потребитель разбора пометок).  Два модуля в одном банке линкуются в один
  сегмент — проверено на .map (BANK7 7928 -> 8556).
* pop_level.c расколот по частоте вызова: горячая половина (чтение тайлов,
  связи, дверные таблицы, живые стражи — зовут все банки, местами на каждый
  тайл) осталась в резиденте, холодная (чтение файла уровня, разбор комнаты,
  рестарт, потабличные различия) уехала в pop_level_cold.c -> банк 8.
  Общее состояние объявлено в _pop_level.h: данные банковых модулей всё
  равно линкуются в общий _DATA, в банк уехал только КОД.

Побочно найден баг кодогена SDCC: `return 1;` из ветки не кладёт 1 в A —
до __banked он маскировался случайным ненулевым остатком в A.  Репро и
разбор — docs/bugs/sdcc-z80-ret-const-lost/, стаб t_char починен одним
выходом через переменную.  Аудит всех .asm roomtest: других мест нет.

_CODE 25628 -> 23005, куча 902 -> 3525 Б.  tests-host 6/6.
This commit is contained in:
2026-08-12 21:53:08 +03:00
parent f541c0aad9
commit 5d31ed086f
13 changed files with 884 additions and 426 deletions
+16 -16
View File
@@ -23,8 +23,8 @@
/* Загрузить уровень из файла в EMM-страницу. 0 — OK, -1 — ошибка
* (open/alloc/размер). Звать ПОСЛЕ первого atlas_load (pop_bg_load) —
* он снимает страницу ядра DSS для W0-unmap. */
int pop_level_load(const char *path);
void pop_level_free(void);
/* pop_level_load — внутренняя для pop_level_cold.c (грузим по НОМЕРУ) */
void pop_level_free(void) __banked;
/* ---- Номер уровня и потабличные различия (levels_plan.md §1) -------- */
@@ -38,23 +38,23 @@ extern uint8_t pop_current_level;
* pop_current_level. 0 — OK, -1 — файла нет / не влез (тогда
* pop_current_level и страница НЕ тронуты — играем дальше на старом).
* Порт связки load_lev_spr + load_level (seg000:1098/1169). */
int pop_level_load_num(uint8_t n);
int pop_level_load_num(uint8_t n) __banked;
/* tbl_entry_pose (data.h:848): 1 — падение внутрь (ур. 1), 2 — вбегание
* (ур. 13), 0 — разворот на месте. Индекс — номер уровня. */
uint8_t pop_level_entry_pose(uint8_t n);
uint8_t pop_level_entry_pose(uint8_t n) __banked;
/* tbl_guard_hp (data.h:846) — база HP стража на уровне (get_guard_hp,
* seg002:0044: extrastrength[skill] + tbl_guard_hp[level]). */
uint8_t pop_level_guard_hp(uint8_t n);
uint8_t pop_level_guard_hp(uint8_t n) __banked;
/* tbl_guard_type (data.h:844): 0 обычный, 1 толстый, 2 скелет, 3 визирь,
* 4 тень, −1 — стражей на уровне НЕТ (уровни 14/15). */
int8_t pop_level_guard_type(uint8_t n);
int8_t pop_level_guard_type(uint8_t n) __banked;
/* tbl_level_type (data.h:840): 0 = dungeon, 1 = palace. Пока читается
* только для контроля — второй тайлсет это levels_plan.md §3. */
uint8_t pop_level_tileset(uint8_t n);
uint8_t pop_level_tileset(uint8_t n) __banked;
/* Извлечь комнату room (1..24) в массивы приложения (W2):
* fg[30], bg[30] — тайлы комнаты (fg маскирован &0x1F, bg raw);
@@ -69,7 +69,7 @@ uint8_t pop_level_tileset(uint8_t n);
* 0 — OK, -1 — уровень не загружен / room вне диапазона. */
int pop_room_load(uint8_t room, uint8_t *fg, uint8_t *bg,
uint8_t *lcol_fg, uint8_t *lcol_bg,
uint8_t *rcol_fg, uint8_t *rcol_bg, uint8_t *below_fg);
uint8_t *rcol_fg, uint8_t *rcol_bg, uint8_t *below_fg) __banked;
/* Связь комнаты по стороне (0=L,1=R,2=U,3=D) → номер соседней (1..24) или
* 0, если стороны нет. Для переходов между комнатами (L2/L3). */
@@ -88,11 +88,11 @@ void pop_level_set_tile(uint8_t room, uint8_t tilepos, uint8_t tile);
/* Рестарт уровня: вернуть ВСЕ тайлы в исходное (порт load_level, который
* play_level зовёт на каждой итерации — в т.ч. после смерти Кида). */
void pop_level_reset_tiles(void);
void pop_level_reset_tiles(void) __banked;
/* Рестарт уровня: вернуть ВСЕХ стражей (тот же load_level + pos_guards).
* Убитый страж после respawn снова жив — как в оригинале. */
void pop_level_reset_guards(void);
void pop_level_reset_guards(void) __banked;
/* Батч-доступ к странице уровня (один map/unmap окна 0 на много чтений).
* Между begin/end — только pop_level_tile_raw, без рисования. */
@@ -115,7 +115,7 @@ void pop_doorlink2_set(uint8_t i, uint8_t v);
/* Ряд приземления падающего loose-куска в колонке col комнаты room: первый
* НЕ-пустой тайл сверху вниз; возвращает его ряд, если это floor (→ debris),
* иначе -1 (пролетел/крушение о стену). */
int8_t pop_room_col_landing(uint8_t room, uint8_t col);
int8_t pop_room_col_landing(uint8_t room, uint8_t col) __banked;
/* Стартовая позиция уровня (блок @2112): комната / поза / направление. */
/* Страж комнаты: 1 = есть (заполнит tile/dir/color/skill), 0 = нет.
@@ -133,17 +133,17 @@ uint16_t pop_guard_state_seq(uint8_t room);
uint8_t pop_guard_state_x(uint8_t room);
uint8_t pop_level_guard(uint8_t room, uint8_t *tile, int8_t *dir,
uint8_t *color, uint8_t *skill);
uint8_t *color, uint8_t *skill) __banked;
/* tbl_level_type (SDLPoP data.h:840) — какой тайлсет у уровня:
* 0 подземелье, 1 дворец. Дворцовые: 4, 5, 6, 10, 11, 14. Индекс —
* НОМЕР уровня; элемент 0 — демо-уровень. Оригинал по нему выбирает и
* .DAT окружения (tbl_envir_ki, seg000:1108), и десяток веток отрисовки
* в seg008, и графику стража (GUARD1/GUARD2). */
uint8_t pop_level_type(void);
uint8_t pop_level_type(void) __banked;
uint8_t pop_level_start_room(void);
uint8_t pop_level_start_pos(void);
int8_t pop_level_start_dir(void);
uint8_t pop_level_start_room(void) __banked;
uint8_t pop_level_start_pos(void) __banked;
int8_t pop_level_start_dir(void) __banked;
#endif