From f28caf5cd0f0b795dbfeea22314c4adee3b18eb8 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 4 Oct 2026 22:09:06 +0300 Subject: [PATCH] Unify OPL4 loading and fix progress page corruption --- DiskRef/WC/VGMPLAY.WMF | Bin 98816 -> 98816 bytes VGMPLAY.WMF | Bin 98816 -> 98816 bytes src_sdcc/asm/opl4_loader.asm | 83 ++++++++- src_sdcc/inc/variant_cfg.h | 2 +- src_sdcc/lib/vgm.c | 342 ++--------------------------------- src_sdcc/main.c | 150 +++++++++++++-- 6 files changed, 242 insertions(+), 335 deletions(-) diff --git a/DiskRef/WC/VGMPLAY.WMF b/DiskRef/WC/VGMPLAY.WMF index 186f268ee1a50204337c2fa58df2116e2e9d26eb..3a5e73e87e6d79210062a56a033ce51540cefaa9 100644 GIT binary patch delta 11429 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 шаг бара 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));