From eef6eebd8c514f7f4c2fe6658d949a9289a99314 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Wed, 29 Jul 2026 20:24:03 +0300 Subject: [PATCH] =?UTF-8?q?PoP=20roomtest:=20=D1=84=D0=B8=D0=BA=D1=81=20?= =?UTF-8?q?=D1=87=D1=82=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=82=D0=B0=D0=B1=D0=BB?= =?UTF-8?q?=D0=B8=D1=86=D1=8B=20=D0=BA=D0=B0=D0=B4=D1=80=D0=BE=D0=B2=20?= =?UTF-8?q?=E2=80=94=20=D0=B1=D0=B0=D0=B3=20=D0=BA=D0=BE=D0=B4=D0=BE=D0=B3?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20SDCC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кадры Kid читались из EMM-страницы по НЕВЕРНОМУ адресу для индексов >= 52. Запись (const uint8_t *)(0x100) + (uint16_t)i * 5u SDCC 4.5 собрал так: умножение честно в 16 битах (add hl,hl / add hl,bc), а затем `ld c,l` + `inc b` — то есть взял только МЛАДШИЙ байт результата и подставил старший байт константы. При i*5 >= 256 адрес уезжал на -256*k, и cur_frame наполнялся чужой строкой таблицы: у кадров бега/шага/подъёма пропадал бит FRAME_NEEDS_FLOOR — Kid «вкручивался» в пол и проваливался вниз, последовательности кадров не соответствовали seqtbl. Фикс: адрес считается в uint16_t (i*5 = i + i<<2, без умножения) и кастуется один раз — сгенерированный код теперь сохраняет старший байт (ex de,hl / inc d). Коварство бага: тот же паттерн в pop_level.c (room_fg_ptr/room_bg_ptr, links) компилируется ПРАВИЛЬНО — проверил все три места по .asm. Записано в memory sdcc_z80_const_ptr_index_bug. Проверено в MAME на замороженных кадрах: 10 выборок (кадры 4,10,15,49,50, 54,123) — cur_frame совпадает с таблицей во всех, включая те, что раньше были испорчены. Co-Authored-By: Claude Opus 5 --- applications/PoP/roomtest/pop_kid.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/applications/PoP/roomtest/pop_kid.c b/applications/PoP/roomtest/pop_kid.c index 01700e8..8d05048 100644 --- a/applications/PoP/roomtest/pop_kid.c +++ b/applications/PoP/roomtest/pop_kid.c @@ -90,7 +90,21 @@ void pop_kid_free(void) extern void _gfx_w0_isr(void); #define KD_DATA_OFF 0x100u -#define KD_FRAMES(i) ((const uint8_t *)(KD_DATA_OFF + KID_BIN_FRAMES_OFF) + (uint16_t)(i) * 5u) +/* Адрес кадра i в замапленной странице. Считаем В uint16_t и кастуем ОДИН + * раз: запись `(const uint8_t *)CONST + (uint16_t)i * 5u` SDCC 4.5 собирает + * НЕВЕРНО — умножение делает в 16 битах (add hl,hl / add hl,bc), а потом + * берёт только МЛАДШИЙ байт результата и подставляет старший байт константы + * (`ld c,l` / `inc b`). Для кадров i >= 52 (i*5 >= 256) адрес уезжает на + * −256*k, и cur_frame наполняется чужой строкой таблицы: у бега/шага/подъёма + * пропадал бит FRAME_NEEDS_FLOOR, и Kid проваливался сквозь пол. + * Артефакт: .sprinter-cc-roomtest/pop_kid.asm, функция load_frame. */ +static const uint8_t *kd_frame_ptr(uint8_t i) +{ + uint16_t off = (uint16_t)i; + off += (uint16_t)(off << 2); /* i*5 = i + i*4, без умножения */ + off += (uint16_t)(KD_DATA_OFF + KID_BIN_FRAMES_OFF); + return (const uint8_t *)off; +} #define KD_SEQ(cs) (*((const uint8_t *)(KD_DATA_OFF + KID_BIN_SEQTBL_OFF) + \ (uint16_t)((cs) - SEQTBL_BASE))) /* Байт seqtbl по адресу оригинала (база SEQTBL_BASE); ТОЛЬКО под замапленной @@ -142,7 +156,7 @@ void pop_kid_data_free(void) * cur_frame. Звать ТОЛЬКО когда страница данных замаплена в W0. */ static void load_frame(void) { - const uint8_t *f = KD_FRAMES(Kid.frame); + const uint8_t *f = kd_frame_ptr(Kid.frame); cur_frame.image = f[0]; cur_frame.dx = (int8_t)f[1]; cur_frame.dy = (int8_t)f[2];