f89b7dd0d2
Замер зелёного блока (комната 23 уровня 13, MAME, такты эмулятора) показал, что 86% его стоимости в ПОКОЕ — это pop_loose_tick, который не делает ничего. Причина — кодоген SDCC, подтверждена чтением .asm. С `int pos` и записью pop_loose_modif[pos] компилятор держал счётчик в IX-фрейме, каждую итерацию заново складывал 16-битный адрес элемента, клал его в локал и тут же вычитывал обратно парами `pop bc / pop hl / push hl / push bc`. 40 холостых итераций (30 тайлов + 10 потолков) стоили 27 936 тактов. То же в pop_loose_mob_tick: запись mobs[i] заставляла умножать i на sizeof(mob_t)=15 заново под КАЖДОЕ поле (.active/.clean/.x/.y), 14 пустых слотов — 35 790. Правка — обход указателем, счётчик uint8_t, пустые слоты отсеиваются в вызывающем цикле (а не гардом внутри mob_tick_one, до которого надо ещё дойти). Холостая итерация стала `ld a,(de) / or a,a / jp Z` — три инструкции вместо дюжины с обращениями к памяти. Результат (такты MAME, холостой кадр): циклы по тайлам 27 936 -> 9 852 (2,8x) pop_loose_mob_tick 35 790 -> 7 416 (4,8x) pop_loose_tick 70 866 -> 24 384 (2,9x) ЗЕЛЁНЫЙ БЛОК 82 242 -> 35 760 (2,3x) Банки ужались: BANK3 -90 Б, BANK7 -74 Б. Все 8 наборов tests-host проходят; комната 23 в MAME рисуется корректно. Плюс ВРЕМЕННАЯ оснастка замера (маркеры m9..m16, pop_dbg_kind) — она же показала, что пик зелёного блока сидит НЕ в тряске плит, а в запекании тайлов; разбор продолжается, оснастку снять перед закрытием темы. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
123 lines
5.4 KiB
C
123 lines
5.4 KiB
C
/*
|
|
* pop_redraw.c — пометки перерисовки тайлов и их разбор. См. pop_redraw.h.
|
|
*
|
|
* Живёт в W1/W2 (НЕ в резиденте W3): пометки ставит логика, которая должна
|
|
* оставаться достижимой из будущих __banked-модулей. Сам разбор зовёт
|
|
* pop_bg (резидент W3) — это законно, потому что разбор дёргает только
|
|
* главный цикл.
|
|
*/
|
|
#include <stdint.h>
|
|
#include "pop_redraw.h"
|
|
#include "pop_bg.h"
|
|
#include "pop_state.h" /* ВРЕМЕННО: счётчики состава перерисовок */
|
|
|
|
#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;
|
|
|
|
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;
|
|
pop_fore_pass_end();
|
|
rdf_pending = 0;
|
|
}
|
|
|
|
void pop_set_redraw_fore(uint8_t tilepos) __banked
|
|
{
|
|
if (tilepos >= NTILES) return;
|
|
if (!rdf[tilepos]) rdf_pending++;
|
|
rdf[tilepos] = 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) 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;
|
|
}
|
|
|
|
void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked
|
|
{
|
|
if (tilepos >= NTILES) return;
|
|
if (!rd_cnt[tilepos]) rd_pending++;
|
|
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++;
|
|
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;
|
|
uint8_t cnt[10], tot = 0; /* ВРЕМЕННО: состав кадра */
|
|
if (!rd_pending) return;
|
|
for (i = 0; i < 10; i++) cnt[i] = 0;
|
|
for (i = 0; i < NTILES; i++) {
|
|
if (rd_cnt[i]) {
|
|
if (rd_kind[i] < 8) { cnt[rd_kind[i]]++; tot++; } /* ВРЕМЕННО */
|
|
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;
|
|
default: break;
|
|
}
|
|
pop_dbg_m16(); /* ВРЕМЕННО */
|
|
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]) {
|
|
cnt[8 + (rda_kind[i] == POP_RDA_CEIL ? 0 : 1)]++; tot++; /* ВРЕМЕННО */
|
|
pop_dbg_kind((uint8_t)(8 + rda_kind[i])); /* ВРЕМЕННО */
|
|
if (rda_kind[i] == POP_RDA_CEIL) pop_ceil_shake_draw(i);
|
|
else pop_ceil_bake_empty(i);
|
|
pop_dbg_m16(); /* ВРЕМЕННО */
|
|
if (--rda_cnt[i]) left++;
|
|
else rda_kind[i] = 0;
|
|
}
|
|
}
|
|
if (tot > pop_dbg_rdmax_tot) { /* ВРЕМЕННО: расклад пика */
|
|
pop_dbg_rdmax_tot = tot;
|
|
for (i = 0; i < 10; i++) pop_dbg_rdmax[i] = cnt[i];
|
|
}
|
|
rd_pending = left;
|
|
}
|