Add OPL4 playback and analyzer support
This commit is contained in:
@@ -0,0 +1,615 @@
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
; VGM Parser & Buffer Filler
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
;
|
||||
; - VGM_PARSE_HEADER: Parse VGM file header from VGMBUF
|
||||
; - VGM_FILL_BUFFER: Fill a command buffer (A=0 for BUF_A, A=1 for BUF_B)
|
||||
; Reads VGM stream, converts to ISR commands
|
||||
;
|
||||
; VGM opcodes processed:
|
||||
; 0x5E rr dd - YMF262 port 0 write (addr=rr, data=dd)
|
||||
; 0x5F rr dd - YMF262 port 1 write (addr=rr, data=dd)
|
||||
; 0x61 nn nn - Wait N samples (16-bit LE)
|
||||
; 0x62 - Wait 735 samples (1/60 sec)
|
||||
; 0x63 - Wait 882 samples (1/50 sec)
|
||||
; 0x66 - End of sound data
|
||||
; 0x70-0x7F - Wait 1-16 samples
|
||||
;
|
||||
; Command buffer format (4 bytes each, aligned):
|
||||
; CMD_WAIT (0): [0x00, ticks_lo, ticks_hi, 0x00]
|
||||
; CMD_WRITE (1): [0x01, port, value, 0x00]
|
||||
; CMD_END (FF): [0xFF, 0x00, 0x00, 0x00]
|
||||
;
|
||||
; Wait counter mapping: VGM "wait N samples" → ISR wait = N-1
|
||||
; (because the ISR tick that processes the WAIT command is tick 0,
|
||||
; and counter=0 means "process next tick")
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
; VGM_PARSE_HEADER
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
; Parse VGM header at VGMBUF.
|
||||
; Output: Z = success, NZ = error
|
||||
VGM_PARSE_HEADER:
|
||||
PUSH HL
|
||||
PUSH DE
|
||||
PUSH BC
|
||||
|
||||
; Check "Vgm " signature
|
||||
LD HL,VGMBUF
|
||||
LD A,(HL)
|
||||
CP 'V'
|
||||
JP NZ,VGM_PARSE_FAIL
|
||||
INC HL
|
||||
LD A,(HL)
|
||||
CP 'g'
|
||||
JP NZ,VGM_PARSE_FAIL
|
||||
INC HL
|
||||
LD A,(HL)
|
||||
CP 'm'
|
||||
JP NZ,VGM_PARSE_FAIL
|
||||
INC HL
|
||||
LD A,(HL)
|
||||
CP ' '
|
||||
JP NZ,VGM_PARSE_FAIL
|
||||
|
||||
; Read total samples (offset 0x18, 32-bit)
|
||||
LD HL,VGMBUF+#18
|
||||
LD E,(HL)
|
||||
INC HL
|
||||
LD D,(HL)
|
||||
LD (VGM_TOTAL_SAMPLES),DE
|
||||
INC HL
|
||||
LD E,(HL)
|
||||
INC HL
|
||||
LD D,(HL)
|
||||
LD (VGM_TOTAL_SAMPLES+2),DE
|
||||
|
||||
; Read OPL3 clock (offset 0x5C, 32-bit) - must be non-zero
|
||||
LD HL,VGMBUF+#5C
|
||||
LD E,(HL)
|
||||
INC HL
|
||||
LD D,(HL)
|
||||
LD (VGM_CLOCK),DE
|
||||
INC HL
|
||||
LD E,(HL)
|
||||
INC HL
|
||||
LD D,(HL)
|
||||
LD (VGM_CLOCK+2),DE
|
||||
|
||||
; Check OPL3 clock != 0 (at least low word should be non-zero for OPL3)
|
||||
LD HL,(VGM_CLOCK)
|
||||
LD A,H
|
||||
OR L
|
||||
JR NZ,VGM_HAS_OPL3
|
||||
LD HL,(VGM_CLOCK+2)
|
||||
LD A,H
|
||||
OR L
|
||||
; OPL3 clock = 0: no OPL3 data, but well allow it (might still have data)
|
||||
VGM_HAS_OPL3:
|
||||
|
||||
; Read data offset (offset 0x34, relative to 0x34)
|
||||
; For VGM version < 1.50, data starts at 0x40
|
||||
LD HL,VGMBUF+#08 ; version
|
||||
LD A,(HL) ; version low byte (BCD minor)
|
||||
LD B,A
|
||||
INC HL
|
||||
LD A,(HL) ; version byte 2
|
||||
; Check if version >= 1.50
|
||||
; Version bytes: [08]=minor_lo, [09]=minor_hi, [0A]=major_lo, [0B]=major_hi
|
||||
; For v1.50: bytes are 50 01 00 00 or similar
|
||||
; Simplified: if offset 0x34 has non-zero value and version >= 1.50, use it
|
||||
LD HL,VGMBUF+#34
|
||||
LD E,(HL)
|
||||
INC HL
|
||||
LD D,(HL)
|
||||
; data_offset = value at 0x34 (relative to 0x34)
|
||||
; If 0, default to 0x0C (data starts at 0x40 = 0x34+0x0C)
|
||||
LD A,D
|
||||
OR E
|
||||
JR NZ,VGM_USE_DATA_OFF
|
||||
LD DE,#0C ; default: 0x40 - 0x34 = 0x0C
|
||||
VGM_USE_DATA_OFF:
|
||||
; VGM_DATA_START = VGMBUF + 0x34 + data_offset
|
||||
LD HL,VGMBUF+#34
|
||||
ADD HL,DE
|
||||
LD (VGM_DATA_START),HL
|
||||
LD (VGM_READ_PTR),HL
|
||||
|
||||
; Read loop offset (offset 0x1C, relative to 0x1C)
|
||||
LD HL,VGMBUF+#1C
|
||||
LD E,(HL)
|
||||
INC HL
|
||||
LD D,(HL)
|
||||
LD A,D
|
||||
OR E
|
||||
JR Z,VGM_NO_LOOP
|
||||
; VGM_LOOP_ADDR = #C000 + (abs_offset & #3FFF), VGM_LOOP_PAGE = abs_offset >> 14
|
||||
; abs_offset = #1C + value_at_1C (16-битное; достаточно для loop-поинтов < 64KB от начала)
|
||||
LD HL,#001C
|
||||
ADD HL,DE ; HL = абсолютное смещение точки петли
|
||||
LD A,H
|
||||
SRL A:SRL A:SRL A:SRL A:SRL A:SRL A ; A = H >> 6 = номер VPL-страницы
|
||||
LD (VGM_LOOP_PAGE),A
|
||||
LD A,H
|
||||
AND #3F
|
||||
OR #C0
|
||||
LD H,A ; HL = #C000 + (offset & #3FFF)
|
||||
LD (VGM_LOOP_ADDR),HL
|
||||
JR VGM_LOOP_DONE
|
||||
VGM_NO_LOOP:
|
||||
LD HL,0
|
||||
LD (VGM_LOOP_ADDR),HL ; 0 = нет петли
|
||||
VGM_LOOP_DONE:
|
||||
; VGM_END_ADDR и VGM_END_PAGE вычисляются в PLUGIN при загрузке файла
|
||||
|
||||
; Success
|
||||
POP BC
|
||||
POP DE
|
||||
POP HL
|
||||
XOR A ; Z = success
|
||||
RET
|
||||
|
||||
VGM_PARSE_FAIL:
|
||||
POP BC
|
||||
POP DE
|
||||
POP HL
|
||||
OR 1 ; NZ = error
|
||||
RET
|
||||
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
; VGM_FILL_BUFFER
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
; Fill a command buffer with ISR commands from VGM stream.
|
||||
; Input: A = buffer index (0=BUF_A, 1=BUF_B)
|
||||
; Reads from VGM_READ_PTR, updates it.
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
VGM_FILL_BUFFER:
|
||||
PUSH HL
|
||||
PUSH DE
|
||||
PUSH BC
|
||||
PUSH IX
|
||||
|
||||
; If song ended or paused, fill with silence
|
||||
LD B,A ; save buffer index
|
||||
LD A,(SONG_ENDED)
|
||||
OR A
|
||||
JP NZ,VGM_FILL_SILENCE
|
||||
LD A,(PAUSED)
|
||||
OR A
|
||||
JP NZ,VGM_FILL_SILENCE
|
||||
LD A,B ; restore buffer index
|
||||
|
||||
; Get buffer base address
|
||||
OR A
|
||||
JR NZ,VGM_FILL_USE_B
|
||||
LD IX,CMD_BUF_A
|
||||
JR VGM_FILL_START
|
||||
VGM_FILL_USE_B:
|
||||
LD IX,CMD_BUF_B
|
||||
VGM_FILL_START:
|
||||
; ВАЖНО: WC API (напр. UI_UPDATE) могла переключить страницу #C000!
|
||||
; Восстановить правильную VPL-страницу перед чтением VGM.
|
||||
PUSH IX ; MNGCVPL (WLD) может затереть IX
|
||||
LD A,(VGM_CUR_PAGE)
|
||||
CALL MNGCVPL ; отобразить нужную страницу VGM на #C000
|
||||
POP IX
|
||||
|
||||
; DE = write pointer (offset from buffer start)
|
||||
LD DE,0 ; offset 0
|
||||
LD HL,(VGM_READ_PTR) ; HL = VGM read pointer
|
||||
|
||||
VGM_FILL_LOOP:
|
||||
; Check if buffer is almost full (leave room for END_BUF + 1 WAIT)
|
||||
; Buffer = 512 bytes, reserve last 8 bytes
|
||||
; When D>=1 offset>=256, close immediately
|
||||
; When D=0, close if E >= 248 (#F8)
|
||||
LD A,D
|
||||
OR A
|
||||
JP NZ,VGM_FILL_CLOSE ; offset >= 256, definitely close to end
|
||||
LD A,E
|
||||
CP #F8 ; 248 - leave room in low page
|
||||
JP NC,VGM_FILL_CLOSE
|
||||
|
||||
; Проверка конца VGM: сравниваем CUR_PAGE с END_PAGE, затем HL с END_ADDR
|
||||
PUSH DE
|
||||
LD A,(VGM_CUR_PAGE)
|
||||
LD D,A
|
||||
LD A,(VGM_END_PAGE)
|
||||
CP D ; A=END_PAGE, D=CUR_PAGE
|
||||
JR C,VGM_FILL_AT_END ; END_PAGE < CUR_PAGE → прошли конец
|
||||
JR NZ,VGM_FILL_NOT_END ; END_PAGE > CUR_PAGE → ещё не конец
|
||||
; На последней странице: сравниваем HL с VGM_END_ADDR
|
||||
LD DE,(VGM_END_ADDR)
|
||||
LD A,H
|
||||
CP D
|
||||
JR C,VGM_FILL_NOT_END ; H < D
|
||||
JR NZ,VGM_FILL_AT_END ; H > D
|
||||
LD A,L
|
||||
CP E
|
||||
JR C,VGM_FILL_NOT_END ; L < E
|
||||
VGM_FILL_AT_END:
|
||||
POP DE
|
||||
; Song ended
|
||||
LD A,1
|
||||
LD (SONG_ENDED),A
|
||||
JP VGM_FILL_CLOSE
|
||||
VGM_FILL_NOT_END:
|
||||
POP DE
|
||||
|
||||
; Read next VGM opcode (+ проверка границы страницы)
|
||||
LD A,(HL)
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
|
||||
; === 0x5E: YMF262 port 0 write (addr, data) ===
|
||||
CP #5E
|
||||
JP Z,VGM_OP_OPL_PORT0
|
||||
|
||||
; === 0x5F: YMF262 port 1 write (addr, data) ===
|
||||
CP #5F
|
||||
JP Z,VGM_OP_OPL_PORT1
|
||||
|
||||
; === 0x61: Wait N samples (16-bit) ===
|
||||
CP #61
|
||||
JP Z,VGM_OP_WAIT_N
|
||||
|
||||
; === 0x62: Wait 735 samples (1/60 sec) ===
|
||||
CP #62
|
||||
JP Z,VGM_OP_WAIT_60HZ
|
||||
|
||||
; === 0x63: Wait 882 samples (1/50 sec) ===
|
||||
CP #63
|
||||
JP Z,VGM_OP_WAIT_50HZ
|
||||
|
||||
; === 0x66: End of sound data ===
|
||||
CP #66
|
||||
JP Z,VGM_OP_END
|
||||
|
||||
; === 0x70-0x7F: Wait 1-16 samples ===
|
||||
LD B,A
|
||||
AND #F0
|
||||
CP #70
|
||||
JP Z,VGM_OP_WAIT_SHORT
|
||||
|
||||
; Unknown opcode: try to skip it
|
||||
; Most VGM opcodes are 1-3 bytes. Common skips:
|
||||
LD A,B ; restore opcode
|
||||
; 0x3x, 0x4x - single byte (reserved)
|
||||
CP #50
|
||||
JP C,VGM_FILL_LOOP ; < 0x50: skip 1 byte total (already read)
|
||||
; 0x5x - 3 bytes total, we need to skip 2 more
|
||||
AND #F0
|
||||
CP #50
|
||||
JR Z,VGM_SKIP2
|
||||
; 0xAx, 0xBx - 3 bytes
|
||||
LD A,B
|
||||
AND #F0
|
||||
CP #A0
|
||||
JR Z,VGM_SKIP2
|
||||
CP #B0
|
||||
JR Z,VGM_SKIP2
|
||||
; 0xCx, 0xDx - 4 bytes
|
||||
CP #C0
|
||||
JR Z,VGM_SKIP3
|
||||
CP #D0
|
||||
JR Z,VGM_SKIP3
|
||||
; 0xEx, 0xFx - 5 bytes
|
||||
CP #E0
|
||||
JR Z,VGM_SKIP4
|
||||
CP #F0
|
||||
JR Z,VGM_SKIP4
|
||||
; Default: skip nothing more
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
VGM_SKIP2:
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
JP VGM_FILL_LOOP
|
||||
VGM_SKIP3:
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
JP VGM_FILL_LOOP
|
||||
VGM_SKIP4:
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
; --- OPL3 port 0 write: emit WRITE addr0, WRITE data0 ---
|
||||
VGM_OP_OPL_PORT0:
|
||||
; Read register number and value
|
||||
LD B,(HL) ; register number
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
LD C,(HL) ; value
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
|
||||
; Emit: WRITE_B0 reg+val (банк 0)
|
||||
PUSH HL
|
||||
PUSH IX
|
||||
POP HL ; HL = buffer base
|
||||
ADD HL,DE ; HL = current write position
|
||||
LD (HL),CMD_WRITE_B0 ; command
|
||||
INC HL
|
||||
LD (HL),B ; register number
|
||||
INC HL
|
||||
LD (HL),C ; value
|
||||
INC HL
|
||||
LD (HL),0 ; padding
|
||||
POP HL ; restore VGM read ptr
|
||||
|
||||
; Advance write offset by 4
|
||||
LD A,E
|
||||
ADD A,4
|
||||
LD E,A
|
||||
JP NC,VGM_FILL_LOOP
|
||||
INC D
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
; --- OPL3 port 1 write: emit WRITE addr1, WRITE data1 ---
|
||||
VGM_OP_OPL_PORT1:
|
||||
LD B,(HL) ; register number
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
LD C,(HL) ; value
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
|
||||
PUSH HL
|
||||
PUSH IX
|
||||
POP HL
|
||||
ADD HL,DE
|
||||
LD (HL),CMD_WRITE_B1 ; command
|
||||
INC HL
|
||||
LD (HL),B ; register number (банк 1)
|
||||
INC HL
|
||||
LD (HL),C ; value
|
||||
INC HL
|
||||
LD (HL),0 ; padding
|
||||
POP HL
|
||||
|
||||
LD A,E
|
||||
ADD A,4
|
||||
LD E,A
|
||||
JP NC,VGM_FILL_LOOP
|
||||
INC D
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
; --- Wait N samples (16-bit LE) ---
|
||||
; ISR freq = 2734 Hz, VGM samples = 44100 Hz. Factor = 44100/2734 ~ 16.13.
|
||||
; Approximation: ISR_ticks = VGM_samples >> 4 (divide by 16)
|
||||
; Ignore delays < 16 samples (too small to represent), emit nothing.
|
||||
VGM_OP_WAIT_N:
|
||||
LD C,(HL) ; ticks low
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
LD B,(HL) ; ticks high
|
||||
INC HL
|
||||
CALL VGM_CHK_PAGE
|
||||
; BC = N samples. Divide by 16: shift right 4.
|
||||
SRL B
|
||||
RR C
|
||||
SRL B
|
||||
RR C
|
||||
SRL B
|
||||
RR C
|
||||
SRL B
|
||||
RR C ; ISR_ticks in BC (= N/16)
|
||||
; If BC == 0 (N < 16): skip
|
||||
LD A,B
|
||||
OR C
|
||||
JP Z,VGM_FILL_LOOP
|
||||
; ISR_WAIT_CTR = ticks - 1
|
||||
DEC BC
|
||||
|
||||
PUSH HL
|
||||
PUSH IX
|
||||
POP HL
|
||||
ADD HL,DE
|
||||
LD (HL),CMD_WAIT
|
||||
INC HL
|
||||
LD (HL),C ; ticks low
|
||||
INC HL
|
||||
LD (HL),B ; ticks high
|
||||
INC HL
|
||||
LD (HL),0 ; padding
|
||||
POP HL
|
||||
|
||||
LD A,E
|
||||
ADD A,4
|
||||
LD E,A
|
||||
JP NC,VGM_FILL_LOOP
|
||||
INC D
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
; --- Wait 735 samples (1/60 sec) ---
|
||||
VGM_OP_WAIT_60HZ:
|
||||
; 735/16 = 45 ISR ticks, -1 = 44 = #2C
|
||||
PUSH HL
|
||||
PUSH IX
|
||||
POP HL
|
||||
ADD HL,DE
|
||||
LD (HL),CMD_WAIT
|
||||
INC HL
|
||||
LD (HL),#2C ; 44 low
|
||||
INC HL
|
||||
LD (HL),#00 ; 44 high
|
||||
INC HL
|
||||
LD (HL),0
|
||||
POP HL
|
||||
|
||||
LD A,E
|
||||
ADD A,4
|
||||
LD E,A
|
||||
JP NC,VGM_FILL_LOOP
|
||||
INC D
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
; --- Wait 882 samples (1/50 sec) ---
|
||||
VGM_OP_WAIT_50HZ:
|
||||
; 882/16 = 55 ISR ticks, -1 = 54 = #36
|
||||
PUSH HL
|
||||
PUSH IX
|
||||
POP HL
|
||||
ADD HL,DE
|
||||
LD (HL),CMD_WAIT
|
||||
INC HL
|
||||
LD (HL),#36 ; 54 low
|
||||
INC HL
|
||||
LD (HL),#00 ; 54 high
|
||||
INC HL
|
||||
LD (HL),0
|
||||
POP HL
|
||||
|
||||
LD A,E
|
||||
ADD A,4
|
||||
LD E,A
|
||||
JP NC,VGM_FILL_LOOP
|
||||
INC D
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
; --- Wait 1-16 samples (opcode 0x70-0x7F) ---
|
||||
; N = low nibble (0-15) = wait (N+1) samples.
|
||||
; (N+1) >> 2: if < 1, skip; otherwise emit WAIT with value-1.
|
||||
VGM_OP_WAIT_SHORT:
|
||||
LD A,B ; restore opcode
|
||||
AND #0F ; N = 0..15, actual wait = N+1 samples
|
||||
INC A ; A = 1..16 samples
|
||||
SRL A ; /2
|
||||
SRL A ; /4
|
||||
SRL A ; /8
|
||||
SRL A ; /16
|
||||
JP Z,VGM_FILL_LOOP ; < 16 samples: skip entirely
|
||||
; WAIT_CTR = A - 1
|
||||
DEC A
|
||||
LD C,A
|
||||
|
||||
PUSH HL
|
||||
PUSH IX
|
||||
POP HL
|
||||
ADD HL,DE
|
||||
LD (HL),CMD_WAIT
|
||||
INC HL
|
||||
LD (HL),C ; ticks low
|
||||
INC HL
|
||||
LD (HL),0 ; ticks high = 0
|
||||
INC HL
|
||||
LD (HL),0
|
||||
POP HL
|
||||
|
||||
LD A,E
|
||||
ADD A,4
|
||||
LD E,A
|
||||
JP NC,VGM_FILL_LOOP
|
||||
INC D
|
||||
JP VGM_FILL_LOOP
|
||||
|
||||
; --- End of VGM data ---
|
||||
VGM_OP_END:
|
||||
LD A,1
|
||||
LD (SONG_ENDED),A
|
||||
; Fall through to close buffer
|
||||
|
||||
; --- Close buffer: emit CMD_END_BUF ---
|
||||
VGM_FILL_CLOSE:
|
||||
LD (VGM_READ_PTR),HL ; save VGM read position
|
||||
|
||||
; Write CMD_END_BUF at current write position
|
||||
PUSH IX
|
||||
POP HL
|
||||
ADD HL,DE
|
||||
LD (HL),CMD_END_BUF
|
||||
INC HL
|
||||
LD (HL),0
|
||||
INC HL
|
||||
LD (HL),0
|
||||
INC HL
|
||||
LD (HL),0
|
||||
|
||||
POP IX
|
||||
POP BC
|
||||
POP DE
|
||||
POP HL
|
||||
RET
|
||||
|
||||
; --- Fill silence buffer (for paused/ended state) ---
|
||||
VGM_FILL_SILENCE:
|
||||
; Buffer index in B
|
||||
LD A,B
|
||||
OR A
|
||||
JR NZ,VGM_SIL_USE_B
|
||||
LD IX,CMD_BUF_A
|
||||
JR VGM_SIL_FILL
|
||||
VGM_SIL_USE_B:
|
||||
LD IX,CMD_BUF_B
|
||||
VGM_SIL_FILL:
|
||||
; Fill with: WAIT 54 (~20ms at 2734Hz), END_BUF
|
||||
PUSH IX
|
||||
POP HL
|
||||
LD (HL),CMD_WAIT
|
||||
INC HL
|
||||
LD (HL),#36 ; 54 low (= ~20ms)
|
||||
INC HL
|
||||
LD (HL),#00 ; 54 high
|
||||
INC HL
|
||||
LD (HL),0
|
||||
INC HL
|
||||
LD (HL),CMD_END_BUF
|
||||
INC HL
|
||||
LD (HL),0
|
||||
INC HL
|
||||
LD (HL),0
|
||||
INC HL
|
||||
LD (HL),0
|
||||
|
||||
POP IX
|
||||
POP BC
|
||||
POP DE
|
||||
POP HL
|
||||
RET
|
||||
|
||||
; ─── VGM_CHK_PAGE: проверка границы VPL-страницы после INC HL ─────────────
|
||||
; Если HL = #0000 (переход #FFFF→#0000), отображает следующую VPL-страницу
|
||||
; на #C000 и устанавливает HL = #C000.
|
||||
; Сохраняет A (через PUSH/POP AF). Может изменить HL при смене страницы.
|
||||
VGM_CHK_PAGE:
|
||||
PUSH AF
|
||||
LD A,H
|
||||
OR L
|
||||
JR NZ,VGM_CHK_RET
|
||||
; Граница страницы: VGM_CUR_PAGE++, новая страница → #C000
|
||||
; WC API (MNGCVPL) может затирать BC и DE — сохраняем их!
|
||||
PUSH BC
|
||||
PUSH DE
|
||||
PUSH IX ; WLD (WC API) затирает IX!
|
||||
LD A,(VGM_CUR_PAGE)
|
||||
INC A
|
||||
LD (VGM_CUR_PAGE),A
|
||||
CALL MNGCVPL ; A = новый VPL-индекс → отобразить на #C000
|
||||
POP IX
|
||||
POP DE
|
||||
POP BC
|
||||
LD HL,#C000
|
||||
VGM_CHK_RET:
|
||||
POP AF
|
||||
RET
|
||||
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
; VGM_FILL_INACTIVE_BUF (called from main loop)
|
||||
; ═══════════════════════════════════════════════════════════════════════════
|
||||
; Check which buffer ISR is reading, fill the other one if it changed.
|
||||
; This is just a wrapper; main loop handles tracking in MAIN_LOOP.
|
||||
; Kept for potential future use.
|
||||
Reference in New Issue
Block a user