1e6c377edc
Резидента --w3 больше нет: отрисовка (pop_bg + pop_gdraw) уехала в БАНК 2, физика/коллизия (pop_map) — в БАНК 3. Куча W1/W2 1294 -> 6750 Б. Что это разблокировало. Резидент был тупиком: из банка он недостижим ни прямо, ни транзитивно, поэтому pop_map (самый крупный модуль, 5.8 КБ) в банк было не увести — он зовёт mob-отрисовку. Проверено, что банк->банк РАБОТАЕТ: ___sdcc_bcall_ehl читает страницу окна портом 0xE2 и кладёт её на СТЕК своего кадра (runtime/bank.s), поэтому вложенность корректна по построению. Подтверждено в MAME цепочкой W1 -> банк1 -> банк2 -> банк1: nested=124 after=8, ровно ожидаемое. Значит развязка mob'а (самое рисковое место, loose-полы) НЕ понадобилась — pop_map зовёт pop_bg трамплином. Правила вызовов проверены на сгенерированном asm и записаны в memory sdcc_banked_call_rules: трамплин выбирает ОБЪЯВЛЕНИЕ (__banked), а не раскладка — даже внутри одного .c между __banked функциями он есть. Внутрибанковые функции оставлены непомеченными и зовутся напрямую, в т.ч. через границу файла (pop_gdraw -> pop_fore_over_char). sprinter-cc: --dataseg BANKn БОЛЬШЕ НЕ ставится по умолчанию. Раньше вся писучая память банкового модуля уезжала в страницу банка и снаружи не читалась (проверено на .map: глобал лёг по 0x0001C000) — грабли на каждом переносе. Теперь данные банков по умолчанию в общем _DATA (W1/W2, замаплен всегда), а прежнее поведение — по явному --bank-data. Замеры (комната 3, Kid + страж; кадр Sprinter в турбо = 430 000 тактов): до переноса 338 508 (0.79 кадра) + pop_bg в банк 2 347 100 (+2.5 %) + pop_map в банк 3 371 100 (+9.6 % к исходному, 0.86 кадра) Плата — трамплины (~654 такта на вызов, ~30 вызовов за кадр). При пейсинге в 3 кадра это 29 % логического кадра, но запас до ОДНОГО кадра всего ~59 000 тактов — под звук его надо возвращать (следующий шаг: батчить кроссбанковые вызовы, начиная с pop_redraw_needed). Профилирование бордюром включено по умолчанию (make PROF=0 выключает) и переведено на реально работающие биты: бит 0 (красный) у бордюра Sprinter ИГНОРИРУЕТСЯ, поэтому различимых состояний четыре и значения обязаны быть чётными — 0 чёрный (ждём vsync), 2 синий (логика), 4 зелёный (фон), 6 циан (спрайты). Раньше нечётные номера сливались и полосы не читались. Проверено в MAME: комнаты 1/3 рисуются как прежде, бег и коллизия работают, в комнате 12 плиты проваливаются со щебнем — то есть цепочка loose банк3 -> банк2 живая. Полосы бордюра в комнате 3: логика 73 строки, фон 64, спрайты 128, свободно 23. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83 lines
3.9 KiB
C
83 lines
3.9 KiB
C
/*
|
|
* pop_guard.h — СОСТОЯНИЕ стража и точки входа в его логику.
|
|
*
|
|
* Разделение по окнам (docs/layout_plan_v2.md §4, подтверждено пробником
|
|
* tests/w3bankgfx):
|
|
* - СОСТОЯНИЕ живёт здесь, в W1/W2. Писучие статики __banked-модуля
|
|
* линкуются В СТРАНИЦУ БАНКА и снаружи не читаются, поэтому банк —
|
|
* только код;
|
|
* - ЛОГИКА (ИИ, боёвка) — в guards.c, собираемом как --bank 1=guards.c;
|
|
* - ОТРИСОВКА стража — в pop_gdraw.c, РЕЗИДЕНТЕ W3 (--w3): её зовёт
|
|
* только главный цикл, а из банка резидент недостижим. Так код
|
|
* отрисовки не занимает дефицитные W1/W2 (куча).
|
|
*
|
|
* Поля char_type — как в оригинале (types.h:302), чтобы порт seg005/seg006
|
|
* ложился один в один.
|
|
*/
|
|
#ifndef POP_GUARD_H
|
|
#define POP_GUARD_H
|
|
|
|
#include <stdint.h>
|
|
#include "kid_data.h" /* kframe */
|
|
|
|
/* charids (types.h:320) — нужны хотя бы для «скелет не убивается читом». */
|
|
#define CHARID_0_KID 0
|
|
#define CHARID_1_SHADOW 1
|
|
#define CHARID_2_GUARD 2
|
|
#define CHARID_4_SKELETON 4
|
|
#define CHARID_5_PRINCESS 5
|
|
|
|
typedef struct {
|
|
uint8_t frame;
|
|
uint8_t x, y;
|
|
int8_t direction;
|
|
int8_t curr_col, curr_row;
|
|
uint8_t action;
|
|
int8_t fall_x, fall_y;
|
|
uint8_t room;
|
|
uint8_t repeat;
|
|
uint8_t charid;
|
|
uint8_t sword;
|
|
int8_t alive; /* -1 = жив (как в оригинале), 0 = убит */
|
|
uint16_t curr_seq;
|
|
} pop_char_t;
|
|
|
|
extern pop_char_t Guard; /* страж ТЕКУЩЕЙ комнаты (как в оригинале) */
|
|
extern uint8_t guardhp_curr, guardhp_max;
|
|
extern int8_t guardhp_delta; /* <0 = в этом кадре отняли HP */
|
|
|
|
/* Текущий кадр стража (порт cur_frame для Char): заполняет логика в W1/W2,
|
|
* читает отрисовка в резиденте W3. image == 255 — рисовать нечего. */
|
|
extern kframe pop_gframe;
|
|
void pop_guard_load_frame(void);
|
|
|
|
/* Есть ли живой страж в отрисованной комнате (для главного цикла). */
|
|
uint8_t pop_guard_present(void);
|
|
|
|
/* Атласы стража (GUARD\\g0..g4.atl) — грузить ДО initgraph, как Kid.
|
|
* 0 — OK, -1 — ошибка (тогда страж просто не рисуется). */
|
|
int pop_guard_load(void) __banked;
|
|
void pop_guard_free(void) __banked;
|
|
|
|
/* Вход в комнату: поднять стража из данных уровня (порт enter_guard,
|
|
* seg002:0112) либо убрать его, если в комнате стража нет. */
|
|
void pop_guard_enter(uint8_t room);
|
|
|
|
/* Нарисовать текущий кадр стража на back-странице (банк GFX_BANK_SPRITE,
|
|
* как kid_draw) и стереть прошлый (heal). Реализация — pop_gdraw.c
|
|
* (резидент W3), поэтому звать ТОЛЬКО из главного цикла, не из банка. */
|
|
void pop_guard_heal(void) __banked;
|
|
void pop_guard_draw(void) __banked;
|
|
|
|
/* Убрать стража со сцены (смена комнаты / инициализация). */
|
|
void pop_guard_reset(void);
|
|
|
|
/* Чит «убить стража» (seg000:786, K): скелета не берёт. Вызывать только
|
|
* при включённых читах — проверку делает вызывающий. */
|
|
void pop_guard_kill(void);
|
|
|
|
/* Тик логики стража — В БАНКЕ (guards.c). Состояние берёт отсюда. */
|
|
void pop_guard_tick(void) __banked;
|
|
|
|
#endif
|