Unify OPL4 loading and fix progress page corruption

This commit is contained in:
2026-10-04 22:09:06 +03:00
parent bafb2f369c
commit f28caf5cd0
6 changed files with 242 additions and 335 deletions
+139 -11
View File
@@ -618,6 +618,99 @@ ivf_no:
__endasm;
}
/* 1 = после холодного загрузчика дочитать непрочитанный хвост файла в
* освободившиеся после сжатия страницы (нужно только если файл целиком
* не поместился в мегабуфер при первичной загрузке). */
static uint8_t s_load_tail;
/* ── Прогресс-бар ────────────────────────────────────────────────────
* Показывает ДОЛГИЕ фазы: заливку сэмплов в SRAM карты со сжатием
* потока (рисует сам холодный загрузчик) и догрузку хвоста большого
* файла (pb_tail_step ниже). Чтение с SD не отмечаем — оно заметно
* быстрее, а лишние обращения к экрану в его цикле ни к чему.
* Для .vgz бар целиком принадлежит inflate.asm, мы молчим.
*
* Доля закрашенного считается по Брезенхэму (только сложение/вычитание):
* умножение и деление на Z80 тянут за собой библиотечные вызовы. */
static uint8_t s_pb_acc; /* < tail_tot + stepv, в байт влезает */
static uint16_t s_pb_cell;
static uint8_t s_pb_stepv; /* прибавка за один шаг */
static uint8_t s_pb_tail_tot; /* страниц в хвосте — шкала pb_tail_step */
/* Отдать бар холодному загрузчику: он рисует, пока основная память
* плагина отключена. Пишем в его блок параметров по фиксированным
* адресам #9802.. (страница 5, смещение #1800). Если дальше будет ещё
* и догрузка хвоста — отдаём загрузчику половину, остальное ему. */
static void pb_handoff(uint8_t with_tail)
{
uint8_t *p = (uint8_t *)(0xD800 + 2);
uint8_t w = ifl_pb_bw;
s_pb_cell = ifl_pb_attr_ofs & 0x3FFF;
s_pb_acc = 0;
if (with_tail) {
w >>= 1;
s_pb_stepv = w; /* вторая половина — под хвост */
s_pb_cell += w;
}
if (s_is_vgz)
w = 0; /* .vgz: бар за inflate.asm */
wc_mngc_pl(CMDBLK_PAGE);
p[0] = ifl_pb_text_pg;
p[1] = (uint8_t)(ifl_pb_attr_ofs & 0x3FFF);
p[2] = (uint8_t)((ifl_pb_attr_ofs & 0x3FFF) >> 8);
p[3] = ifl_pb_green;
p[4] = w; /* прибавка за страницу (шкала — 64 страницы) */
p[5] = w; /* сколько ячеек ещё не закрашено */
p[6] = 0; /* аккумулятор: загрузчик может вызываться не раз */
}
/* Один шаг догрузки хвоста — вызывается из ll4_tail_loop (уже на своей
* странице, основная память снова видна). */
void pb_tail_step(void)
{
uint8_t saved_pg;
if (s_is_vgz || !s_pb_tail_tot)
return;
saved_pg = sfr_page3;
sfr_page3 = ifl_pb_text_pg;
s_pb_acc += s_pb_stepv;
while (s_pb_acc >= s_pb_tail_tot) {
s_pb_acc -= s_pb_tail_tot;
*(volatile uint8_t *)(0xC000 + s_pb_cell) = ifl_pb_green;
s_pb_cell++;
}
sfr_page3 = saved_pg;
}
/* Один шаг из total; за все total шагов закрашивается ровно ifl_pb_bw ячеек. */
static void pb_step(uint8_t total)
{
uint8_t saved_pg;
if (s_is_vgz)
return;
saved_pg = sfr_page3;
sfr_page3 = ifl_pb_text_pg;
s_pb_acc += ifl_pb_bw;
while (s_pb_acc >= total) {
s_pb_acc -= total;
*(volatile uint8_t *)(0xC000 + s_pb_cell) = ifl_pb_green;
s_pb_cell++;
}
sfr_page3 = saved_pg;
}
/* The unused tail of plugin-relative page 5 (not WC physical page #05)
* contains the one-shot large OPL4 loader at logical address 9800h.
* Offset 1800h is also above WC's 1000h..1447h screen scratch range. */
@@ -651,6 +744,10 @@ ll4_irq_was_off:
ld c, a ; final page count
; D/E already hold page/address high byte
ld a, (_s_load_tail) ; файл целиком поместился → хвоста нет
or a
jr z, ll4_success
ll4_tail_loop:
ld a, d
cp a, #64
@@ -681,6 +778,12 @@ ll4_tail_loop:
or a
jr nz, ll4_fail
push bc
push de
call _pb_tail_step ; страница хвоста дочитана — шаг бара
pop de
pop bc
inc d
ld e, #0xc0
jr ll4_tail_loop
@@ -714,7 +817,9 @@ uint8_t load_vgm(void)
s_pages = (uint8_t)((fsize + 16383UL) >> 14);
if (!s_pages)
s_pages = 1;
s_pb_tail_tot = 0;
if (s_pages > 64) {
s_pb_tail_tot = (uint8_t)(s_pages - 64); /* шкала бара для хвоста */
s_pages = 64;
large_file = 1;
}
@@ -734,6 +839,7 @@ uint8_t load_vgm(void)
/* WC_EOF нормален на последнем секторе */
if (rc && rc != WC_EOF)
return 0;
}
wc_mngcvpl(0); /* страница 0 для проверки заголовка */
@@ -804,12 +910,33 @@ uint8_t load_vgm(void)
dbg_trace2(0xA5, s_pages, 0);
}
if (large_file) {
s_pages = load_large_opl4();
if (!s_pages)
return 0;
/* Холодный загрузчик (asm/opl4_loader.asm, страница 5) — единственный
* путь заливки сэмплов OPL4 в SRAM карты. Запускаем его всегда, когда
* поток начинается с блока 0x67, независимо от размера файла: он же
* вырезает залитые блоки из потока и правит смещения в заголовке.
* large_file → вдобавок дочитать хвост файла в освободившиеся страницы.
* Файл без блоков (только встроенные GM-инструменты, либо вообще не
* OPL4) загрузчику отдавать нельзя — он такое отвергает. */
wc_mngcvpl(0);
{
const uint8_t *b = (const uint8_t *)0xC000;
uint16_t ds = 0x40;
if (*(const uint16_t *)(b + 0x08) >= 0x0150) {
/* data offset относителен 0x34; старшие 16 бит всегда нулевые */
uint16_t d = *(const uint16_t *)(b + 0x34);
if (d)
ds = 0x34 + d;
}
if (b[ds] != 0x67 && !large_file)
return s_pages;
}
pb_handoff(large_file);
s_load_tail = large_file;
s_pages = load_large_opl4();
if (!s_pages)
return 0;
return s_pages;
}
@@ -1202,14 +1329,15 @@ void main(void)
s_vgz_compressed_pages = 0;
draw_pre_load_info();
/* Показать статус ДО начала загрузки — "Unpacking..." для .vgz
* (распаковка), "Loading..." для файлов >= 1 МБ (синхронная заливка
* сэмплов OPL4 в SRAM карты идёт байт за байтом с busy-wait и
* занимает секунды; без надписи выглядит как зависание). Для .vgz
* это последний WC API вызов перед inflate — дальше load_vgm
* работает только портами. */
/* Показать статус ДО начала загрузки: "Unpacking.." для .vgz,
* "Loading.." для остальных. Мгновенной загрузки не бывает ни для
* одного размера — чтение с SD плюс (для OPL4) побайтовая заливка
* сэмплов в SRAM карты с busy-wait. Здесь же настраиваем прогресс-
* бар: для .vgz его рисует inflate.asm, иначе — pb_step() ниже.
* Для .vgz это последний WC API вызов перед inflate — дальше
* load_vgm работает только портами. */
s_is_vgz = is_vgz_filename();
if (s_is_vgz || ((const uint8_t *)&wc_file_size)[2] >= 0x10) {
{
buf_clear(work_buf);
buf_append_str(work_buf, s_is_vgz ? "Unpacking.." : "Loading..");
print_line(&s_wnd, ROW_VGM_START, work_buf, WC_COLOR(WC_YELLOW, WC_BLACK));