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
Binary file not shown.
BIN
View File
Binary file not shown.
+82 -1
View File
@@ -15,6 +15,20 @@ OPL_WAVE_DAT EQU #7F
PORT_SYSCONF EQU #20AF ; System Config (turbo bits [1:0]), see isr.s
TURBO_14MHZ EQU #02
; ── Блок параметров прогресс-бара ───────────────────────────────────
; Основная память плагина (Win2) на время работы загрузчика отключена —
; здесь стоит страница 5, — поэтому ifl_pb_* прочитать неоткуда. main.c
; кладёт нужное сюда ДО вызова, замапив страницу 5 в Win3 (адреса ниже
; фиксированы намеренно: #9802..#9806, их знает load_large_opl4()).
; Точка входа остаётся #9800, поэтому первым стоит перепрыг блока.
JR opl4_large_entry ; #9800
pb_text_pg: DB 0 ; #9802 физ. страница текстового экрана
pb_ptr: DW 0 ; #9803 текущая ячейка атрибутов (окно-отн.)
pb_green: DB 0 ; #9805 цвет закрашенной части
pb_step: DB 0 ; #9806 прибавка к аккумулятору за страницу
pb_left: DB 0 ; #9807 сколько ячеек ещё можно закрасить
pb_acc: DB 0 ; #9808 аккумулятор Брезенхэма (шкала — 64)
; Return: A bit 0..6 = compacted 16K page count (0 = reject),
; A bit 7 = ROM-image flag,
; H = first VPL page for the unread tail,
@@ -502,6 +516,17 @@ compact_and_patch:
ld a,(dst_page)
inc a
ld (dst_page),a
; Дальше копировать нечего, как только приёмник добрал до
; result_pages — это весь сжатый поток вместе с GD3. Без этой
; проверки цикл всегда шёл до 64-й страницы и для небольшого
; файла перемалывал сотни килобайт мусора за концом данных.
; (Для большого файла не срабатывает: там исходные страницы
; кончатся раньше, хвост дочитывается уже после возврата.)
push hl
ld hl,result_pages
cp (hl)
pop hl
jr nc,.copy_done
add a,#20
ld bc,PORT_W0
out (c),a
@@ -514,6 +539,10 @@ compact_and_patch:
ld (copy_src_page),a
cp 64
jr z,.copy_done
push de
call pb_tick ; фаза сжатия: продолжаем ту же шкалу
pop de
ld a,(copy_src_page)
add a,#20
ld bc,PORT_W3
out (c),a
@@ -596,7 +625,7 @@ next_src_page:
call map_vpl
ld hl,#C000
ld (src_ptr),hl
ret
jp pb_tick ; фаза заливки: одна страница -> шаг бара
len_is_zero:
ld hl,block_len
@@ -639,6 +668,58 @@ sub_len_bc:
ld (block_len+2),hl
ret
; ── Прогресс-бар ────────────────────────────────────────────────────
; Обе фазы загрузчика идут по страницам мегабуфера монотонно 0 -> 64:
; заливка двигает src_page от 0 до конца блоков, копирование продолжает
; с того же места до 64. Поэтому шкала фиксированная (64), и хватает
; одного счётчика Брезенхэма без умножения и деления.
; Вызывается из next_src_page и из цикла копирования; регистры не портит.
pb_tick:
push af
push bc
push hl
ld a,(pb_step)
ld hl,pb_acc
add a,(hl)
ld (hl),a
.spill: cp 64
jr c,.done
sub 64
ld (pb_acc),a
ld a,(pb_left)
or a
jr z,.done ; бар уже закрашен до конца
call pb_paint
ld a,(pb_acc)
jr .spill
.done: pop hl
pop bc
pop af
ret
; Закрасить одну ячейку. Текстовый экран временно подставляем в Win0.
; PORT_W0 — write-only на реальном TSConf (и не обязан возвращать ранее
; записанное значение в эмуляторе), поэтому восстанавливать страницу через
; IN нельзя. Номер приёмника нам и так известен: #20 + dst_page. Во время
; заливки dst_page=0 и Win0 не используется, а во время копирования это
; всегда текущая страница назначения.
pb_paint:
ld bc,PORT_W0
ld a,(pb_text_pg)
out (c),a
ld hl,(pb_ptr)
ld a,(pb_green)
ld (hl),a ; Win0 начинается с #0000 — смещение и есть адрес
inc hl
ld (pb_ptr),hl
ld hl,pb_left
dec (hl)
ld a,(dst_page)
add a,#20
ld bc,PORT_W0
out (c),a
ret
chip_wait:
in a,(OPL_STAT)
rrca
+1 -1
View File
@@ -35,7 +35,7 @@
/* Плотная раскладка: полная версия spectrum.s + загрузчик OPL4.
* Проверяется build.bat: _CODE должен закончиться раньше _DATA,
* а последняя секция — раньше 0xC000. */
#define VARIANT_DATA_LOC 0xB8DF
#define VARIANT_DATA_LOC 0xB8B8
/* ── Спектроанализатор: скорости затухания (в TV-кадрах) ────────── */
/* Меньше = быстрее затухание. 1 = каждый кадр (~20 мс). */
+14 -316
View File
@@ -128,17 +128,11 @@ static uint16_t fb_scaled; /* temp: масштабированная часто
static uint8_t fb_reg; /* temp: AY рег. (0xA0) / YMF278B port (0xD0) /
* тип data-block (0x67) — ветки взаимоисключающие */
/* ── Патч заголовков wave-таблицы после загрузки блока 0x84 (ROM Image) ──
* См. wave_upload_block(): референсный NedoOS-драйвер (vgm/opl4.asm,
* opl4loadromdatablock) после заливки ROM-блока, чей адрес попадает в
* первые 64КБ RAM-региона (где живёт таблица заголовков сэмплов, 128
* записей по 12 байт), принудительно взводит бит RAM-адресации (#20) в
* первом байте КАЖДОЙ записи — иначе встроенные в заголовки адреса
* сэмплов остаются ROM-относительными, и чип читает не тот PCM-семпл. */
static uint8_t hdr_recs; /* оставшиеся записи таблицы (0-128); 0=не патчим */
static uint8_t hdr_sub; /* позиция внутри 12-байтной записи (0-11) */
static uint8_t opl4_rom_loaded; /* 0x84 ROM image перенесён в SRAM; номера
* волн надо отображать в диапазон 384-511 */
/* Взводится холодным загрузчиком (через vgm_opl4_preloaded_rom), когда он
* перенёс в SRAM блок типа 0x84 (ROM Image) и пропатчил бит RAM-адресации
* в 128 заголовках wave-таблицы. Тогда исходные номера волн из VGM надо
* отображать в диапазон SRAM-заголовков 384-511 (см. обработчик 0xD0). */
static uint8_t opl4_rom_loaded;
/* VGM_FILL_CMD_BUDGET определён в variant_cfg.h (через isr.h) */
@@ -827,307 +821,16 @@ static void asm_read_2bytes(void) __naked
__endasm;
}
/* ─────────────────────────────────────────────────────────────────────
* OPL4 (ZXM-MoonSound) — прямая (не через cmd-buffer) запись в wave-порты
* #7E (выбор регистра) / #7F (данные), плюс переключение turbo-режима.
* Используются ТОЛЬКО в wave_upload_block() — разовая синхронная заливка
* PCM-сэмплов при загрузке трека, до старта ISR-плейбека (не hot-path).
* ───────────────────────────────────────────────────────────────────── */
/* out (n),a — короткая форма (2 байта). Старший байт адреса (A15-A8)
* при этом равен A (значению-аргументу), но плата его не видит: линия
* CA[7..0] у CPLD DD2 — единственный адресный вход декодера портов
* #7E/#7F/#C4-#C7 (8 бит), верхняя половина шины к нему не подключена
* (см. схему ZXM-MoonSound, dd2.tdf) — как и в родном moon_driver.asm.
/* Заливка PCM-сэмплов в SRAM карты живёт целиком в холодном загрузчике
* (asm/opl4_loader.asm на оверлейной странице 5): он вызывается из
* load_vgm() ДО старта воспроизведения, заливает блоки 0x67 типа
* 0x84/0x87 и вырезает их из потока. Поэтому сюда, в основной бюджет
* 0x8000-0xBFFF, эти 376 байт больше не входят, а обработчик 0x67 ниже
* просто пропускает блок: к началу воспроизведения его уже нет.
*
* Перед каждой записью — опрос busy-флага (бит0 порта #C4 — тот же
* порт, что и FM Bank1 addr-select, но на чтение отдаёт статус чипа).
* Без этого ожидания запись может быть потеряна чипом (особенно в
* режиме прямого доступа к памяти) — подтверждено рабочим NedoOS-
* драйвером (common/moonsound.asm: opl4_wait), а не только datasheet. */
static void chip_wait(void) __naked {
__asm
push af
cw_loop:
in a, (0xC4)
rrca
jr c, cw_loop
pop af
ret
__endasm;
}
static void wave_out_reg(uint8_t reg) __naked {
__asm
call _chip_wait
out (0x7E), a
ret
__endasm;
(void)reg;
}
static void wave_out_dat(uint8_t val) __naked {
__asm
call _chip_wait
out (0x7F), a
ret
__endasm;
(void)val;
}
/* Быстрая заливка fb_w байт из текущей VPL-страницы. В отличие от
* asm_read_byte()+wave_out_dat() указатели и счётчик держатся в регистрах;
* обязательный busy-wait перед каждым байтом сохранён. Вызывающий код не
* даёт циклу пересечь границу страницы. */
static void wave_upload_fast(void) __naked {
__asm
ld hl, (_fb_rp)
ld bc, (_fb_w)
wuf_loop:
in a, (0xC4)
rrca
jr c, wuf_loop
ld a, (hl)
out (0x7F), a
inc hl
dec bc
ld a, b
or a, c
jr nz, wuf_loop
ld (_fb_rp), hl
ret
__endasm;
}
/* ─────────────────────────────────────────────────────────────────────
* wave_upload_block — залить payload VGM data-block 0x67 типа 0x84
* (YMF278B ROM Image) / 0x87 (RAM Image) в SRAM карты ZXM-MoonSound.
*
* Вызывается из обработчика 0x67 в vgm_fill_buffer() ДО начала реальной
* ISR-подачи звука (все такие блоки в реальных файлах идут одним пакетом
* в самом начале потока) — поэтому синхронная блокирующая заливка здесь
* безопасна: играть ещё нечему.
*
* Формат payload (VGM spec, "ROM/RAM Image" data block, offset от начала
* data-payload, т.е. после opcode+0x66+type+4-байт общего размера):
* [0..3] ROM/RAM Size (информационное, не используется)
* [4..7] Start Address — смещение внутри SRAM карты (0-based)
* [8..] сами байты сэмпла/инструмент-заголовков
*
* Адресация чипа YMF278B: 2МБ встроенный ROM (General MIDI) + 1МБ SRAM,
* физически идущие в чипе одним адресным пространством — SRAM начинается
* сразу по границе 0x200000 (аппаратный CS чипа по адресному биту A21,
* см. datasheet, от платы не зависит). wavetblhdr=4 (4×0x80000=0x200000)
* выставлен так, чтобы номер сэмпла (wave) 384 указывал ровно на начало
* этой SRAM — тем самым абсолютный адрес = 0x200000 + Start Address.
*
* len = полный размер data-block (4-байтовое поле "size" из потока),
* включает 8-байтовый под-заголовок выше.
* ───────────────────────────────────────────────────────────────────── */
static void wave_upload_block(void) __naked
{
__asm
; ROM/RAM Size (4 bytes) is informational only.
call _asm_read_2bytes
call _asm_read_2bytes
; NEW2: FM bank 1, register 05h = 03h. Wave registers ignore writes
; until this bit is enabled, and the command ISR has not started yet.
call _chip_wait
ld a, #0x05
out (0xC6), a
call _chip_wait
ld a, #0x03
out (0xC7), a
; VGM start address is little-endian; YMF278B wants high/mid/low.
call _asm_read_byte
ld (_fb_b2), a
call _asm_read_byte
ld (_fb_b1), a
call _asm_read_byte
ld (_fb_ch), a
call _asm_read_byte ; unused top byte of the 32-bit address
; Patch 128 twelve-byte headers only for a ROM image beginning at 0.
xor a
ld (_hdr_recs), a
ld (_hdr_sub), a
ld a, (_fb_reg)
cp a, #0x84
jr nz, wub_header_ready
ld a, (_fb_ch)
ld hl, #_fb_b1
or a, (hl)
inc hl
or a, (hl)
jr nz, wub_header_ready
ld a, #0x80
ld (_hdr_recs), a
wub_header_ready:
; Memory address = 200000h + start address. Register order matters.
ld a, #0x03
call _wave_out_reg
ld a, (_fb_ch)
add a, #0x20
call _wave_out_dat
ld a, #0x04
call _wave_out_reg
ld a, (_fb_b1)
call _wave_out_dat
ld a, #0x05
call _wave_out_reg
ld a, (_fb_b2)
call _wave_out_dat
; 11h = memory access mode, WT header base 200000h; 06h = data port.
ld a, #0x02
call _wave_out_reg
ld a, #0x11
call _wave_out_dat
ld a, #0x06
call _wave_out_reg
; fb_t32 contains the whole block payload, including its 8-byte header.
ld hl, (_fb_t32)
ld de, #0x0008
or a
sbc hl, de
ld (_fb_t32), hl
jr nc, wub_len_ready
ld hl, (_fb_t32 + 2)
dec hl
ld (_fb_t32 + 2), hl
wub_len_ready:
ld a, (_hdr_recs)
or a
jr z, wub_fast_test
ld a, #0x01
ld (_opl4_rom_loaded), a
; Patch address bit 21 in byte 0 of each of the first 128 headers.
wub_patch_test:
ld a, (_hdr_recs)
or a
jr z, wub_fast_test
ld hl, #_fb_t32
ld a, (hl)
inc hl
or a, (hl)
inc hl
or a, (hl)
inc hl
or a, (hl)
jp z, wub_finish
call _asm_read_byte
ld e, a
ld a, (_hdr_sub)
or a
jr nz, wub_patch_count
ld a, e
or a, #0x20
ld e, a
wub_patch_count:
ld a, (_hdr_sub)
inc a
cp a, #0x0C
jr nz, wub_store_sub
xor a
ld (_hdr_sub), a
ld hl, #_hdr_recs
dec (hl)
jr wub_patch_write
wub_store_sub:
ld (_hdr_sub), a
wub_patch_write:
ld a, e
call _wave_out_dat
; --fb_t32. The high word changes only when the low word wraps.
ld hl, (_fb_t32)
dec hl
ld (_fb_t32), hl
ld a, h
and a, l
inc a
jr nz, wub_patch_test
ld hl, (_fb_t32 + 2)
dec hl
ld (_fb_t32 + 2), hl
jr wub_patch_test
; Upload the rest in chunks that never cross a mapped 16K page.
wub_fast_test:
ld hl, #_fb_t32
ld a, (hl)
inc hl
or a, (hl)
inc hl
or a, (hl)
inc hl
or a, (hl)
jr z, wub_finish
; fb_w = 0 - fb_rp: number of bytes through FFFFh inclusive.
ld hl, (_fb_rp)
xor a
sub a, l
ld e, a
ld a, #0x00
sbc a, h
ld d, a
ld (_fb_w), de
; If the remaining 32-bit length is smaller, use its low word.
ld hl, (_fb_t32 + 2)
ld a, h
or a, l
jr nz, wub_fast_upload
ld hl, (_fb_t32)
or a
sbc hl, de
jr nc, wub_fast_upload
ld hl, (_fb_t32)
ld (_fb_w), hl
wub_fast_upload:
call _wave_upload_fast
; fb_t32 -= fb_w, propagating carry into the high word.
ld hl, (_fb_t32)
ld de, (_fb_w)
or a
sbc hl, de
ld (_fb_t32), hl
jr nc, wub_after_subtract
ld hl, (_fb_t32 + 2)
dec hl
ld (_fb_t32 + 2), hl
wub_after_subtract:
; wave_upload_fast leaves fb_rp at zero exactly on a page boundary.
ld hl, (_fb_rp)
ld a, h
or a, l
jr nz, wub_fast_test
ld hl, #_fb_cpg
inc (hl)
ld a, (hl)
call _wc_mngcvpl
ld hl, #0xC000
ld (_fb_rp), hl
jr wub_fast_test
wub_finish:
ld a, #0x02
call _wave_out_reg
ld a, #0x10 ; sound generation, WT base 200000h
call _wave_out_dat
ret
__endasm;
}
* ВАЖНО: загрузчик снимает только непрерывную серию блоков в САМОМ
* начале потока. Блок сэмплов в середине трека будет пропущен, а не
* залит (проверено: ни в одном файле Moonsound_pack такого нет). */
/* ─────────────────────────────────────────────────────────────────────
* asm_emit_skip_spec — запись CMD_SKIP_TICKS + spec_mask в cmd buffer
*
@@ -1920,17 +1623,12 @@ ym2_fm_passthru:;
asm_read_2bytes();
fb_t32 |= ((uint32_t)fb_b1 << 16) | ((uint32_t)fb_b2 << 24);
if (fb_reg == 0x84 || fb_reg == 0x87) {
/* YMF278B ROM/RAM Image — залить payload в SRAM карты */
wave_upload_block();
} else {
/* сохранить → vgm_skip → восстановить */
vgm_read_ptr = (uint16_t)fb_rp;
vgm_cur_page = fb_cpg;
vgm_skip(fb_t32);
fb_rp = (uint8_t *)vgm_read_ptr;
fb_cpg = vgm_cur_page;
}
continue;
}
+137 -9
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,11 +910,32 @@ uint8_t load_vgm(void)
dbg_trace2(0xA5, s_pages, 0);
}
if (large_file) {
/* Холодный загрузчик (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));