Оптимизация: логический кадр уложился в бюджет, цикл 4 растровых кадра -> 3

Главный цикл спейсится тремя gfx_wait_vsync, поэтому работа сверх 430 000
тактов стоит сразу целый лишний растровый кадр.  Было 470 964, стало
~425 600 — игра быстрее на треть (16,7 логических кадров/с против 12,5).

- pop_y_to_row: цепочка сравнений вместо (y+60)/63%4-1.  ВАЖНО: медленный
  хвост вынесен в ОТДЕЛЬНУЮ функцию — SDCC видит одинаковое выражение в двух
  ветках и поднимает деление в вершину, быстрые возвраты не спасают.
- col_from_x (pop_bg) и get_tile_div_mod (pop_map) — общие резидентные
  таблицы POP_TILE_DIV/POP_TILE_MOD в pop_tile.c (const банка из чужого
  банка не читается).
- pop_fore_over_char: расширение окна считается арифметикой, а не перебором
  10 колонок и 3 рядов (условие монотонно -> границы).  Формулы сверены с
  прежним перебором перебором значений, расхождений нет.
- pop_cd_touch: цикл по страницам развёрнут, x+w/y+h считаются один раз.
  Зовётся с каждого блита фона, стоил 6 846 тактов.
- process_trobs: tp/10 и tp%10 у факелов — таблицей.
- Пустой слот соперника (стража на сцене нет, на странице ничего не
  нарисовано) считается «тихим»: ни heal, ни вход в pop_char_draw, ни
  fore-проход.

Приём для поиска делений: брейкпоинт на __divsint/__divuint/__divuchar с
печатью адреса возврата (printf "%04X", w@(sp)).

Профиль остатка — TASKS_OPEN.md#draw-cost.  tests-host: 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 15:49:46 +03:00
parent d0030922ff
commit b0524b9ad0
9 changed files with 261 additions and 111 deletions
+49 -28
View File
@@ -55,7 +55,7 @@
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
| — | [DRAW-COST](#draw-cost) | шаг 1 (пропуск персонажа) и логика сделаны: синяя полоса 60 % -> 30 %. ОСТАЛОСЬ: отрисовка одного Кида = 47 % кадра, `process_trobs` = 21 % | плавность на ВСЕХ уровнях |
| — | [DRAW-COST](#draw-cost) | **кадр уложился в бюджет 2026-08-09**: 470 964 -> 425 600 тактов, период цикла 4 растровых кадра -> **3** (игра быстрее на треть). Дальнейшее — запас, не срочность | плавность на ВСЕХ уровнях |
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
@@ -396,40 +396,61 @@ wait-state'ы, и замеренная стоимость выходит **≈ 2
два полноценных 16-битных деления на каждый вопрос «в какой колонке точка».
Оригинал делит таблицей (seg006:702) — мы просто не портировали это место.
**Профиль работы за один логический кадр ПОСЛЕ правок (466 560 тактов):**
**ГЛАВНОЕ: логический кадр уложился в бюджет.** Главный цикл спейсится
тремя `gfx_wait_vsync`, поэтому работа сверх 430 000 тактов стоит СРАЗУ
целый лишний растровый кадр. Было 470 964 (период цикла 4 кадра), стало
**409 956 + ~15 600 на ввод = 425 600** — период цикла **3 растровых кадра**.
Игра стала быстрее на треть (16,7 логических кадров/с против 12,5).
Что дало последние тысячи (по убыванию):
| правка | экономия |
|---|---|
| `pop_y_to_row` — цепочка сравнений вместо `/63 % 4` | ~12 000 |
| расширение окна fore-прохода арифметикой вместо перебора 10 колонок и 3 рядов | ~8 500 |
| `col_from_x` — таблицей (те же `POP_TILE_DIV`, вынесены в резидент) | ~11 000 |
| `pop_cd_touch` — развёрнутый цикл по страницам, `x+w`/`y+h` один раз | ~8 600 (зовётся с каждого блита фона) |
| `tp / 10`, `tp % 10` у факелов — таблицей | ~4 000 |
| пустой слот соперника считается «тихим» | ~8 200 |
**Ловушка SDCC, на которой я потерял один прогон:** в `pop_y_to_row` одно и
то же выражение `t / 63 % 4 - 1` стояло в двух ветках, и компилятор поднял
деление В ВЕРШИНУ функции — быстрые возвраты не спасали, `__divsint` звался
всё равно. Лечится выносом медленного хвоста в ОТДЕЛЬНУЮ функцию. Тот же
эффект уже был описан в `pop_loose_tick`; теперь ясно, что это правило, а не
частный случай: **любое деление, встречающееся дважды, SDCC поднимает выше
всех проверок.**
Приём, которым это ловится: брейкпоинт на `__divsint`/`__divuint`/
`__divuchar` с печатью адреса возврата (`printf "%04X", w@(sp)`) — сразу
видно, кто и сколько раз делит за кадр.
**Профиль работы за логический кадр СЕЙЧАС (409 956 тактов + ~15 600 ввод):**
| блок | тактов | % растрового кадра |
|---|---|---|
| see_kid + ctrl_tick + heal | 46 920 | 11 |
| kid_tick (play_seq) | 6 468 | 1,5 |
| физика Кида (`pop_phys_tick`) | 63 102 | 15 |
| страж + боёвка | 10 350 | 2,4 |
| see_kid + ctrl_tick + skip + heal + kid_tick | 52 536 | 12 |
| физика Кида + страж + боёвка | 73 452 | 17 |
| `pop_loose_tick` | 27 438 | 6,4 |
| **`pop_process_trobs`** | **92 346** | **21,5** |
| `pop_redraw_needed` + шов | 6 474 | 1,5 |
| skip + отрисовка соперника (пусто) | 11 178 | 2,6 |
| **`pop_char_draw` + `pop_char_fore` Кида** | **201 336** | **47** |
| борта | 732 | 0,2 |
| **`pop_process_trobs`** (два факела) | **75 720** | **17,6** |
| `pop_redraw_needed` + шов + skip_mask | 10 932 | 2,5 |
| **`pop_char_draw` Кида** | **56 250** | **13** |
| **`pop_char_fore` Кида + борта** | **113 628** | **26** |
Синяя полоса (ввод+heal+логика) была ~60 % → стала ~30 %. Внутри физики
`check_collisions` — по-прежнему больше половины (38 334 из 63 102): три ряда
× окно 4–5 колонок, как в оригинале. Дальше там режется плохо — осталась
арифметика граней, а её удешевление требует перевода сверок в 8 бит и
доказательства, что `coll_xl`/`coll_xr` не выходят из 0..255.
Синяя полоса (ввод + логика) была ~60 % → стала ~29 %.
**Что осталось — по убыванию (это и есть остаток шага 2):**
**Что осталось (запас на будущее, срочности больше нет):**
1. **Отрисовка одного Кида 201 336 тактов = 47 % кадра.** Ровно тот «циан»,
который пользователь назвал следующей целью. План — ниже.
2. **`pop_process_trobs` 92 346 на ДВА факела** (46 000 на факел). У
оригинала `process_trobs` только двигает состояние, рисование —
в `redraw_needed`; у нас `redraw_needed` стоит 6 474, значит блиты факелов
сидят внутри process_trobs. Разобрать, где именно.
3. **`pop_loose_tick` 27 438** при полном отсутствии падающих плит в комнате —
похоже, безусловный проход по всем 30 тайлам.
4. Работа за цикл 466 560 против 430 000 бюджета: **не хватает ~37 000**,
чтобы уложиться в три растровых кадра вместо четырёх. Любая из правок
выше даёт игре сразу +25 % скорости — граница проходит рядом.
1. **`pop_char_fore` 113 628.** Внутри: `char_footprint` + расширение окна
~21 000, дальше шесть `fore_tile`, из которых два реально рисуют (по
~32 000). Дальше резать — кэш «в этом тайле переднего слоя нет вовсе».
2. **`pop_process_trobs` 75 720 на два факела** (~31 000 на факел). Внутри
одного факела: `gfx_blit_noclip` 8 700, чтение w/h и клип 6 400,
`pop_cd_touch` (теперь дешевле), маппинг окна 0 и возвраты. Пламя
перерисовывается каждый кадр обязательно (`TORCH_ANIM_DIV = 1`, кадр
меняется), так что пропуск тут не поможет — только удешевление блита.
3. `pop_loose_tick` 27 438 при полном отсутствии падающих плит.
4. Одно `__divsint` осталось в `pop_char_draw` (`obj_x * 8 / 7`) — ~2 400.
### Шаг 2 (СЛЕДУЮЩИЙ, назначен пользователем): ДВИЖУЩИЙСЯ Кид
+39 -16
View File
@@ -304,10 +304,16 @@ void pop_gate_back_b(uint8_t modl, int xh, int dby, int dmy) __banked
* X кадра Kid (до ×8/7), obj_y — низ спрайта, w/h — размер, dir — направление. */
static int8_t col_from_x(int xpos) /* get_tile_div_mod: колонка (58/14) */
{
int x = xpos - 58;
int xh = x / 14;
if (x % 14 < 0) xh--; /* округление вниз для отрицательных */
return (int8_t)xh;
/* Таблицей (резидентная POP_TILE_DIV, см. pop_tile.h) — как в оригинале.
* Было `/14` и `%14`: два полноценных 16-битных деления SDCC на вызов,
* а зовётся это дважды на каждый футпринт персонажа. */
if ((unsigned int)xpos < 256u) return POP_TILE_DIV[xpos];
{
int x = xpos - 58;
int xh = x / 14;
if (x % 14 < 0) xh--; /* округление вниз для отрицательных */
return (int8_t)xh;
}
}
#define y_to_row(y) pop_y_to_row((int16_t)(y)) /* общий (pop_geom) */
@@ -626,20 +632,37 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y,
* ради скорости (см. pop_fore_layer_cost). Поэтому перебор расширяется
* САМИМ ОКНОМ fore-клипа — оно и есть объединение «персонаж + клинок +
* брызги», считать его второй раз не надо. */
/* Границы считаются АРИФМЕТИКОЙ, а не перебором всех колонок и рядов.
* Условие монотонно по номеру (x0 растёт), значит принятые номера идут
* подряд — хватает первого и последнего. Перебор стоил 38 970 тактов на
* кадр (замер MAME): тринадцать итераций, в каждой пара 16-битных
* ЗНАКОВЫХ сверок, а SDCC пишет их через `jp PO / xor 0x80 / jp P`. */
if (pop_t_fclip_x1 > pop_t_fclip_x0 && pop_t_fclip_y1 > pop_t_fclip_y0) {
int8_t c2, r2;
for (c2 = 0; c2 <= 9; c2++) {
int x0 = POP_COL_XH[c2] * 8;
if (x0 + 40 > pop_t_fclip_x0 && x0 < pop_t_fclip_x1) {
if (c2 < fp_cL) fp_cL = c2;
if (c2 > fp_cR) fp_cR = c2;
}
int8_t lo, hi;
/* Колонки: x0 = POP_COL_XH[c]*8 = 32*c (таблица линейна, шаг 4).
* 32c + 40 > x0 -> c >= floor((x040)/32) + 1
* 32c < x1 -> c <= ceil(x1/32) 1
* Сдвиг вправо у SDCC z80 для int — арифметический, то есть floor и
* для отрицательных; на это здесь и рассчитано. */
lo = (int8_t)(((pop_t_fclip_x0 - 40) >> 5) + 1);
hi = (int8_t)((((pop_t_fclip_x1 + 31) >> 5)) - 1);
if (lo < 0) lo = 0;
if (hi > 9) hi = 9;
if (lo <= hi) {
if (lo < fp_cL) fp_cL = lo;
if (hi > fp_cR) fp_cR = hi;
}
for (r2 = 0; r2 <= 2; r2++) {
int y0 = POP_YOFF + 63 * r2;
if (y0 + 70 > pop_t_fclip_y0 && y0 - 8 < pop_t_fclip_y1) {
if (r2 < fp_rT) fp_rT = r2;
if (r2 > fp_rB) fp_rB = r2;
/* Ряды: y0 = POP_YOFF + 63*r, r только 0..2 — деления на 63 не надо,
* достаточно сравнить с границами полос.
* 63r > y0 POP_YOFF 70 и 63r < y1 POP_YOFF + 8 */
{
int a = pop_t_fclip_y0 - POP_YOFF - 70;
int b = pop_t_fclip_y1 - POP_YOFF + 8;
lo = a < 0 ? 0 : (a < 63 ? 1 : 2);
hi = b > 126 ? 2 : (b > 63 ? 1 : 0);
if (a < 126 && b > 0 && lo <= hi) {
if (lo < fp_rT) fp_rT = lo;
if (hi > fp_rB) fp_rB = hi;
}
}
}
+11 -1
View File
@@ -223,6 +223,13 @@ static uint8_t cd_quiet(uint8_t who, uint8_t p)
cd_sig_t g;
const uint8_t *a, *b;
uint8_t i;
/* Соперника на сцене нет и на ЭТОЙ странице от него ничего не осталось —
* стирать и рисовать нечего, слот «тихий». Без этого пустой слот каждый
* кадр честно проходил heal + вход в pop_char_draw + fore-проход и стоил
* ~8 200 тактов из ничего (замер MAME, комната без стража). */
if (who == POP_CH_OPP && Guard.charid == 0 &&
!pop_cd[who].valid[p] && !pop_cd[who].ovalid[p])
return 1;
if (!pop_cd[who].valid[p]) return 0;
if (pop_cd_dirty & (1 << p)) { /* фон правили — но задели ли нас? */
int x0, y0, x1, y1;
@@ -254,7 +261,10 @@ uint8_t pop_char_skip_mask(void) __banked
* рядом — перерисовываем ОБОИХ. */
uint8_t st = (uint8_t)(q0 ? POP_CH_KID : POP_CH_OPP);
uint8_t mv = (uint8_t)(st ^ 1);
if (pop_cd[mv].valid[p]) {
/* valid[st] тоже: «тихий» слот, в котором на этой странице НИЧЕГО не
* нарисовано (соперника на сцене нет), выесть невозможно, а bbox у
* него мусорный — правило соседства к нему неприменимо. */
if (pop_cd[st].valid[p] && pop_cd[mv].valid[p]) {
int ax0, ay0, ax1, ay1, bx0, by0, bx1, by1;
cd_bbox(st, p, &ax0, &ay0, &ax1, &ay1);
cd_bbox(mv, p, &bx0, &by0, &bx1, &by1);
+27 -1
View File
@@ -10,9 +10,35 @@ const int16_t pop_x_bump[20] = {
};
const int16_t pop_y_land[5] = {-8, 55, 118, 181, 244};
#define TILE_SIZEY_OFF 60 /* сдвиг «ряд 0 начинается выше нуля» */
/* Медленный хвост — ОТДЕЛЬНОЙ функцией. Иначе SDCC видит одно и то же
* выражение в двух ветках и поднимает деление в вершину функции: быстрые
* возвраты уже не спасают, __divsint зовётся всё равно (проверено —
* 5 вызовов за кадр остались после первой переписи). */
static int8_t y_row_div(int16_t t)
{
return (int8_t)(t / TILE_SIZEY % 4 - 1);
}
int8_t pop_y_to_row(int16_t y)
{
return (int8_t)((y + 60) / TILE_SIZEY % 4 - 1);
/* Было `(y + 60) / 63 % 4 - 1` — SDCC разворачивает это в __divsint плюс
* __modsint (а тот внутри снова зовёт __divsint): ~5 400 тактов на вызов
* по замеру в MAME, при том что полос всего четыре. Цепочка сравнений
* повторяет ИМЕННО прежнюю арифметику, включая усечение деления К НУЛЮ
* для отрицательных (t в −62..−1 даёт 0, а не 1). */
int16_t t = (int16_t)(y + TILE_SIZEY_OFF);
if (t >= 0) {
if (t < TILE_SIZEY) return -1; /* 0 % 4 - 1 */
if (t < 2*TILE_SIZEY) return 0;
if (t < 3*TILE_SIZEY) return 1;
if (t < 4*TILE_SIZEY) return 2;
if (t < 5*TILE_SIZEY) return -1; /* 4 % 4 = 0 */
} else if (t > -TILE_SIZEY) {
return -1; /* t/63 == 0 -> 0 % 4 - 1 */
}
return y_row_div(t);
}
/* Уложить значение в диапазон 0..maxv. Вызовы оригинала — prandom(1),
+5 -53
View File
@@ -21,6 +21,7 @@
#include "pop_map.h"
#include "pop_ctrl.h" /* pop_ctrl_shift_held() — для check_grab */
#include "pop_geom.h" /* общая геометрия (x_bump/y_land/y_to_row) */
#include "pop_tile.h" /* POP_TILE_DIV/MOD — деление на ширину тайла таблицей */
#include "pop_redraw.h" /* пометки перерисовки (порт set_redraw_*) */
/* pop_bg нужен ТОЛЬКО падающему куску (mob): spawn/tick/pos — это движущийся
* ОБЪЕКТ, а не перерисовка тайла (в оригинале он и живёт отдельно: mobs +
@@ -387,64 +388,15 @@ static int char_dx_forward(int8_t dx)
return (int)Char.x + d;
}
/* tile_div_tbl (data:22A6). Оригинал делит xpos на 14 ТАБЛИЦЕЙ, а не
* арифметикой — и это оказалось не мелочью: `/14` и `%` 14 SDCC разворачивает
* в __divsint + __modsint, причём __modsint внутри зовёт __divsint ещё раз.
* Замер в MAME (стоящий Кид, комната 1.1): один вызов get_tile_div_mod —
* ~5 400 тактов, 13 вызовов за кадр = ~70 000 = 16 % кадрового периода
* на одно только «в какой колонке точка».
*
* Таблица сверена с SDLPoP seg006:702 побайтно. Индекс — xpos как есть,
* смещение -58 уже внутри значений. Вне 0..255 (у нас xpos - int, у
/* Деление на ширину тайла — таблицами POP_TILE_DIV/POP_TILE_MOD (резидент,
* pop_tile.c: их же читает слой фона). Вне 0..255 (у нас xpos — int, у
* DOS-оригинала байт) остаётся честная арифметика: путь редкий. */
static const int8_t tile_div_tbl[256] = {
-5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14,
};
/* tile_mod_tbl (data:23A6) — остаток от того же деления. Сначала я считал
* его из xh тремя сдвигами, чтобы сэкономить 256 байт; SDCC развернул это в
* десяток команд со спиллами через IY. Вторая таблица и дешевле, и это
* ровно то, что делает оригинал. */
static const uint8_t tile_mod_tbl[256] = {
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
};
/* col = (xpos-58)/14 округл. вниз; g_obj_xl = позиция в тайле (0..13). */
static int8_t get_tile_div_mod(int xpos)
{
if ((unsigned int)xpos < 256u) {
g_obj_xl = tile_mod_tbl[xpos];
return tile_div_tbl[xpos];
g_obj_xl = POP_TILE_MOD[xpos];
return POP_TILE_DIV[xpos];
} else {
int x = xpos - SCREENSPACE_X;
int xh = x / TILE_SIZEX;
+72 -11
View File
@@ -72,6 +72,54 @@ const piece pop_tile_table[31] = {
{ 97, 1, 0, 98, 1, 2, 0, 0, 43,100, 0, 0}, /* 1E debris w/torch */
};
/* Деление координаты на ширину тайла ТАБЛИЦЕЙ (tile_div_tbl/tile_mod_tbl,
* seg006:702/23A6) — как в оригинале. Живут в резиденте, потому что нужны
* и карте (банк 3, колонка персонажа), и слою фона (банк 2, футпринт), а
* const-таблицу чужого банка не прочитать.
*
* Почему не деление: SDCC разворачивает / и % в __divsint + __modsint,
* причём __modsint внутри снова зовёт __divsint — замер в MAME дал 5 400
* тактов на один вызов при 13+ вызовах за кадр. Индекс — координата как
* есть (0..255), смещение -58 уже внутри значений: POP_TILE_DIV[x] =
* floor((x - 58) / 14), POP_TILE_MOD[x] — остаток. Сверено с SDLPoP
* побайтно. */
const int8_t POP_TILE_DIV[256] = {
-5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14,
};
const uint8_t POP_TILE_MOD[256] = {
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
};
const uint8_t POP_COL_XH[10] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36};
const uint8_t POP_WALL_FRAM_BOTTOM[4] = {7, 9, 5, 3};
const uint8_t POP_WALL_FRAM_MAIN[4] = {8, 10, 6, 4};
@@ -107,19 +155,32 @@ uint8_t pop_loose_frame(uint8_t m)
uint8_t pop_cd_dirty;
int pop_cd_dx0[2], pop_cd_dy0[2], pop_cd_dx1[2], pop_cd_dy1[2];
/* Цикл по страницам развёрнут, а x+w / y+h посчитаны один раз. Не
* вкусовщина: индекс-переменная заставляла SDCC считать адрес каждой из
* восьми ячеек сложением, а `x + w` в условии и в присваивании считались
* дважды — замер в MAME дал 6 846 тактов на вызов, а зовётся он с КАЖДОГО
* блита фона (два факела в комнате — уже 13 700 за кадр). */
void pop_cd_touch(int x, int y, int w, int h)
{
uint8_t p;
for (p = 0; p < 2; p++) {
if (!(pop_cd_dirty & (1 << p))) {
pop_cd_dx0[p] = x; pop_cd_dy0[p] = y;
pop_cd_dx1[p] = x + w; pop_cd_dy1[p] = y + h;
} else {
if (x < pop_cd_dx0[p]) pop_cd_dx0[p] = x;
if (y < pop_cd_dy0[p]) pop_cd_dy0[p] = y;
if (x + w > pop_cd_dx1[p]) pop_cd_dx1[p] = x + w;
if (y + h > pop_cd_dy1[p]) pop_cd_dy1[p] = y + h;
}
int x1 = x + w, y1 = y + h;
uint8_t d = pop_cd_dirty;
if (!(d & 1)) {
pop_cd_dx0[0] = x; pop_cd_dy0[0] = y;
pop_cd_dx1[0] = x1; pop_cd_dy1[0] = y1;
} else {
if (x < pop_cd_dx0[0]) pop_cd_dx0[0] = x;
if (y < pop_cd_dy0[0]) pop_cd_dy0[0] = y;
if (x1 > pop_cd_dx1[0]) pop_cd_dx1[0] = x1;
if (y1 > pop_cd_dy1[0]) pop_cd_dy1[0] = y1;
}
if (!(d & 2)) {
pop_cd_dx0[1] = x; pop_cd_dy0[1] = y;
pop_cd_dx1[1] = x1; pop_cd_dy1[1] = y1;
} else {
if (x < pop_cd_dx0[1]) pop_cd_dx0[1] = x;
if (y < pop_cd_dy0[1]) pop_cd_dy0[1] = y;
if (x1 > pop_cd_dx1[1]) pop_cd_dx1[1] = x1;
if (y1 > pop_cd_dy1[1]) pop_cd_dy1[1] = y1;
}
pop_cd_dirty = 3;
}
+6
View File
@@ -74,6 +74,12 @@ extern const piece pop_tile_table[31];
/* Таблицы, нужные ОБЕИМ половинам слоя фона (односторонние живут по месту). */
extern const uint8_t POP_COL_XH[10]; /* x тайла = COL_XH[col]*8 */
/* Деление координаты на ширину тайла таблицей (оригинал: tile_div_tbl /
* tile_mod_tbl, seg006:702). Индекс — координата 0..255 как есть, смещение
* -58 уже внутри: POP_TILE_DIV[x] = floor((x-58)/14), POP_TILE_MOD[x] —
* остаток. Резидент: нужны и карте (банк 3), и слою фона (банк 2). */
extern const int8_t POP_TILE_DIV[256];
extern const uint8_t POP_TILE_MOD[256];
extern const uint8_t POP_WALL_FRAM_BOTTOM[4]; /* нижняя грань стены по bg */
extern const uint8_t POP_WALL_FRAM_MAIN[4]; /* основная грань стены */
extern const uint8_t POP_SPIKES_FRAM_LEFT[10]; /* кадр пик в своей ячейке */
+12 -1
View File
@@ -467,6 +467,14 @@ static uint8_t torch_tick;
#define TORCH_ANIM_NOW 1
#endif
/* Разбор номера тайла 0..29 на ряд/колонку без деления. */
static const uint8_t TP_ROW[30] = {
0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2
};
static const uint8_t TP_COL[30] = {
0,1,2,3,4,5,6,7,8,9, 0,1,2,3,4,5,6,7,8,9, 0,1,2,3,4,5,6,7,8,9
};
void pop_process_trobs(uint8_t cur_room) __banked
{
uint8_t i, w = 0;
@@ -549,7 +557,10 @@ void pop_process_trobs(uint8_t cur_room) __banked
(code == TILE_TORCH || code == TILE_TORCH_DEBRIS || code == TILE_POTION)) {
/* Пламя/пузырёк анимируются КАЖДЫЙ кадр и в фон не запечены —
* рисуем поверх (сами делают heal своей области). */
uint8_t trow = tp / 10, tcol = tp % 10;
/* Таблицей, а не tp/10 и tp%10: у Z80 деления нет, SDCC зовёт
* __divuchar + __moduchar — на два факела это четыре вызова за
* кадр (замер MAME). Тайл всегда 0..29. */
uint8_t trow = TP_ROW[tp], tcol = TP_COL[tp];
if (code == TILE_POTION) pop_potion_draw(trow, tcol, mod[tp]);
else pop_torch_draw(trow, tcol, mod[tp]);
}
@@ -273,3 +273,43 @@ void pop_guard_state_save(uint8_t room, uint8_t tile, int8_t dir, uint8_t x,
uint16_t pop_guard_state_seq(uint8_t room)
{ return (room >= 1 && room <= TK_ROOMS) ? tk_gs_seq[room - 1] : 0; }
uint8_t pop_guard_state_x(uint8_t room) { (void)room; return 0; }
/* Таблицы деления на ширину тайла — настоящие (резидент pop_tile.c в тест
* не линкуется). Не заглушка: физика считает по ним колонку, подменять
* нельзя. Значения сгенерированы той же формулой, что в pop_tile.c. */
const int8_t POP_TILE_DIV[256] = {
-5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14,
};
const uint8_t POP_TILE_MOD[256] = {
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1,
};