Оптимизация: логический кадр уложился в бюджет, цикл 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:
@@ -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 (СЛЕДУЮЩИЙ, назначен пользователем): ДВИЖУЩИЙСЯ Кид
|
||||
|
||||
|
||||
@@ -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((x0−40)/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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
@@ -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]; /* кадр пик в своей ячейке */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user