Synchronize OPL4 track finalization

This commit is contained in:
2026-09-15 18:00:52 +03:00
parent 3ef0b1dc95
commit ffee082140
6 changed files with 41 additions and 33 deletions
Binary file not shown.
BIN
View File
Binary file not shown.
+15 -19
View File
@@ -29,6 +29,7 @@
.globl _isr_play_seconds
.globl _isr_read_ptr
.globl _isr_done
.globl _isr_final_pending
.globl _cmd_buf_a
.globl _cmd_buf_b
.globl _isr_init
@@ -56,7 +57,7 @@ WC_IM2_VEC = 0x5BFF
CMD_WRITE_AY = 0x00
CMD_INC_SEC = 0x10
CMD_WRITE_AY2 = 0x20
CMD_CALL_WC = 0x30
CMD_FINALIZE = 0x30
CMD_WRITE_B0 = 0x40
CMD_SKIP_TICKS= 0x50
CMD_WRITE_SAA = 0x60
@@ -301,8 +302,8 @@ _isr_cmd_loop:
jp z, _isr_cmd_done
cp #CMD_INC_SEC
jp z, _isr_inc_sec
cp #CMD_CALL_WC
jp z, _isr_call_wc
cp #CMD_FINALIZE
jp z, _isr_cmd_finalize
; Unknown command: skip 3 bytes, continue
inc de
inc de
@@ -589,29 +590,23 @@ _skip_spec_next:
; ISR зацикливается на этой команде, не переключает буферы.
; Main loop ждёт isr_done == 1 перед завершением.
_isr_cmd_done:
inc de
inc de
inc de ; пропустить 3 байта параметров
; Выставить флаг готовности к завершению
ld a, #1
ld (_isr_done), a
; Перемотать read_ptr на начало CMD_ISR_DONE (DE - 4)
jr _isr_cmd_freeze
;--- CMD_FINALIZE: музыкальный поток полностью исполнен -----------------
; Замереть на маркере и попросить main собрать shutdown как при ESC.
_isr_cmd_finalize:
ld a, #1
ld (_isr_final_pending), a
_isr_cmd_freeze:
; DE уже указывает на первый параметр: вернуть read_ptr на opcode.
ex de, hl
dec hl
dec hl
dec hl
dec hl
ld (_isr_read_ptr), hl
jp _isr_exit
;--- CMD_CALL_WC: вызов оригинального WC handler [0,0,0] ------------------
; Внутренняя команда ISR (не используется buffer filler, зарезервировано)
_isr_call_wc:
inc de
inc de
inc de ; пропустить 3 байта параметров
jp _isr_cmd_loop ; просто пропустить
;==============================================================================
; call_wc_handler() — Вызов WC ISR из main loop (C-callable)
;
@@ -641,6 +636,7 @@ _isr_play_seconds:: .dw 0 ; инкрементируется CMD_INC_SE
_isr_read_ptr:: .dw 0
_isr_wait_ctr:: .dw 0
_isr_done:: .db 0 ; 1 = ISR замер на CMD_ISR_DONE, готов к завершению
_isr_final_pending:: .db 0 ; 1 = последний VGM 0x66 уже исполнен
_pos_ptr: .dw 0 ; инициализируется в isr_init()
_isr_saved_vec: .dw 0 ; сохранённый вектор WC (#5BFF)
+5 -2
View File
@@ -34,7 +34,7 @@
* CMD_WRITE_AY │ reg │ val │ 0 │ OUT AY8910 chip 1
* CMD_INC_SEC │ 0 │ 0 │ 0 │ Инкр. isr_play_seconds
* CMD_WRITE_AY2 │ reg │ val │ 0 │ OUT AY8910 chip 2 (TS)
* CMD_CALL_WC │ 0 │ 0 │ 0 │ Вызов WC ISR handler
* CMD_FINALIZE │ 0 │ 0 │ 0 │ Конец VGM → shutdown в main
* CMD_WRITE_B0 │ reg │ val │ 0 │ OUT OPL3 Bank 0
* CMD_SKIP_TICKS│ N │ smsk_lo│ smsk_hi│ Пропустить N позиций pos_table
* CMD_WRITE_SAA │ reg │ val │ 0 │ OUT SAA1099 chip 0
@@ -81,7 +81,7 @@
#define CMD_WRITE_AY 0x00 /* AY8910 chip 1: [reg, val, 0] */
#define CMD_INC_SEC 0x10 /* Инкрементировать счётчик секунд: [0,0,0] */
#define CMD_WRITE_AY2 0x20 /* AY8910 chip 2 (TurboSound): [reg,val,0]*/
#define CMD_CALL_WC 0x30 /* Вызов WC ISR handler: [0,0,0] */
#define CMD_FINALIZE 0x30 /* Конец VGM: остановить ISR и уведомить main */
#define CMD_WRITE_B0 0x40 /* OPL3 Bank 0 write: [reg, val, 0] */
#define CMD_SKIP_TICKS 0x50 /* Пропустить N pos_table: [N,sml,smh] */
#define CMD_WRITE_SAA 0x60 /* SAA1099 write: [reg, val, 0] bit7=chip */
@@ -136,6 +136,9 @@ extern volatile uint16_t isr_play_seconds;
* Main loop очищает при старте воспроизведения. */
extern volatile uint8_t isr_done;
/** ISR дошёл до последнего 0x66; main должен запустить shutdown. */
extern volatile uint8_t isr_final_pending;
/** Командные буферы A и B. Заполняются main loop, читаются ISR. */
extern uint8_t cmd_buf_a[CMD_BUF_SIZE];
extern uint8_t cmd_buf_b[CMD_BUF_SIZE];
+10
View File
@@ -1755,6 +1755,16 @@ ym2_fm_passthru:;
vgm_wait_accum = fb_wacc;
}
vgm_hl_pos++;
/* Финальная silence-цепочка должна начинаться с пустого
* ISR-буфера. Иначе большой cmdblock может быть разрезан
* границей буфера и при следующем fill стартует сначала. */
if (fb_is_vgm && vgm_hl_pos == vgm_hl_abort_pos) {
/* ISR замрёт здесь после исполнения всего музыкального
* хвоста. Main затем запустит shutdown как при ESC. */
fb_wp[0] = CMD_FINALIZE;
fb_wp += 4;
goto finish;
}
goto next_hl;
}
+11 -12
View File
@@ -675,6 +675,7 @@ void start_playback(void)
isr_active_buf = 0;
isr_wait_ctr = 0;
isr_done = 0;
isr_final_pending = 0;
/* MultiSound ctrl: включить FM/SAA только для присутствующих чипов.
* 0xF2 = FM ON + SAA ON + normal read + chip1 (base).
@@ -763,6 +764,7 @@ void stop_playback(void)
s_buf_ready[1] = 0;
s_last_active_buf = 0;
isr_done = 0;
isr_final_pending = 0;
ints_enable();
}
@@ -865,18 +867,6 @@ void update_playback_info(void)
sec = isr_play_seconds;
/* Страховка для VGM без разрешённого loop. Нормальный путь
* заканчивается на opcode 0x66; если нестандартный хвост до него не
* дошёл, не оставляем OPL4 удерживать последнюю ноту бесконечно.
* Две секунды запаса покрывают округление total_samples и уже
* поставленные в ISR команды. */
if (!vgm_loop_enabled && vgm_total_seconds &&
sec >= (uint16_t)(vgm_total_seconds + 2u))
{
vgm_hl_pos = vgm_hl_abort_pos;
instant_abort();
}
sec_changed = (sec != s_last_displayed_sec);
loop_changed = (vgm_loop_count != s_last_loop_count);
@@ -1130,6 +1120,15 @@ void main(void)
/* ── 1. Буфер: максимальный приоритет ──────────────── */
update_buffer();
/* ISR исполнил весь музыкальный хвост и замер на FINALIZE.
* Пересобрать shutdown с начала чистого буфера — тот же путь,
* который используется для надёжного выхода по ESC. */
if (isr_final_pending) {
isr_final_pending = 0;
vgm_hl_pos = vgm_hl_abort_pos;
instant_abort();
}
/* ISR замер на CMD_ISR_DONE → чистый выход */
if (isr_done)
break;