Compare commits

...

2 commits

6 changed files with 984 additions and 0 deletions

View file

@ -0,0 +1,5 @@
@echo off
bas2tap -a10 -spsgplay loader.bas loader.tap
sjasmplus main.asm --lst=main.lst
copy /b loader.tap+player.tap "moebius.tap"
pause

5
tsfm_speccy_player/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.tap
*.sna
*.obj
*.exe
*.lst

View file

@ -0,0 +1,3 @@
10 BORDER 0 : PAPER 7 : INK 7 : CLEAR 24575
20 LOAD "moebius" CODE
100 RANDOMIZE USR 33155

229
tsfm_speccy_player/main.asm Normal file
View file

@ -0,0 +1,229 @@
; moe-bius - zx/tsfm player
; --atemka 14.o8.2o25
device zxspectrum128
ivt equ 0x8000
isr equ 0x8181
codestart equ 0x8080
reg_buffer equ 0x5B00 ; basic variables :meatjob:
EMPTYTAP "player.tap"
; IM2 vector table - all 257 values for floating bus condition
org ivt
block 257, (isr >> 8) ; isr at 0x8181
; dummy ISR (vblank synchronization is done by HALT)
org isr
stack_top:
ei
ret
; entrypoint
start:
di
ld sp, stack_top
; set IM2 vector table to 0x80
ld a, high ivt
ld i, a
im 2
; enable interrupts
ei
.loop:
halt
; output OPN registers
; TODO
ld hl, test_dump
call player.reg_out
di
halt
; draw VU meters
.base = 0x4000 + 2048*2 + 32*6 + 19
ld bc, .base + 0 ; 10
call vumeter.draw_0 ; 17
.vu0 equ $-2
ld bc, .base + 2 ; 10
call vumeter.draw_0 ; 17
.vu1 equ $-2
ld bc, .base + 4 ; 10
call vumeter.draw_0 ; 17
.vu2 equ $-2
ld bc, .base + 7 ; 10
call vumeter.draw_0 ; 17
.vu3 equ $-2
ld bc, .base + 9 ; 10
call vumeter.draw_0 ; 17
.vu4 equ $-2
ld bc, .base + 11 ; 10
call vumeter.draw_0 ; 17
.vu5 equ $-2
; and jump to next frame
xor a : ld bc, 0x7FFD : out (c), a ; map fast page to fix 128k AY ports contention
jp .loop
test_dump:
db 0x2F, 0xFF ; prescaler reg
db 0x2D, 0xFF ; prescaler reg
db 0x30, 0x01
db 0x34, 0x21
db 0x38, 0x52
db 0x3C, 0x02
db 0x40, 0x32
db 0x44, 0x17
db 0x48, 0x1C
db 0x4C, 0x03
db 0x50, 0x1F
db 0x54, 0x1F
db 0x58, 0x1F
db 0x5C, 0x1F
db 0x60, 0x08
db 0x64, 0x0C
db 0x68, 0x11
db 0x6C, 0x09
db 0x70, 0x00
db 0x74, 0x00
db 0x78, 0x01
db 0x7C, 0x00
db 0x80, 0xC0
db 0x84, 0xB1
db 0x88, 0x52
db 0x8C, 0xF4
db 0x90, 0x00
db 0x94, 0x00
db 0x98, 0x00
db 0x9C, 0x00
db 0xB0, 0x28
db 0x28, 0x00
db 0xA4, 0x0C
db 0xA0, 0xA3
db 0x28, 0xF0
db -1
; ----------------------------------------------------
; vu meters
; orig by natt, rewritten by me
vumeter:
ld a, 0
call .get_address
ld [start.vu0], hl
ld a, 1
call .get_address
ld [start.vu1], hl
ld a, 2
call .get_address
ld [start.vu2], hl
ld a, 3
call .get_address
ld [start.vu3], hl
ld a, 4
call .get_address
ld [start.vu4], hl
ld a, 5
call .get_address
ld [start.vu5], hl
ret
; DE - screen pos, A - register value
.get_address:
bit 4, a
jp nz, .get_e
and 0x0E
ld h, high .calltab
ld l, a
ld e, [hl] : inc l
ld d, [hl]
exd
ret
.get_e:
ld hl, .draw_e
ret
; ------------------
; drawing routines
; DE - dst
.draw_e:
ld a, 0b00000000 : ld [bc], a : inc b ; 18
ld a, 0b11111111 : ld [bc], a : inc b ; 18
ld a, 0b11000011 : ld [bc], a : inc b ; 18
ld a, 0b11011111 : ld [bc], a : inc b ; 18
ld a, 0b11000111 : ld [bc], a : inc b ; 18
ld a, 0b11011111 : ld [bc], a : inc b ; 18
ld a, 0b11000011 : ld [bc], a : inc b ; 18
ld a, 0b11111111 : ld [bc], a : ret ; 24 = 150t per char
.draw_0:
.draw_1:
dup 7 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*7
dup 1 : ld a, 0x00 : ld [bc], a : ret : edup ; 24*1
.draw_2:
dup 6 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*6
dup 1 : ld a, 0xFF : ld [bc], a : inc b : edup ; 18*1
dup 1 : ld a, 0xFF : ld [bc], a : ret : edup ; 24
.draw_3:
dup 5 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*5
dup 2 : ld a, 0xFF : ld [bc], a : inc b : edup ; 18*2
dup 1 : ld a, 0xFF : ld [bc], a : ret : edup ; 24
.draw_4:
dup 4 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*4
dup 3 : ld a, 0xFF : ld [bc], a : inc b : edup ; 18*3
dup 1 : ld a, 0xFF : ld [bc], a : ret : edup ; 24
.draw_5:
dup 3 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*3
dup 4 : ld a, 0xFF : ld [bc], a : inc b : edup ; 18*4
dup 1 : ld a, 0xFF : ld [bc], a : ret : edup ; 24
.draw_6:
dup 2 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*2
dup 5 : ld a, 0xFF : ld [bc], a : inc b : edup ; 18*5
dup 1 : ld a, 0xFF : ld [bc], a : ret : edup ; 24
.draw_7:
dup 1 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*1
dup 6 : ld a, 0xFF : ld [bc], a : inc b : edup ; 18*6
dup 1 : ld a, 0xFF : ld [bc], a : ret : edup ; 24
align 256
.calltab:
dw .draw_0, .draw_1, .draw_2, .draw_3, .draw_4, .draw_5, .draw_6, .draw_7
; player include
include "player.asm"
code_end
; -------------------------------------------------
; music pages
page 0
emptytap "page0.tap"
org 0xC000
align 256
;incbin "!music/200_percent_partyfinal.2.6.psg.packed"
savetap "page0.tap",CODE,"page0",0xC000,$-0xC000
; splash screen
emptytap "splash.tap"
org 0x4000
splash:
block 6144, 0
block 768, (0<<3)|(7<<0)
;incbin "200a.scr"
savetap "splash.tap",CODE,"splash",0x4000,$-0x4000
endmodule
; ----------------------------------------------------
total_codelength equ code_end-codestart
display "total: ", /a, total_codelength, " bytes"
savesna "moebius.sna", start
savetap "player.tap",CODE,"moebius",codestart,total_codelength

View file

@ -0,0 +1,176 @@
; OPM v0 stream data, stream-independent commands
OPM_STREAM_END_FRAME equ 0xFF ; end of frame, next channel
OPM_STREAM_END equ 0xFE ; end of stream, stop here or loop to OPM_STREAM_LOOP stream point
OPM_STREAM_NOP equ 0xFD ; nop
OPM_STREAM_NEW_ORDER equ 0xFC ; nop, marks new order
OPM_STREAM_SET_FRAME_RATE equ 0xFB ; word rate (as in opm_header_t::frame_rate)
OPM_STREAM_LOOP equ 0xFA ; set loop point here
; delay commands
OPM_STREAM_DELAY_INT32 equ 0xF9 ; dword delay
OPM_STREAM_DELAY_INT16 equ 0xF8 ; word delay
OPM_STREAM_DELAY_INT12 equ 0xD0 ; D0..DF - 0..4095 frames delay (hibyte in low 4 bits of command)
OPM_STREAM_DELAY_SHORT equ 0xC0 ; C0..CF - 1..16 frames delay
; back reference
OPM_STREAM_BACKREF equ 0xE0 ; E0..EF - word backrefpos (12 bit), byte frames
; OPN control stream commands
OPM_CTRL_EXTCH3 equ 0x00 ; 00..7F - ext. CH3 op1-3 frequency
OPM_CTRL_TIMER_CSM equ 0x80 ; 80..9F - set timer/CSM/LFO frequency
OPM_CTRL_RHYTHM equ 0xA0 ; A0..BF - rhythm control
OPM_CTRL_CMD80_REG25 equ (1 << 0) ;
OPM_CTRL_CMD80_REG25_BIT equ 0 ; shift value for OPM_CTRL_CMD80_REG25
OPM_CTRL_CMD80_REG24 equ (1 << 1) ;
OPM_CTRL_CMD80_REG24_BIT equ 1 ; shift value for OPM_CTRL_CMD80_REG24
OPM_CTRL_CMD80_REG27 equ (1 << 2) ;
OPM_CTRL_CMD80_REG27_BIT equ 2 ; shift value for OPM_CTRL_CMD80_REG27
OPM_CTRL_CMD80_REG22 equ (1 << 3) ;
OPM_CTRL_CMD80_REG22_BIT equ 3 ; shift value for OPM_CTRL_CMD80_REG22
OPM_CTRL_CMD80_EOF equ (1 << 4) ;
OPM_CTRL_CMD80_EOF_BIT equ 4 ; shift value for OPM_CTRL_CMD80_EOF
OPM_CTRL_EXTCH3_OP1_HIGH equ (1 << 0) ;
OPM_CTRL_EXTCH3_OP1_HIGH_BIT equ 0 ; shift value for OPM_CTRL_EXTCH3_OP1_HIGH
OPM_CTRL_EXTCH3_OP1_LOW equ (1 << 1) ;
OPM_CTRL_EXTCH3_OP1_LOW_BIT equ 1 ; shift value for OPM_CTRL_EXTCH3_OP1_LOW
OPM_CTRL_EXTCH3_OP2_HIGH equ (1 << 2) ;
OPM_CTRL_EXTCH3_OP2_HIGH_BIT equ 2 ; shift value for OPM_CTRL_EXTCH3_OP2_HIGH
OPM_CTRL_EXTCH3_OP2_LOW equ (1 << 3) ;
OPM_CTRL_EXTCH3_OP2_LOW_BIT equ 3 ; shift value for OPM_CTRL_EXTCH3_OP2_LOW
OPM_CTRL_EXTCH3_OP3_HIGH equ (1 << 4) ;
OPM_CTRL_EXTCH3_OP3_HIGH_BIT equ 4 ; shift value for OPM_CTRL_EXTCH3_OP3_HIGH
OPM_CTRL_EXTCH3_OP3_LOW equ (1 << 5) ;
OPM_CTRL_EXTCH3_OP3_LOW_BIT equ 5 ; shift value for OPM_CTRL_EXTCH3_OP3_LOW
OPM_CTRL_EXTCH3_EOF equ (1 << 6) ;
OPM_CTRL_EXTCH3_EOF_BIT equ 6 ; shift value for OPM_CTRL_EXTCH3_EOF
OPM_CTRL_CMDA0_REG_MASK equ (0x0F << 0) ;
OPM_CTRL_CMDA0_REG_MASK_BIT equ 0 ; shift value for OPM_CTRL_CMDA0_REG_MASK
OPM_CTRL_CMDA0_EOF equ (1 << 4) ;
OPM_CTRL_CMDA0_EOF_BIT equ 4 ; shift value for OPM_CTRL_CMDA0_EOF
; OPN FM stream commands
OPM_FM_ADSR equ 0x00 ; 00..3F - set ADSR
OPM_FM_MUL_TL_EG equ 0x40 ; 40..7F - set MULT/TL/SSG-EG
OPM_FM_FREQ_FB_PAN equ 0x80 ; 80..9F - set frequency/feedback/panning
OPM_FM_KEY equ 0xA0 ; A0..BF - key on/off
OPM_FM_CMD00_REG50 equ (1 << 0) ;
OPM_FM_CMD00_REG50_BIT equ 0 ; shift value for OPM_FM_CMD00_REG50
OPM_FM_CMD00_REG60 equ (1 << 1) ;
OPM_FM_CMD00_REG60_BIT equ 1 ; shift value for OPM_FM_CMD00_REG60
OPM_FM_CMD00_REG70 equ (1 << 2) ;
OPM_FM_CMD00_REG70_BIT equ 2 ; shift value for OPM_FM_CMD00_REG70
OPM_FM_CMD00_REG80 equ (1 << 3) ;
OPM_FM_CMD00_REG80_BIT equ 3 ; shift value for OPM_FM_CMD00_REG80
OPM_FM_CMD00_OP_MASK equ (3 << 4) ;
OPM_FM_CMD00_OP_MASK_BIT equ 4 ; shift value for OPM_FM_CMD00_OP_MASK
OPM_FM_CMD40_REG30 equ (1 << 0) ;
OPM_FM_CMD40_REG30_BIT equ 0 ; shift value for OPM_FM_CMD40_REG30
OPM_FM_CMD40_REG40 equ (1 << 1) ;
OPM_FM_CMD40_REG40_BIT equ 1 ; shift value for OPM_FM_CMD40_REG40
OPM_FM_CMD40_REG90 equ (1 << 2) ;
OPM_FM_CMD40_REG90_BIT equ 2 ; shift value for OPM_FM_CMD40_REG90
OPM_FM_CMD40_EOF equ (1 << 3) ;
OPM_FM_CMD40_EOF_BIT equ 3 ; shift value for OPM_FM_CMD40_EOF
OPM_FM_CMD40_OP_SHIFT equ 4 ;
OPM_FM_CMD40_OP_MASK equ (3 << 4) ;
OPM_FM_CMD40_OP_MASK_BIT equ 4 ; shift value for OPM_FM_CMD40_OP_MASK
OPM_FM_CMD80_REGA4 equ (1 << 0) ;
OPM_FM_CMD80_REGA4_BIT equ 0 ; shift value for OPM_FM_CMD80_REGA4
OPM_FM_CMD80_REGA0 equ (1 << 1) ;
OPM_FM_CMD80_REGA0_BIT equ 1 ; shift value for OPM_FM_CMD80_REGA0
OPM_FM_CMD80_REGB0 equ (1 << 2) ;
OPM_FM_CMD80_REGB0_BIT equ 2 ; shift value for OPM_FM_CMD80_REGB0
OPM_FM_CMD80_REGB4 equ (1 << 3) ;
OPM_FM_CMD80_REGB4_BIT equ 3 ; shift value for OPM_FM_CMD80_REGB4
OPM_FM_CMD80_EOF equ (1 << 4) ;
OPM_FM_CMD80_EOF_BIT equ 4 ; shift value for OPM_FM_CMD80_EOF
OPM_FM_CMDA0_OP_SHIFT equ 0 ;
OPM_FM_CMDA0_OP_MASK equ (0x0F << 0) ;
OPM_FM_CMDA0_OP_MASK_BIT equ 0 ; shift value for OPM_FM_CMDA0_OP_MASK
OPM_FM_CMDA0_EOF equ (1 << 4) ;
OPM_FM_CMDA0_EOF_BIT equ 4 ; shift value for OPM_FM_CMDA0_EOF
; OPNA rhythm channel stream
OPN_RHYTHM_KEY equ 0x00 ; 00..7F - key on
OPN_RHYTHM_REGS1 equ 0x80 ; 80..9F - write reg set 1
OPN_RHYTHM_REGS2 equ 0xA0 ; A0..BF - write reg set 2
OPN_RHYTHM_KEY_EOF equ (1 << 6) ;
OPN_RHYTHM_KEY_EOF_BIT equ 6 ; shift value for OPN_RHYTHM_KEY_EOF
OPN_RHYTHM_CMD80_REG10 equ (1 << 0) ;
OPN_RHYTHM_CMD80_REG10_BIT equ 0 ; shift value for OPN_RHYTHM_CMD80_REG10
OPN_RHYTHM_CMD80_REG11 equ (1 << 1) ;
OPN_RHYTHM_CMD80_REG11_BIT equ 1 ; shift value for OPN_RHYTHM_CMD80_REG11
OPN_RHYTHM_CMD80_REG18 equ (1 << 2) ;
OPN_RHYTHM_CMD80_REG18_BIT equ 2 ; shift value for OPN_RHYTHM_CMD80_REG18
OPN_RHYTHM_CMD80_REG19 equ (1 << 3) ;
OPN_RHYTHM_CMD80_REG19_BIT equ 3 ; shift value for OPN_RHYTHM_CMD80_REG19
OPN_RHYTHM_CMDA0_REG1A equ (1 << 0) ;
OPN_RHYTHM_CMDA0_REG1A_BIT equ 0 ; shift value for OPN_RHYTHM_CMDA0_REG1A
OPN_RHYTHM_CMDA0_REG1B equ (1 << 1) ;
OPN_RHYTHM_CMDA0_REG1B_BIT equ 1 ; shift value for OPN_RHYTHM_CMDA0_REG1B
OPN_RHYTHM_CMDA0_REG1C equ (1 << 2) ;
OPN_RHYTHM_CMDA0_REG1C_BIT equ 2 ; shift value for OPN_RHYTHM_CMDA0_REG1C
OPN_RHYTHM_CMDA0_REG1D equ (1 << 3) ;
OPN_RHYTHM_CMDA0_REG1D_BIT equ 3 ; shift value for OPN_RHYTHM_CMDA0_REG1D
OPN_RHYTHM_REGS_EOF equ (1 << 4) ;
OPN_RHYTHM_REGS_EOF_BIT equ 4 ; shift value for OPN_RHYTHM_REGS_EOF
; OPN SSG tone stream commands (shared with AY chip type)
OPM_AYTONE_REGS equ 0x00 ; 00..7F - set volume and period low
OPM_AYTONE_PERIOD equ 0x80 ; 80..BF - set period
OPM_AYTONE_MASK equ 0xF0 ; F0..F7 - set tone/noise mask
OPM_AYTONE_CMD00_VOLUME_MASK equ (0x1F << 0) ;
OPM_AYTONE_CMD00_VOLUME_MASK_BIT equ 0 ; shift value for OPM_AYTONE_CMD00_VOLUME_MASK
OPM_AYTONE_CMD00_PERIOD_LOW equ (1 << 5) ;
OPM_AYTONE_CMD00_PERIOD_LOW_BIT equ 5 ; shift value for OPM_AYTONE_CMD00_PERIOD_LOW
OPM_AYTONE_CMD00_EOF equ (1 << 6) ;
OPM_AYTONE_CMD00_EOF_BIT equ 6 ; shift value for OPM_AYTONE_CMD00_EOF
OPM_AYTONE_CMD80_PERIOD_HIGH equ (0xF << 0) ;
OPM_AYTONE_CMD80_PERIOD_HIGH_BIT equ 0 ; shift value for OPM_AYTONE_CMD80_PERIOD_HIGH
OPM_AYTONE_CMD80_PERIOD_LOW equ (1 << 4) ;
OPM_AYTONE_CMD80_PERIOD_LOW_BIT equ 4 ; shift value for OPM_AYTONE_CMD80_PERIOD_LOW
OPM_AYTONE_CMD80_EOF equ (1 << 5) ;
OPM_AYTONE_CMD80_EOF_BIT equ 5 ; shift value for OPM_AYTONE_CMD80_EOF
OPM_AYTONE_MASK_TONE equ (1 << 0) ;
OPM_AYTONE_MASK_TONE_BIT equ 0 ; shift value for OPM_AYTONE_MASK_TONE
OPM_AYTONE_MASK_NOISE equ (1 << 1) ;
OPM_AYTONE_MASK_NOISE_BIT equ 1 ; shift value for OPM_AYTONE_MASK_NOISE
OPM_AYTONE_MASK_EOF equ (1 << 2) ;
OPM_AYTONE_MASK_EOF_BIT equ 2 ; shift value for OPM_AYTONE_MASK_EOF
; OPN SSG envelope/noise stream commands (shared with AY chip type)
OPM_AYENV_REGS equ 0x00 ; 00..7F - set noise and period low
OPM_AYENV_ENVTYPE equ 0x80 ; 80..BF - set env type and period low
OPM_AYENV_PERIOD_FULL equ 0xF0 ; F0..F7 - set full envelope period
OPM_AYENV_CMD00_NOISE_MASK equ (0x1F << 0) ;
OPM_AYENV_CMD00_NOISE_MASK_BIT equ 0 ; shift value for OPM_AYENV_CMD00_NOISE_MASK
OPM_AYENV_CMD00_PERIOD_LOW equ (1 << 5) ;
OPM_AYENV_CMD00_PERIOD_LOW_BIT equ 5 ; shift value for OPM_AYENV_CMD00_PERIOD_LOW
OPM_AYENV_CMD00_EOF equ (1 << 6) ;
OPM_AYENV_CMD00_EOF_BIT equ 6 ; shift value for OPM_AYENV_CMD00_EOF
OPM_AYENV_CMD80_ENV_TYPE equ (0xF << 0) ;
OPM_AYENV_CMD80_ENV_TYPE_BIT equ 0 ; shift value for OPM_AYENV_CMD80_ENV_TYPE
OPM_AYENV_CMD80_PERIOD_LOW equ (1 << 4) ;
OPM_AYENV_CMD80_PERIOD_LOW_BIT equ 4 ; shift value for OPM_AYENV_CMD80_PERIOD_LOW
OPM_AYENV_CMD80_EOF equ (1 << 5) ;
OPM_AYENV_CMD80_EOF_BIT equ 5 ; shift value for OPM_AYENV_CMD80_EOF
OPM_AYENV_CMDF0_PERIOD_LOW equ (1 << 0) ;
OPM_AYENV_CMDF0_PERIOD_LOW_BIT equ 0 ; shift value for OPM_AYENV_CMDF0_PERIOD_LOW
OPM_AYENV_CMDF0_PERIOD_HIGH equ (1 << 1) ;
OPM_AYENV_CMDF0_PERIOD_HIGH_BIT equ 1 ; shift value for OPM_AYENV_CMDF0_PERIOD_HIGH
OPM_AYENV_CMDF0_EOF equ (1 << 2) ;
OPM_AYENV_CMDF0_EOF_BIT equ 2 ; shift value for OPM_AYENV_CMDF0_EOF

View file

@ -0,0 +1,566 @@
; 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_ch0 dw 0
ch3_ofs_ch1 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 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
channel_struct_t 0, 0
player_struct player_struct_t player_channels+(channel_struct_t*(3+0)), player_channels+(channel_struct_t*(3+8))
; -----------------------------
; -----------------------------
; OPN register dump output
; in: HL - registers in (reg:data) byte pairs, reg==0xFF - return
reg_out:
ld bc, 0xFFFD
ld de, 0x00C0 ; 0xFFBF compensated for outi
; select 1st chip
ld a, 0b11111000
out (c), a
.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:
ld a, [hl] : inc hl : and 0x0F : ld d, a
ld e, [hl] : inc hl
ret
.short:
ld 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 nz, 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 nz, 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 nz, 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 nz, 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] ; a = ctx->ssg_r7[chip_index] &= ~(((1 << 3) | (1 << 0)) << ch);
srl e : srl e
ld hl, .mask_lookup
add hl, de
or [hl] : ld [ix], a ; ctx->ssg_r7[chip_index] |= (mask_lookup[mask & 3] << ch);
pop hl, de, ix
; if (mask & OPM_AYTONE_MASK_EOF) endOfFrame = true;
bit OPM_AYTONE_MASK_EOF_BIT, b
jp nz, 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 envalope/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 nz, 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 nz, 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 nz, 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 nz, 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 nz, 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 nz, 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 nz, 1f
ld a, b : ld [player_struct.end_of_frame], a
1:
scf
ret
.not_period:
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.frames_to_play]
dec de
ld [ix+channel_struct_t.frames_to_play], de
ld a, d
or e
ret z
; save proc ptr
ld bc, .proc
; 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
; call stream parser proc
call 0 ; :grins:
.proc
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
; TODO
; 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
exd : ld [ix + channel_struct_t.ptr], de ; 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
ld a, b
cp OPM_STREAM_END
jp z, .tok_end_of_stream
cp OPM_STREAM_END_FRAME
jp z, .tok_end_of_frame
; then it's most likely an delay
call set_delay
ld [ix + channel_struct_t.delay], 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.delay]
ld [ix + channel_struct_t.reload], 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
; chip_index = 1;
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
ret
endmodule