Unify OPL4 loading and fix progress page corruption
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 мс). */
|
||||
|
||||
+20
-322
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+139
-11
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user