diff --git a/DiskRef/WC/VGMPLAY.WMF b/DiskRef/WC/VGMPLAY.WMF index 186f268..3a5e73e 100644 Binary files a/DiskRef/WC/VGMPLAY.WMF and b/DiskRef/WC/VGMPLAY.WMF differ diff --git a/VGMPLAY.WMF b/VGMPLAY.WMF index 186f268..3a5e73e 100644 Binary files a/VGMPLAY.WMF and b/VGMPLAY.WMF differ diff --git a/src_sdcc/asm/opl4_loader.asm b/src_sdcc/asm/opl4_loader.asm index 553e652..03dfbde 100644 --- a/src_sdcc/asm/opl4_loader.asm +++ b/src_sdcc/asm/opl4_loader.asm @@ -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 diff --git a/src_sdcc/inc/variant_cfg.h b/src_sdcc/inc/variant_cfg.h index b4e909f..dcc9c0a 100644 --- a/src_sdcc/inc/variant_cfg.h +++ b/src_sdcc/inc/variant_cfg.h @@ -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 мс). */ diff --git a/src_sdcc/lib/vgm.c b/src_sdcc/lib/vgm.c index d767fa6..9f8fb2e 100644 --- a/src_sdcc/lib/vgm.c +++ b/src_sdcc/lib/vgm.c @@ -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; - } + /* сохранить → 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; } diff --git a/src_sdcc/main.c b/src_sdcc/main.c index 9774676..53e0892 100644 --- a/src_sdcc/main.c +++ b/src_sdcc/main.c @@ -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));