826 lines
27 KiB
NASM
826 lines
27 KiB
NASM
; if this code looks like HLL vomit, you're pretty close
|
|
; i used index registers since speed is not critical, it's a music compo player after all ;)
|
|
|
|
include "opmfile.inc"
|
|
|
|
module player
|
|
|
|
MAX_CHANNELS equ 16
|
|
MAX_STACK_DEPTH equ 4
|
|
|
|
; channel structures format
|
|
struct channel_stack_t
|
|
ptr dw 0
|
|
frames_to_play dw 0
|
|
ends
|
|
|
|
struct channel_struct_t
|
|
page db 0 ; base page with stream data
|
|
ptr dw 0 ; current pointer
|
|
frames_to_play dw -1 ; used for stack popping
|
|
delay dw 1
|
|
reload dw 1
|
|
stack_pos db 0 ; stack offset
|
|
stack block channel_stack_t*MAX_STACK_DEPTH, 0 ; stack
|
|
|
|
; reg cache
|
|
reg_extch3_fhi block 3, 0 ; extch3 block/fnum-high reg
|
|
reg_fhi db 0 ; block/fnum-high reg
|
|
reserved block 2, 0
|
|
ends
|
|
|
|
struct player_struct_t
|
|
ch3_ofs_chip0 dw 0
|
|
ch3_ofs_chip1 dw 0
|
|
chip_idx db 0 ; current chip active
|
|
channel_idx db 0
|
|
end_of_frame db 0
|
|
do_next_op db 0
|
|
ssg_r7 block 2, 0
|
|
ends
|
|
|
|
; instantinate structures
|
|
align 256
|
|
player_channels:
|
|
channel_struct_t 0, 0 ; TODO: fill start offsets here
|
|
channel_struct_t 0, 0
|
|
channel_struct_t 0, 0
|
|
channel_struct_t 0, 0
|
|
channel_struct_t 2, music_p0_ch4
|
|
channel_struct_t 2, music_p0_ch5
|
|
channel_struct_t 2, music_p0_ch6
|
|
channel_struct_t 2, music_p0_ch7
|
|
channel_struct_t 0, 0
|
|
channel_struct_t 0, 0
|
|
channel_struct_t 0, 0
|
|
channel_struct_t 0, 0
|
|
channel_struct_t 2, music_p0_ch12
|
|
channel_struct_t 2, music_p0_ch13
|
|
channel_struct_t 2, music_p0_ch14
|
|
channel_struct_t 2, music_p0_ch15
|
|
|
|
player_struct player_struct_t player_channels+(channel_struct_t*(3+0)+channel_struct_t.reg_extch3_fhi), player_channels+(channel_struct_t*(3+8)+channel_struct_t.reg_extch3_fhi)
|
|
|
|
.i = 0
|
|
dup 16
|
|
display "channel ", /h, .i, " struct ofs: ", /h, player_channels+(channel_struct_t*.i)
|
|
.i = .i + 1
|
|
edup
|
|
|
|
; -----------------------------
|
|
; -----------------------------
|
|
; OPN register dump output
|
|
; in:
|
|
; BC = 0xFFFD
|
|
; DE = 0x00C0 ; 0xFFBF compensated for outi
|
|
; HL - registers in (reg:data) byte pairs, reg==0xFF - return
|
|
reg_out:
|
|
.loop:
|
|
ld b, d
|
|
ld a, [hl]
|
|
inc a ; test for 0xFF terminator
|
|
ret z
|
|
outi
|
|
ld b, e
|
|
outi
|
|
dup 4: nop : edup ; delay
|
|
jp .loop
|
|
|
|
; ------------------------
|
|
; pop from stack
|
|
; in: IX - channel context
|
|
; trashes A, DE
|
|
pop_stack:
|
|
dec [ix + channel_struct_t.stack_pos] ; --chctx->stack_pos;
|
|
ld l, [ix + channel_struct_t.stack_pos] : ld h, 0 ; hl = chctx->stack_pos;
|
|
add hl, hl ; *= 2
|
|
add hl, hl ; *= 4
|
|
ld de, channel_struct_t.stack
|
|
add hl, de
|
|
ld de, ix
|
|
add hl, de ; hl = st = chctx->stack + chctx->stack_pos;
|
|
|
|
ld e, [hl] : inc l
|
|
ld d, [hl] : inc l ; de = st->ptr
|
|
ld [ix + channel_struct_t.ptr], de ; chctx->ptr = st->ptr
|
|
|
|
ld e, [hl] : inc l
|
|
ld d, [hl] ; de = st->frames_to_play
|
|
ld [ix + channel_struct_t.frames_to_play], de ; chctx->frames_to_play = st->frames_to_play
|
|
|
|
ret
|
|
|
|
; ------------------------
|
|
; push to stack
|
|
; in: IX - channel context
|
|
; trashes A, DE
|
|
push_stack:
|
|
ld l, [ix + channel_struct_t.stack_pos] : ld h, 0 ; hl = chctx->stack_pos;
|
|
add hl, hl ; *= 2
|
|
add hl, hl ; *= 4
|
|
ld de, channel_struct_t.stack
|
|
add hl, de
|
|
ld de, ix
|
|
add hl, de ; hl = st = chctx->stack + chctx->stack_pos;
|
|
|
|
ld de, [ix + channel_struct_t.ptr]
|
|
ld [hl], e : inc l
|
|
ld [hl], d : inc l ; st->ptr = chctx->ptr
|
|
|
|
ld de, [ix + channel_struct_t.frames_to_play]
|
|
ld [hl], e : inc l
|
|
ld [hl], d : inc l ; st->frames_to_play = chctx->frames_to_play
|
|
|
|
inc [ix + channel_struct_t.stack_pos]
|
|
ret
|
|
|
|
; ------------------------
|
|
; set delay
|
|
; in: HL - stream ptr
|
|
; out: DE - delay, trashes A
|
|
set_delay:
|
|
ld a, [hl]
|
|
cp OPM_STREAM_DELAY_INT16
|
|
jp z, .int16
|
|
and 0xF0
|
|
cp OPM_STREAM_DELAY_INT12
|
|
jp z, .int12
|
|
cp OPM_STREAM_DELAY_SHORT
|
|
jp z, .short
|
|
ret
|
|
|
|
.int16:
|
|
inc hl
|
|
ldi de, [hl]
|
|
ret
|
|
|
|
.int12:
|
|
ldi a, [hl] : and 0x0F : ld d, a
|
|
ldi e, [hl]
|
|
ret
|
|
|
|
.short:
|
|
ldi a, [hl] : and 0x0F : inc a : ld e, a : ld d, 0
|
|
ret
|
|
|
|
; ------------------------------------------
|
|
; parse AY channel stream
|
|
; HL - stream data, IX- channel context, IY - register buffer, CF - do next op
|
|
parse_ay_channel_stream:
|
|
ld b, [hl]
|
|
; if ((*(data) & 0x80) == OPM_AYTONE_REGS) {
|
|
ld a, b : and 0x80 : cp OPM_AYTONE_REGS
|
|
jp nz, .not_regs
|
|
|
|
; volume and period low
|
|
; opn_write_reg(chip_index, 8 + ch, (mask & OPM_AYTONE_CMD00_VOLUME_MASK));
|
|
inc hl
|
|
ld a, [player_struct.channel_idx]
|
|
add 8
|
|
ldi [iy], a
|
|
ld a, b : and OPM_AYTONE_CMD00_VOLUME_MASK
|
|
ldi [iy], a
|
|
|
|
; if (mask & OPM_AYTONE_CMD00_PERIOD_LOW) opn_write_reg(chip_index, 0 + (ch<<1), *data++);
|
|
bit OPM_AYTONE_CMD00_PERIOD_LOW_BIT, b
|
|
jp z, 1f
|
|
ld a, [player_struct.channel_idx]
|
|
add a
|
|
ldi [iy], a
|
|
ldi a, [hl]
|
|
ldi [iy], a
|
|
|
|
1:
|
|
; if (mask & OPM_AYTONE_CMD00_EOF) endOfFrame = true;
|
|
bit OPM_AYTONE_CMD00_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
.not_regs:
|
|
; if ((*(data) & 0xC0) == OPM_AYTONE_PERIOD) {
|
|
ld a, b : and 0xC0 : cp OPM_AYTONE_PERIOD
|
|
jp nz, .not_period
|
|
|
|
; period low/high
|
|
; opn_write_reg(chip_index, 1 + (ch<<1), (mask & OPM_AYTONE_CMD80_PERIOD_HIGH));
|
|
inc hl
|
|
ld a, [player_struct.channel_idx]
|
|
add a
|
|
add 1
|
|
ldi [iy], a
|
|
exa
|
|
ld a, b : and OPM_AYTONE_CMD80_PERIOD_HIGH
|
|
ldi [iy], a
|
|
exa
|
|
|
|
; if (mask & OPM_AYTONE_CMD80_PERIOD_LOW) opn_write_reg(chip_index, 0 + (ch << 1), *data++);
|
|
bit OPM_AYTONE_CMD80_PERIOD_LOW_BIT, b
|
|
jp z, 1f
|
|
dec a
|
|
ldi [iy], a
|
|
ldi a, [hl]
|
|
ldi [iy], a
|
|
1:
|
|
|
|
; if (mask & OPM_AYTONE_CMD00_EOF) endOfFrame = true;
|
|
bit OPM_AYTONE_CMD80_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
|
|
.not_period:
|
|
; if ((*(data) & 0xF8) == OPM_AYTONE_MASK) {
|
|
ld a, b : and 0xF8 : cp OPM_AYTONE_MASK
|
|
jp nz, .not_mask
|
|
|
|
inc hl
|
|
push de, hl, ix
|
|
|
|
; ctx->ssg_r7[chip_index] &= ~(((1 << 3) | (1 << 0)) << ch);
|
|
ld a, [player_struct.chip_idx] : ld e, a : ld d, 0
|
|
ld ix, player_struct.ssg_r7
|
|
add ix, de ; ix = ctx->ssg_r7[chip_index]
|
|
|
|
ld a, [player_struct.channel_idx] : ld e, a
|
|
ld hl, .mask_channel
|
|
add hl, de
|
|
ld a, [ix] : and [hl] : exa ; a = ctx->ssg_r7[chip_index] &= ~(((1 << 3) | (1 << 0)) << ch);
|
|
|
|
sla e : sla e
|
|
ld a, b : and 3 : add e : ld e, a
|
|
ld hl, .mask_lookup
|
|
add hl, de
|
|
exa : or [hl] : ld [ix], a ; ctx->ssg_r7[chip_index] |= (mask_lookup[mask & 3] << ch);
|
|
|
|
pop ix, hl, de
|
|
|
|
; if (mask & OPM_AYTONE_MASK_EOF) endOfFrame = true;
|
|
bit OPM_AYTONE_MASK_EOF_BIT, b
|
|
jp z, 1f
|
|
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
|
|
scf
|
|
ret
|
|
|
|
.mask_lookup:
|
|
.p = 0
|
|
dup 3
|
|
db ((0<<3)|(0<<0)) << .p, ((0<<3)|(1<<0)) << .p, ((1<<3)|(0<<0)) << .p, ((1<<3)|(1<<0)) << .p
|
|
.p = .p + 1
|
|
edup
|
|
.mask_channel:
|
|
.p = 0
|
|
dup 3
|
|
db ~(((1<<3)|(1<<0)) << .p)
|
|
.p = .p + 1
|
|
edup
|
|
|
|
.not_mask:
|
|
.done:
|
|
and a ; clear carry
|
|
ret
|
|
|
|
; parse AY envelope/noise stream
|
|
; HL - stream data, IX- channel context, IY - register buffer, CF - do next op
|
|
parse_ay_envnoise_stream:
|
|
ld b, [hl]
|
|
; if ((*(data) & 0x80) == OPM_AYENV_REGS) {
|
|
ld a, b : and 0x80 : cp OPM_AYENV_REGS
|
|
jp nz, .not_regs
|
|
|
|
; opn_write_reg(chip_index, 6, (mask & OPM_AYENV_CMD00_NOISE_MASK));
|
|
inc hl
|
|
ldi [iy], 6
|
|
ld a, b : and OPM_AYENV_CMD00_NOISE_MASK
|
|
ldi [iy], a
|
|
|
|
; if (mask & OPM_AYENV_CMD00_PERIOD_LOW) opn_write_reg(chip_index, 11, *data++);
|
|
bit OPM_AYENV_CMD00_PERIOD_LOW_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 11
|
|
ldi a, [hl]
|
|
ldi [iy], a
|
|
1:
|
|
|
|
; if (mask & OPM_AYENV_CMD00_EOF) endOfFrame = true;
|
|
bit OPM_AYENV_CMD00_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
|
|
scf
|
|
ret
|
|
|
|
.not_regs:
|
|
; if ((*(data) & 0xC0) == OPM_AYENV_ENVTYPE) {
|
|
ld a, b : and 0xC0 : cp OPM_AYENV_ENVTYPE
|
|
jp nz, .not_envtype
|
|
|
|
; opn_write_reg(chip_index, 13, (mask & OPM_AYENV_CMD80_ENV_TYPE));
|
|
inc hl
|
|
ldi [iy], 13
|
|
ld a, b : and OPM_AYENV_CMD80_ENV_TYPE
|
|
ldi [iy], a
|
|
|
|
; if (mask & OPM_AYENV_CMD80_PERIOD_LOW) opn_write_reg(chip_index, 11, *data++);
|
|
bit OPM_AYENV_CMD80_PERIOD_LOW_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 11
|
|
ldi a, [hl]
|
|
ldi [iy], a
|
|
1:
|
|
|
|
; if (mask & OPM_AYENV_CMD80_EOF) endOfFrame = true;
|
|
bit OPM_AYENV_CMD80_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
|
|
scf
|
|
ret
|
|
|
|
.not_envtype:
|
|
; if ((*(data) & 0xF8) == OPM_AYENV_PERIOD_FULL) {
|
|
ld a, b : and 0xF8 : cp OPM_AYENV_PERIOD_FULL
|
|
jp nz, .not_period
|
|
|
|
inc hl
|
|
; if (mask & OPM_AYENV_CMDF0_PERIOD_LOW) opn_write_reg(chip_index, 11, *data++);
|
|
bit OPM_AYENV_CMDF0_PERIOD_LOW_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 11
|
|
ldi a, [hl]
|
|
ldi [iy], a
|
|
1:
|
|
; if (mask & OPM_AYENV_CMDF0_PERIOD_HIGH) opn_write_reg(chip_index, 12, *data++);
|
|
bit OPM_AYENV_CMDF0_PERIOD_HIGH_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 12
|
|
ldi a, [hl]
|
|
ldi [iy], a
|
|
1:
|
|
; if (mask & OPM_AYENV_CMDF0_EOF) endOfFrame = true;
|
|
bit OPM_AYENV_CMDF0_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
|
|
scf
|
|
ret
|
|
|
|
.not_period:
|
|
and a ; clear carry
|
|
ret
|
|
|
|
; parse FM control stream
|
|
; HL - stream data, IX- channel context, IY - register buffer, CF - do next op
|
|
parse_fm_control_stream:
|
|
ld b, [hl]
|
|
; if ((*(data) & 0xE0) == OPM_CTRL_TIMER_CSM) {
|
|
ld a, b : and 0xE0 : cp OPM_CTRL_TIMER_CSM
|
|
jp nz, .not_timer_csm
|
|
|
|
inc hl
|
|
;if (mask & OPM_CTRL_CMD80_REG25) opn_write_reg(chip_index, 0x25, *data++);
|
|
bit OPM_CTRL_CMD80_REG25_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 0x25 : ldi a, [hl] : ldi [iy], a
|
|
1:
|
|
;if (mask & OPM_CTRL_CMD80_REG24) opn_write_reg(chip_index, 0x24, *data++);
|
|
bit OPM_CTRL_CMD80_REG24_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 0x24 : ldi a, [hl] : ldi [iy], a
|
|
1:
|
|
;if (mask & OPM_CTRL_CMD80_REG27) opn_write_reg(chip_index, 0x27, *data++);
|
|
bit OPM_CTRL_CMD80_REG27_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 0x27 : ldi a, [hl] : ldi [iy], a
|
|
1:
|
|
;if (mask & OPM_CTRL_CMD80_REG22) opn_write_reg(chip_index, 0x22, *data++);
|
|
bit OPM_CTRL_CMD80_REG25_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 0x22 : ldi a, [hl] : ldi [iy], a
|
|
1:
|
|
;if (mask & OPM_CTRL_CMD80_EOF) endOfFrame = true;
|
|
bit OPM_CTRL_CMD80_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
.not_timer_csm:
|
|
; if ((*(data) & 0x80) == OPM_CTRL_EXTCH3) {
|
|
ld a, b : and 0x80 : cp OPM_CTRL_EXTCH3
|
|
jp nz, .not_extch3
|
|
|
|
inc hl
|
|
; extract extch3 offset
|
|
push hl
|
|
ld a, [player_struct.chip_idx] : add a : ld e, a : ld d, 0
|
|
ld hl, player_struct.ch3_ofs_chip0 : add hl, de
|
|
ld e, [hl] : inc hl : ld d, [hl] ; de - channel 3 extch3 block
|
|
pop hl
|
|
|
|
; if (mask & OPM_CTRL_EXTCH3_OP1_HIGH) ctx->extch3_block[chip_index][0] = *data++;
|
|
; if (mask & OPM_CTRL_EXTCH3_OP1_LOW) {
|
|
; opn_write_reg(chip_index, 0xAD, ctx->extch3_block[chip_index][0]);
|
|
; opn_write_reg(chip_index, 0xA9, *data++);
|
|
; }
|
|
bit OPM_CTRL_EXTCH3_OP1_HIGH_BIT, b
|
|
jp z, 1f
|
|
ld a, [hl] : ld [de], a : inc hl
|
|
1:
|
|
bit OPM_CTRL_EXTCH3_OP1_LOW_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 0xAD : ld a, [de] : ldi [iy], a
|
|
ldi [iy], 0xA9 : ldi a, [hl] : ldi [iy], a
|
|
1:
|
|
inc e
|
|
; if (mask & OPM_CTRL_EXTCH3_OP2_HIGH) ctx->extch3_block[chip_index][1] = *data++;
|
|
; if (mask & OPM_CTRL_EXTCH3_OP2_LOW) {
|
|
; opn_write_reg(chip_index, 0xAC, ctx->extch3_block[chip_index][1]);
|
|
; opn_write_reg(chip_index, 0xA8, *data++);
|
|
; }
|
|
bit OPM_CTRL_EXTCH3_OP2_HIGH_BIT, b
|
|
jp z, 1f
|
|
ld a, [hl] : ld [de], a : inc hl
|
|
1:
|
|
bit OPM_CTRL_EXTCH3_OP2_LOW_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 0xAC : ld a, [de] : ldi [iy], a
|
|
ldi [iy], 0xA8 : ldi a, [hl] : ldi [iy], a
|
|
1:
|
|
inc e
|
|
|
|
; if (mask & OPM_CTRL_EXTCH3_OP3_HIGH) ctx->extch3_block[chip_index][2] = *data++;
|
|
; if (mask & OPM_CTRL_EXTCH3_OP3_LOW) {
|
|
; opn_write_reg(chip_index, 0xAE, ctx->extch3_block[chip_index][2]);
|
|
; opn_write_reg(chip_index, 0xAA, *data++);
|
|
; }
|
|
bit OPM_CTRL_EXTCH3_OP3_HIGH_BIT, b
|
|
jp z, 1f
|
|
ld a, [hl] : ld [de], a : inc hl
|
|
1:
|
|
bit OPM_CTRL_EXTCH3_OP3_LOW_BIT, b
|
|
jp z, 1f
|
|
ldi [iy], 0xAE : ld a, [de] : ldi [iy], a
|
|
ldi [iy], 0xAA : ldi a, [hl] : ldi [iy], a
|
|
1:
|
|
|
|
; if (mask & OPM_CTRL_EXTCH3_EOF) endOfFrame = true;
|
|
bit OPM_CTRL_EXTCH3_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
.not_extch3:
|
|
and a ; clear carry
|
|
ret
|
|
|
|
; parse FM channel stream
|
|
; HL - stream data, IX- channel context, IY - register buffer, CF - do next op
|
|
parse_fm_channel_stream:
|
|
ld b, [hl]
|
|
; if ((*(data) & 0xC0) == OPM_FM_ADSR) {
|
|
ld a, b : and 0xC0 : cp OPM_FM_ADSR
|
|
jp nz, .not_adsr
|
|
|
|
inc hl
|
|
; int regbase = (ch & 3) + ((mask & OPM_FM_CMD00_OP_MASK) >> 2);
|
|
ld a, [player_struct.channel_idx] : ld c, a
|
|
ld a, b : and OPM_FM_CMD00_OP_MASK : rrca : rrca
|
|
or c : ld c, a
|
|
|
|
;if (mask & OPM_FM_CMD00_REG50) opn_write_reg(chip_index, 0x50 + regbase, *data++);
|
|
bit OPM_FM_CMD00_REG50_BIT, b
|
|
jp z, 1f
|
|
ld a, 0x50 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
;if (mask & OPM_FM_CMD00_REG60) opn_write_reg(chip_index, 0x60 + regbase, *data++);
|
|
bit OPM_FM_CMD00_REG60_BIT, b
|
|
jp z, 1f
|
|
ld a, 0x60 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
;if (mask & OPM_FM_CMD00_REG70) opn_write_reg(chip_index, 0x70 + regbase, *data++);
|
|
bit OPM_FM_CMD00_REG70_BIT, b
|
|
jp z, 1f
|
|
ld a, 0x70 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
;if (mask & OPM_FM_CMD00_REG80) opn_write_reg(chip_index, 0x80 + regbase, *data++);
|
|
bit OPM_FM_CMD00_REG80_BIT, b
|
|
jp z, 1f
|
|
ld a, 0x80 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
.not_adsr:
|
|
; if ((*(data) & 0xC0) == OPM_FM_MUL_TL_EG) {
|
|
ld a, b : and 0xC0 : cp OPM_FM_MUL_TL_EG
|
|
jp nz, .not_mul_tl_eg
|
|
|
|
; multplier\TL\SSG-EG
|
|
inc hl
|
|
; int regbase = (ch & 3) + ((mask & OPM_FM_CMD40_OP_MASK) >> 2);
|
|
ld a, [player_struct.channel_idx] : ld c, a
|
|
ld a, b : and OPM_FM_CMD40_OP_MASK : rrca : rrca
|
|
or c : ld c, a
|
|
|
|
;if (mask & OPM_FM_CMD40_REG30) opn_write_reg(chip_index, 0x30 + regbase, *data++);
|
|
bit OPM_FM_CMD40_REG30_BIT, b
|
|
jp z, 1f
|
|
ld a, 0x30 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
;if (mask & OPM_FM_CMD40_REG40) opn_write_reg(chip_index, 0x40 + regbase, *data++);
|
|
bit OPM_FM_CMD40_REG40_BIT, b
|
|
jp z, 1f
|
|
ld a, 0x40 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
;if (mask & OPM_FM_CMD40_REG90) opn_write_reg(chip_index, 0x90 + regbase, *data++);
|
|
bit OPM_FM_CMD40_REG90_BIT, b
|
|
jp z, 1f
|
|
ld a, 0x90 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
;if (mask & OPM_FM_CMD40_EOF) endOfFrame = true;
|
|
bit OPM_FM_CMD40_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
.not_mul_tl_eg:
|
|
; if ((*(data) & 0xE0) == OPM_FM_FREQ_FB_PAN) {
|
|
ld a, b : and 0xE0 : cp OPM_FM_FREQ_FB_PAN
|
|
jp nz, .not_freq_fb_pan
|
|
|
|
inc hl
|
|
ld a, [player_struct.channel_idx] : ld c, a
|
|
|
|
; if (mask & OPM_FM_CMD80_REGA4) chctx->block = *data++;
|
|
bit OPM_FM_CMD80_REGA4_BIT, b
|
|
jp z, 1f
|
|
ldi a, [hl] : ld [ix + channel_struct_t.reg_fhi], a
|
|
1:
|
|
; if (mask & OPM_FM_CMD80_REGA0) {
|
|
; opn_write_reg(chip_index, 0xA4 + ch, chctx->block);
|
|
; opn_write_reg(chip_index, 0xA0 + ch, *data++);
|
|
; }
|
|
bit OPM_FM_CMD80_REGA0_BIT, b
|
|
jp z, 1f
|
|
ld a, 0xA4 : add c : ldi [iy], a
|
|
ld a, [ix + channel_struct_t.reg_fhi] : ldi [iy], a
|
|
ld a, 0xA0 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
; if (mask & OPM_FM_CMD80_REGB0) opn_write_reg(chip_index, 0xB0 + ch, *data++);
|
|
bit OPM_FM_CMD80_REGB0_BIT, b
|
|
jp z, 1f
|
|
ld a, 0xb0 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
; if (mask & OPM_FM_CMD80_REGB4) opn_write_reg(chip_index, 0xB4 + ch, *data++);
|
|
bit OPM_FM_CMD80_REGB4_BIT, b
|
|
jp z, 1f
|
|
ld a, 0xb4 : add c : ldi [iy], a
|
|
ldi a, [hl] : ld [iy], a
|
|
1:
|
|
; if (mask & OPM_FM_CMD80_EOF) endOfFrame = true;
|
|
bit OPM_FM_CMD80_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
.not_freq_fb_pan:
|
|
; if ((*(data) & 0xE0) == OPM_FM_KEY) {
|
|
ld a, b : and 0xE0 : cp OPM_FM_KEY
|
|
jp nz, .not_key
|
|
|
|
; opn_write_reg(chip_index, 0x28, ((mask & OPM_FM_CMDA0_OP_MASK) << 4) + ch);
|
|
ld a, [player_struct.channel_idx] : ld c, a
|
|
ld a, b : and OPM_FM_CMDA0_OP_MASK
|
|
add a, a : add a, a : add a, a : add a, a : add a, c
|
|
ldi [iy], 0x28 : ldi [iy], a
|
|
1:
|
|
; if (mask & OPM_FM_CMDA0_EOF) endOfFrame = true;
|
|
bit OPM_FM_CMDA0_EOF_BIT, b
|
|
jp z, 1f
|
|
ld a, b : ld [player_struct.end_of_frame], a
|
|
1:
|
|
scf
|
|
ret
|
|
|
|
.not_key:
|
|
and a ; clear carry
|
|
ret
|
|
|
|
; --------------------------------
|
|
; parse channel stream
|
|
; in: IX - stream struct, IY - register buffer, BC - stream parser procedure, A - channel index
|
|
parse_stream:
|
|
; endOfFrame = false;
|
|
xor a : ld [player_struct.end_of_frame], a
|
|
|
|
; if (--chctx->stream.delay == 0) {
|
|
ld de, [ix+channel_struct_t.delay]
|
|
dec de
|
|
ld [ix+channel_struct_t.delay], de
|
|
ld a, d
|
|
or e
|
|
ret nz
|
|
|
|
; save proc ptr
|
|
ld [.proc], bc
|
|
|
|
; load stream pointer
|
|
ld a, [ix+channel_struct_t.page]
|
|
ld bc, 0x7FFD
|
|
out (c), a
|
|
ld de, [ix+channel_struct_t.ptr]
|
|
exd ; hl = chctx->stream.ptr
|
|
|
|
.tok_loop:
|
|
ld a, [player_struct.end_of_frame] : and a : jp nz, .end_of_frame
|
|
|
|
; common tokens
|
|
ld a, [hl]
|
|
cp OPM_STREAM_END
|
|
jp z, .tok_end_of_stream
|
|
cp OPM_STREAM_END_FRAME
|
|
jp z, .tok_end_of_frame
|
|
|
|
; call stream parser proc
|
|
call 0 ; :grins:
|
|
.proc equ $-2
|
|
jp c, .tok_loop ; token handled
|
|
|
|
; handle common tokens
|
|
ld b, [hl]
|
|
|
|
; if ((*data & 0xF0) == OPM_STREAM_BACKREF) {
|
|
ld a, b : and 0xF0 : cp OPM_STREAM_BACKREF
|
|
jp nz, .not_backref
|
|
|
|
; back reference, nested call :)
|
|
; int distance = ((*(data + 0) & 0x0F) << 8) | (*(data + 1));
|
|
ld a, b : and 0x0F : ld d, a : inc hl
|
|
ldi e, [hl] ; de = distance
|
|
ldi a, [hl] ; int frames_to_play = *(data + 2); data += 3
|
|
ld [ix + channel_struct_t.ptr], hl ; chctx->stream.ptr = data + 3;
|
|
|
|
push af, de, hl
|
|
call push_stack
|
|
pop hl, de, af
|
|
and a : sbc hl, de ; data -= distance;
|
|
|
|
; chctx->stream.frames_to_play = frames_to_play; // hack?
|
|
ld e, a : ld d, 0 : ld [ix + channel_struct_t.frames_to_play], de
|
|
jp .tok_loop
|
|
|
|
.not_backref:
|
|
; then it's most likely an delay
|
|
call set_delay
|
|
ld [ix + channel_struct_t.reload], de
|
|
|
|
; fetch next token until it's end of frame
|
|
jp .tok_loop
|
|
|
|
.tok_end_of_stream:
|
|
ld de, -1
|
|
ld [ix + channel_struct_t.delay], de
|
|
jp .end_of_frame
|
|
|
|
.tok_end_of_frame:
|
|
inc hl
|
|
.end_of_frame:
|
|
|
|
; chctx->stream.delay = chctx->stream.reload;
|
|
ld de, [ix + channel_struct_t.reload]
|
|
ld [ix + channel_struct_t.delay], de
|
|
|
|
; // decrement samples to play counter
|
|
; if (--chctx->stream.samples_to_play == 0) {
|
|
push hl
|
|
ld hl, [ix + channel_struct_t.frames_to_play]
|
|
dec hl
|
|
ld [ix + channel_struct_t.frames_to_play], hl
|
|
ld a, h : or l
|
|
pop hl
|
|
jp nz, .not_pop
|
|
|
|
.pop_loop:
|
|
call pop_stack ; todo: kludge below, fixme
|
|
push hl
|
|
ld hl, [ix + channel_struct_t.frames_to_play]
|
|
dec hl
|
|
ld [ix + channel_struct_t.frames_to_play], hl
|
|
ld a, h : or l
|
|
pop hl
|
|
jp z, .pop_loop ; to parse nested backrefs
|
|
ret
|
|
|
|
.not_pop:
|
|
exd
|
|
ld [ix+channel_struct_t.ptr], de
|
|
ret
|
|
|
|
; -----------------------------
|
|
; -----------------------------
|
|
; play one tick!!!
|
|
; trashes all registers
|
|
play_tick:
|
|
ld iy, reg_buffer
|
|
|
|
; play AY only for now
|
|
|
|
; chip_index = 0;
|
|
xor a : ld [player_struct.chip_idx], a
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 4, 0, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 4)
|
|
ld bc, parse_ay_channel_stream
|
|
ld a, 0 : ld [player_struct.channel_idx], a
|
|
call parse_stream
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 5, 1, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 5)
|
|
ld bc, parse_ay_channel_stream
|
|
ld a, 1 : ld [player_struct.channel_idx], a
|
|
call parse_stream
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 6, 2, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 6)
|
|
ld bc, parse_ay_channel_stream
|
|
ld a, 2 : ld [player_struct.channel_idx], a
|
|
call parse_stream
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 7, 0, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 7)
|
|
ld bc, parse_ay_envnoise_stream
|
|
call parse_stream
|
|
|
|
ldi [iy], 7
|
|
ld a, [player_struct.ssg_r7 + 0]
|
|
ldi [iy], a
|
|
|
|
; terminate list
|
|
ldi [iy], -1
|
|
|
|
; chip_index = 1;
|
|
ld a, 1 : ld [player_struct.chip_idx], a
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 4, 0, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 12)
|
|
ld bc, parse_ay_channel_stream
|
|
ld a, 0 : ld [player_struct.channel_idx], a
|
|
call parse_stream
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 5, 1, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 13)
|
|
ld bc, parse_ay_channel_stream
|
|
ld a, 1 : ld [player_struct.channel_idx], a
|
|
call parse_stream
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 6, 2, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 14)
|
|
ld bc, parse_ay_channel_stream
|
|
ld a, 2 : ld [player_struct.channel_idx], a
|
|
call parse_stream
|
|
|
|
; opmplay_parse_stream(ctx, ctx->channels + 7, 0, opmplay_parse_ay_channel_stream);
|
|
ld ix, player_channels + (channel_struct_t * 15)
|
|
ld bc, parse_ay_envnoise_stream
|
|
call parse_stream
|
|
|
|
ldi [iy], 7
|
|
ld a, [player_struct.ssg_r7 + 1]
|
|
ldi [iy], a
|
|
|
|
; terminate list
|
|
ldi [iy], -1
|
|
ret
|
|
|
|
|
|
endmodule |