/* * pop_redraw.c — пометки перерисовки тайлов и их разбор. См. pop_redraw.h. * * Живёт в W1/W2 (НЕ в резиденте W3): пометки ставит логика, которая должна * оставаться достижимой из будущих __banked-модулей. Сам разбор зовёт * pop_bg (резидент W3) — это законно, потому что разбор дёргает только * главный цикл. */ #include #include "pop_redraw.h" #include "pop_bg.h" #include "_pop_bg.h" /* pop_bake_slot_reset — тот же банк, без трамплина */ #define NTILES 30 #define NCOLS 10 static uint8_t rd_kind[NTILES], rd_cnt[NTILES]; static uint8_t rda_kind[NCOLS], rda_cnt[NCOLS]; /* Сколько всего непустых пометок: без него проход по 40 ячейкам крутился бы * КАЖДЫЙ кадр впустую (замер 2026-07-27 на прежнем rest-цикле: ~12 % кадра). */ static uint8_t rd_pending; /* Пометки ПЕРЕДНЕГО слоя — однокадровые, отдельно от rd_*: те живут по * страницам дабл-буфера (запекают фон), а эти транзиентны. */ static uint8_t rdf[NTILES]; static uint8_t rdf_pending; /* Передний слой РЯДА −1 (полоса кладки у потолка). Отдельным набором, как и * сами перерисовки ряда −1 (rda_*): у оригинала это вообще другой проход — * redraw_needed_tiles сначала обходит ряды 2,1,0, а ПОТОМ ряд 2 комнаты сверху * (seg008:1B06), и его draw_tile_fore кладёт куски в foretable, то есть поверх * midtable, где лежат падающие плиты, и поверх объектов. */ static uint8_t rdfa[NCOLS]; static uint8_t rdfa_pending; void pop_redraw_reset(void) __banked { uint8_t i; for (i = 0; i < NTILES; i++) { rd_kind[i] = 0; rd_cnt[i] = 0; } for (i = 0; i < NCOLS; i++) { rda_kind[i] = 0; rda_cnt[i] = 0; } rd_pending = 0; for (i = 0; i < NTILES; i++) rdf[i] = 0; for (i = 0; i < NCOLS; i++) rdfa[i] = 0; pop_fore_pass_end(); rdf_pending = 0; rdfa_pending = 0; } void pop_set_redraw_fore(uint8_t tilepos) __banked { if (tilepos >= NTILES) return; if (!rdf[tilepos]) rdf_pending++; rdf[tilepos] = 1; } /* То же для полосы кладки У ПОТОЛКА (ряд −1). Ставит падающий кусок, когда * его спрайт достаёт до кромки потолка: иначе кусок накрывает эту кромку * собой, чего быть не может (найдено пользователем 2026-08-17, ур.1 к.6 — * плита (−1,5) перекрывала потолок (−1,6)). */ void pop_set_redraw_fore_above(uint8_t col) __banked { if (col >= NCOLS) return; if (!rdfa[col]) rdfa_pending++; rdfa[col] = 1; } /* Разбор пометок переднего слоя: тайлы обходятся в том же порядке, что и * фон, и рисуются ПОСЛЕ всех объектов кадра — как foretable у оригинала * (draw_tables, seg008:1373: back -> mid -> fore). */ void pop_fore_needed(void) __banked { uint8_t i, row = 0, col = 0; if (!rdf_pending && !rdfa_pending) return; pop_fore_pass_begin(); for (i = 0; i < NTILES; i++) { if (rdf[i]) { rdf[i] = 0; pop_fore_tile_b(row, col); } if (++col == 10) { col = 0; row++; } } rdf_pending = 0; /* Ряд −1 — ПОСЛЕДНИМ, как в redraw_needed_tiles (seg008:1B06). */ if (rdfa_pending) { for (i = 0; i < NCOLS; i++) if (rdfa[i]) { rdfa[i] = 0; pop_ceil_fore_tile_b(i); } rdfa_pending = 0; } } void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked { if (tilepos >= NTILES) return; if (!rd_cnt[tilepos]) rd_pending++; else pop_bake_slot_reset(tilepos); /* перепометка — копия запечки не годится */ /* ПРИОРИТЕТ ПОЛНОЙ ПЕРЕРИСОВКИ НАД слоем anim. У оригинала это не * конфликт видов, а два независимых счётчика, и полная побеждает * (redraw_needed, seg008:0211: `if (redraw_frames_full) draw_tile(); * else if (redraw_frames_anim) {...}`). У нас вид ОДИН на тайл, и без * этой проверки исход решал бы порядок trob'ов в списке: чомпер, который * СЕЙЧАС смыкает челюсти, ставит POP_RD_CHOMP (ему нужен heal — поза * меняется), а факел слева тут же метил бы тот же тайл как * POP_RD_CHOMP_ANIM, и heal пропал бы — новая поза легла бы поверх * старой. */ if (!(kind == POP_RD_CHOMP_ANIM && rd_kind[tilepos] == POP_RD_CHOMP && rd_cnt[tilepos])) rd_kind[tilepos] = kind; if (pages > rd_cnt[tilepos]) rd_cnt[tilepos] = pages; } void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked { if (col >= NCOLS) return; if (!rda_cnt[col]) rd_pending++; else pop_bake_slot_reset_above(col); /* перепометка — копия запечки не годится */ rda_kind[col] = kind; if (pages > rda_cnt[col]) rda_cnt[col] = pages; } /* redraw_needed (seg008:0178): пройти пометки и нарисовать. row/col ведём * бегущими счётчиками, а не через i/10 и i%10 — у Z80 нет деления, и SDCC * выносит __divuchar/__moduchar в вершину цикла как общее подвыражение * аргументов (30*2 делений за кадр даже когда рисовать нечего). */ void pop_redraw_needed(void) __banked { uint8_t i, row = 0, col = 0, left = 0, cnt = 0; /* cnt — ВРЕМЕННО, замер */ pop_dbg_rdmax_tot = 0; if (!rd_pending) return; for (i = 0; i < NTILES; i++) { if (rd_cnt[i]) { pop_dbg_kind(rd_kind[i]); /* ВРЕМЕННО: какой вид перерисовки */ switch (rd_kind[i]) { case POP_RD_SPIKE: pop_spike_redraw(row, col); break; case POP_RD_LOOSE: pop_loose_shake_draw(row, col); break; case POP_RD_LOOSE_GONE: pop_loose_bake_empty(row, col); break; case POP_RD_FLOOR: pop_floor_bake(row, col); break; case POP_RD_LEVELDOOR: pop_leveldoor_redraw(row, col); break; case POP_RD_GATE: pop_gate_redraw(row, col); break; case POP_RD_CHOMP: pop_chomp_redraw(row, col); break; case POP_RD_CHOMP_ANIM: pop_chomp_anim_draw(row, col); break; default: break; } cnt++; /* ВРЕМЕННО: замер */ if (--rd_cnt[i]) left++; else rd_kind[i] = 0; } if (++col == 10) { col = 0; row++; } } for (i = 0; i < NCOLS; i++) { if (rda_cnt[i]) { if (rda_kind[i] == POP_RDA_CEIL) pop_ceil_shake_draw(i); else pop_ceil_bake_empty(i); cnt++; /* ВРЕМЕННО: замер */ if (--rda_cnt[i]) left++; else rda_kind[i] = 0; } } rd_pending = left; pop_dbg_rdmax_tot = cnt; /* ВРЕМЕННО: замер */ if (cnt > pop_dbg_rdmax[0]) pop_dbg_rdmax[0] = cnt; }