//****************************************************************************************************************************************************** // Header //****************************************************************************************************************************************************** /** Delta Music Composer 1.7 Reference Player @author Marek Konopka @date 03.09.2009 */ .local NRefDlt17Player //****************************************************************************************************************************************************** // Constants //****************************************************************************************************************************************************** PLAYER_BASE_PTR = $0200 PLAYER_ZPG_VARS = $e0 DEFAULT_TEMPO = 6 MAX_NUM_PATTERNS = 64 MAX_NUM_INSTRUMENTS = 32 SONG_LENGTH = 128 PATTERN_SIZE = 128 INSTRUMENT_SIZE = 64 FRQ_TABLE_SIZE = 64 ENVELOPE_SIZE = 16 TRACK_CMD_STOP = $40 TRACK_CMD_JUMP = $41 TRACK_CMD_TEMPO = $42 TRACK_CMD_LAST = TRACK_CMD_TEMPO ACC_OFFSET_FILTER_TRANSP = 0 INSTR_OFFSET_AUDCTLS = 48+0 INSTR_OFFSET_VOL_DECAY_DELAY = 48+1 INSTR_OFFSET_FRQ_SLIDE = 48+2 INSTR_OFFSET_TRANSP_DELAY = 48+3 INSTR_OFFSET_TRANSP_TBL = 48+4 ACCENT_NONE = 0 ACCENT_FRQ_STORE = 1 ACCENT_FRQ_TRANSP = 2 ACCENT_NOTE_TRANSP = 3 TRANSP_TBL_SIZE = 4 // must be a power of two CHANNEL0_FILTER_MASK = 4 CHANNEL1_FILTER_MASK = 2 AUDCTL_CHANNEL0_MASK = %11010101 AUDCTL_CHANNEL1_MASK = %10000011 AUDCTL_CHANNEL2_MASK = %10101001 AUDCTL_CHANNEL3_MASK = %10000001 OFFSET_PATTERN_PTRS = $0000 OFFSET_INSTR_PTRS = $0080 OFFSET_VARIABLES = $0100 OFFSET_FRQ = $0200 OFFSET_PLAYER_CODE = $0300 //****************************************************************************************************************************************************** // Zero page variable //****************************************************************************************************************************************************** patt0_ptr = PLAYER_ZPG_VARS+$00 // track #0 pattern pointer patt1_ptr = PLAYER_ZPG_VARS+$02 // track #1 pattern pointer patt2_ptr = PLAYER_ZPG_VARS+$04 // track #2 pattern pointer patt3_ptr = PLAYER_ZPG_VARS+$06 // track #3 pattern pointer instr0_ptr = PLAYER_ZPG_VARS+$08 // track #0 combined volume-distortion pointer instr1_ptr = PLAYER_ZPG_VARS+$0a // track #1 combined volume-distortion pointer instr2_ptr = PLAYER_ZPG_VARS+$0c // track #2 combined volume-distortion pointer instr3_ptr = PLAYER_ZPG_VARS+$0e // track #3 combined volume-distortion pointer acc0_ptr = PLAYER_ZPG_VARS+$10 // track #0 accents ptr acc1_ptr = PLAYER_ZPG_VARS+$12 // track #1 accents ptr acc2_ptr = PLAYER_ZPG_VARS+$14 // track #2 accents ptr acc3_ptr = PLAYER_ZPG_VARS+$16 // track #3 accents ptr acc_param0_ptr = PLAYER_ZPG_VARS+$18 // track #0 accent params ptr acc_param1_ptr = PLAYER_ZPG_VARS+$1a // track #1 accent params ptr acc_param2_ptr = PLAYER_ZPG_VARS+$1c // track #2 accent params ptr acc_param3_ptr = PLAYER_ZPG_VARS+$1e // track #2 accent params ptr //****************************************************************************************************************************************************** // Pointers //****************************************************************************************************************************************************** ModulePtr = $2000 TracksPtr = ModulePtr+$2000 InstrumentsPtr = ModulePtr+$2400 ParametersPtr = ModulePtr+$2c00 BassFrqType = ParametersPtr+$0000 //****************************************************************************************************************************************************** // Hardware //****************************************************************************************************************************************************** POKEY = $d200 AUDCTL = $d208 SKCTL = $d20f //****************************************************************************************************************************************************** // Variables //****************************************************************************************************************************************************** org PLAYER_BASE_PTR + OFFSET_VARIABLES env_ind: .ds 4 // envelope indices (ENVELOPE_SIZE-1 ... 0) tr_stat: .ds 4 // track statuses (0 -> off, !=0 -> on) notes: .ds 4 // notes = pattern note + track transposition vd_cnt: .ds 4 // volume decay counters trns_del: .ds 4 // instrument transposition delays trns_ind: .ds 4 // instrument transposition indices ptransp: .ds 4 // pattern transpositions freq_add: .ds 4 // frequency additions audc: .ds 4 // audc audf: .ds 4 // audf audctls: .ds 4 // audctl vol_stat: .ds 4 // volume statuses tmp_ptr: .ds 2 // temporary pointer play_stat: .ds 1 // playing status tempo: .ds 1 // tempo tempo_cnt: .ds 1 // tempo counter patt_pos: .ds 1 // pattern position track_pos: .ds 1 // track position //tmp_note: .ds 1 // temporary note tmp_freq: .ds 1 // temporary frequency //****************************************************************************************************************************************************** // Data //****************************************************************************************************************************************************** //.align 256 FrqTbl = PLAYER_BASE_PTR + OFFSET_FRQ org FrqTbl ; ins 'frq_tbl_normal.dat' dta $ff,$f1,$e4,$d7,$cb,$c0,$b5,$aa,$a1,$98,$8f,$87,$7f,$79,$72,$6b dta $65,$5f,$5a,$55,$50,$4b,$47,$43,$3f,$3c,$38,$35,$32,$2f,$2c,$2a dta $27,$25,$23,$21,$1f,$1d,$1c,$1a,$18,$17,$16,$14,$13,$12,$11,$10 dta $0f,$0e,$0d,$0c,$0b,$0a,$09,$08,$07,$06,$05,$04,$ff,$f1,$e4,$d7 ; ins 'frq_tbl_bass_cmc.dat' dta $f2,$e9,$da,$ce,$bf,$b6,$aa,$a1,$98,$8f,$89,$80,$7a,$71,$6b,$65 dta $5f,$5c,$56,$50,$67,$60,$5a,$55,$51,$4c,$48,$43,$3f,$3d,$39,$34 dta $33,$30,$2d,$2a,$28,$25,$24,$21,$1f,$1e,$1c,$1b,$19,$00,$16,$15 dta $00,$0a,$09,$08,$07,$06,$05,$04,$03,$02,$01,$00,$f2,$e9,$da,$ce ; ins 'frq_tbl_bass_cmc.dat' dta $f2,$e9,$da,$ce,$bf,$b6,$aa,$a1,$98,$8f,$89,$80,$7a,$71,$6b,$65 dta $5f,$5c,$56,$50,$67,$60,$5a,$55,$51,$4c,$48,$43,$3f,$3d,$39,$34 dta $33,$30,$2d,$2a,$28,$25,$24,$21,$1f,$1e,$1c,$1b,$19,$00,$16,$15 dta $00,$0a,$09,$08,$07,$06,$05,$04,$03,$02,$01,$00,$f2,$e9,$da,$ce ; ins 'frq_tbl_bass_mpt.dat' dta $ff,$f1,$e4,$d8,$ca,$c0,$b5,$ab,$a2,$99,$8e,$87,$7f,$78,$73,$6c dta $66,$61,$5a,$55,$51,$4b,$48,$43,$3f,$3c,$39,$34,$33,$30,$2d,$2a dta $28,$25,$24,$21,$1f,$1e,$1c,$1b,$19,$17,$16,$15,$13,$12,$11,$10 dta $0f,$0e,$0d,$0c,$0b,$0a,$09,$08,$07,$06,$05,$04,$03,$02,$01,$ff PatternPtrsTbl = PLAYER_BASE_PTR + OFFSET_PATTERN_PTRS InstrumentPtrsTbl = PLAYER_BASE_PTR + OFFSET_INSTR_PTRS PlayerCodePtr = PLAYER_BASE_PTR + OFFSET_PLAYER_CODE //****************************************************************************************************************************************************** // Code //****************************************************************************************************************************************************** org PlayerCodePtr //****************************************************************************************************************************************************** jmp init jmp player.entry_point jmp stop //****************************************************************************************************************************************************** /// Initialize. /** @param .byte Y - Initial track position. */ .proc init dey sty track_pos lda #(PATTERN_SIZE-2) sta patt_pos ldx #DEFAULT_TEMPO stx tempo ldx #1 stx tempo_cnt jsr init_patterns_table jsr init_instruments_tbl jsr init_variables jsr init_frq_tbl lda #1 sta play_stat lda #3 sta SKCTL rts .endp //****************************************************************************************************************************************************** .proc init_patterns_table ldx #ModulePtr stx tmp_ptr sty tmp_ptr+1 ldy #0 loop lda tmp_ptr sta PatternPtrsTbl,y lda tmp_ptr+1 sta PatternPtrsTbl+MAX_NUM_PATTERNS,y lda tmp_ptr clc adc #PATTERN_SIZE sta tmp_ptr bcc skip inc tmp_ptr+1 skip iny cpy #MAX_NUM_PATTERNS bne loop rts .endp //****************************************************************************************************************************************************** .proc init_instruments_tbl ldx #InstrumentsPtr stx tmp_ptr sty tmp_ptr+1 ldy #0 loop lda tmp_ptr sta InstrumentPtrsTbl,y lda tmp_ptr+1 sta InstrumentPtrsTbl+MAX_NUM_INSTRUMENTS,y lda tmp_ptr clc adc #INSTRUMENT_SIZE sta tmp_ptr bcc skip inc tmp_ptr+1 skip iny cpy #MAX_NUM_INSTRUMENTS bne loop rts .endp //****************************************************************************************************************************************************** .proc init_frq_tbl lda BassFrqType and #1 lsr @ ror @ ror @ tay ldx #0 loop lda FrqTbl+FRQ_TABLE_SIZE*2,y sta FrqTbl+FRQ_TABLE_SIZE,x iny inx cpx #FRQ_TABLE_SIZE bne loop rts .endp //****************************************************************************************************************************************************** .proc init_variables ldy #3 lda #0 loop sta audctls,y sta audc,y sta audf,y sta vol_stat,y dey bpl loop rts .endp //****************************************************************************************************************************************************** .proc stop lda #0 sta play_stat ldy #7 lda #0 loop sta POKEY,y dey bpl loop rts .endp //****************************************************************************************************************************************************** .nowarn .proc player play_done rts entry_point lda play_stat beq play_done lda audctls+0 ora audctls+1 ora audctls+2 ora audctls+3 sta AUDCTL ldx audf+0 ldy audc+0 stx POKEY+0 sty POKEY+1 ldx audf+1 ldy audc+1 stx POKEY+2 sty POKEY+3 ldx audf+2 ldy audc+2 stx POKEY+4 sty POKEY+5 ldx audf+3 ldy audc+3 stx POKEY+6 sty POKEY+7 dec tempo_cnt bne play_instruments lda tempo sta tempo_cnt inc patt_pos inc patt_pos bpl take_notes new_track_pos: inc track_pos lda #0 sta patt_pos jsr tktrx.entry_point lda tr_stat+0 ora tr_stat+1 ora tr_stat+2 ora tr_stat+3 bne take_notes jmp stop take_notes: lda tr_stat+0 beq skip1 jsr tkpt1 skip1: lda tr_stat+1 beq skip2 jsr tkpt2 skip2: lda tr_stat+2 beq skip3 jsr tkpt3 skip3: lda tr_stat+3 beq skip4 jsr tkpt4 skip4: play_instruments lda tr_stat+0 beq skip10 lda vol_stat+0 beq skip10 jsr play_instr0 skip10: lda tr_stat+1 beq skip11 lda vol_stat+1 beq skip11 jsr play_instr1 skip11: lda tr_stat+2 beq skip12 lda vol_stat+2 beq skip12 jsr play_instr2 skip12: lda tr_stat+3 beq skip13 lda vol_stat+3 beq skip13 jsr play_instr3 skip13: rts .endp //****************************************************************************************************************************************************** .nowarn .proc tktrx //------------------------------------------------------------------------------------------------------------------------------------------------------ tr1_non_pattern cpy #(TRACK_CMD_LAST+1) bcc continue lda #0 sta tr_stat+0 sta audc+0 sta audctls+0 jmp track2 continue cpy #TRACK_CMD_TEMPO bne non_tempo cmd_tempo lda TracksPtr+SONG_LENGTH,x sta tempo sta tempo_cnt inc track_pos jmp tktrx.entry_point non_tempo: cpy #TRACK_CMD_JUMP bne cmd_stop cmd_jmp lda TracksPtr+SONG_LENGTH,x sta track_pos jmp tktrx.entry_point cmd_stop // no need to continue processing pla pla jmp stop //------------------------------------------------------------------------------------------------------------------------------------------------------ entry_point ldx track_pos track1 ldy TracksPtr,x cpy #MAX_NUM_PATTERNS bcs tr1_non_pattern lda TracksPtr+SONG_LENGTH,x sta ptransp+0 lda PatternPtrsTbl,y sta patt0_ptr lda PatternPtrsTbl+MAX_NUM_PATTERNS,y sta patt0_ptr+1 lda #1 sta tr_stat+0 track2 ldy TracksPtr+SONG_LENGTH*2,x cpy #MAX_NUM_PATTERNS bcs non_pattern_2 lda TracksPtr+SONG_LENGTH*3,x sta ptransp+1 lda PatternPtrsTbl,y sta patt1_ptr lda PatternPtrsTbl+MAX_NUM_PATTERNS,y sta patt1_ptr+1 lda #1 sta tr_stat+1 track3 ldy TracksPtr+SONG_LENGTH*4,x cpy #MAX_NUM_PATTERNS bcs non_pattern_3 lda TracksPtr+SONG_LENGTH*5,x sta ptransp+2 lda PatternPtrsTbl,y sta patt2_ptr lda PatternPtrsTbl+MAX_NUM_PATTERNS,y sta patt2_ptr+1 lda #1 sta tr_stat+2 track4 ldy TracksPtr+SONG_LENGTH*6,x cpy #MAX_NUM_PATTERNS bcs non_pattern_4 lda TracksPtr+SONG_LENGTH*7,x sta ptransp+3 lda PatternPtrsTbl,y sta patt3_ptr lda PatternPtrsTbl+MAX_NUM_PATTERNS,y sta patt3_ptr+1 lda #1 sta tr_stat+3 rts non_pattern_2 lda #0 sta tr_stat+1 sta audc+1 sta audctls+1 beq track3 non_pattern_3 lda #0 sta tr_stat+2 sta audc+2 sta audctls+2 beq track4 non_pattern_4 lda #0 sta tr_stat+3 sta audc+3 sta audctls+3 rts .endp //****************************************************************************************************************************************************** /// Take pattern position. /** @param %%1 .byte channel_num - Channel number. @param %%2 .word patt_ptr - Pattern pointer. @param %%3 .word instr_ptr - Instrument pointer. @param %%4 .word acc_ptr - Accents pointer. @param %%5 .word acc_param_ptr - Accent params pointer. @param %%6 .byte audctl_channel_mask - AUDCTL mask. */ .macro tkpt_macro ldy patt_pos lda (%%2),y bmi note iny lda (%%2),y bmi end_of_pattern // empty rts end_of_pattern: pla pla jmp player.new_track_pos note // note = (patt_note + ptransp) & 0x7f; clc adc ptransp+%%1 and #127 sta notes+%%1 // env_ind = vol_stat = (ENVELOPE_SIZE-1); lda #(ENVELOPE_SIZE-1) sta env_ind+%%1 sta vol_stat+%%1 iny lda (%%2),y tax lda InstrumentPtrsTbl+MAX_NUM_INSTRUMENTS,x sta %%3+1 sta %%4+1 sta %%5+1 lda InstrumentPtrsTbl,x sta %%3 //clc eor #$10 sta %%4 eor #$30 sta %%5 // vd_cnt = instr_ptr[INSTR_OFFSET_VOL_DECAY_DELAY]; ldy #INSTR_OFFSET_VOL_DECAY_DELAY lda (%%3),y sta vd_cnt+%%1 // trns_del = instr_ptr[INSTR_OFFSET_VOL_DECAY_DELAY] & 0x7f; ldy #INSTR_OFFSET_TRANSP_DELAY lda (%%3),y and #127 sta trns_del+%%1 // trns_ind = freq_add = 0; lda #0 sta trns_ind+%%1 sta freq_add+%%1 // audctls = instr_ptr[INSTR_OFFSET_AUDCTLS] & audctl_channel_mask ldy #INSTR_OFFSET_AUDCTLS lda (%%3),y and %%6 sta audctls+%%1 rts .endm //****************************************************************************************************************************************************** .proc tkpt1 tkpt_macro 0, patt0_ptr, instr0_ptr, acc0_ptr, acc_param0_ptr, #AUDCTL_CHANNEL0_MASK .endp //****************************************************************************************************************************************************** .proc tkpt2 tkpt_macro 1, patt1_ptr, instr1_ptr, acc1_ptr, acc_param1_ptr, #AUDCTL_CHANNEL1_MASK .endp //****************************************************************************************************************************************************** .proc tkpt3 tkpt_macro 2, patt2_ptr, instr2_ptr, acc2_ptr, acc_param2_ptr, #AUDCTL_CHANNEL2_MASK .endp //****************************************************************************************************************************************************** .proc tkpt4 tkpt_macro 3, patt3_ptr, instr3_ptr, acc3_ptr, acc_param3_ptr, #AUDCTL_CHANNEL3_MASK .endp //****************************************************************************************************************************************************** /// Play instrument. /** @param %%1 .byte channel_num - Channel number. @param %%2 .word instr_ptr - Instrument pointer. @param %%3 .word acc_ptr - Accents pointer. @param %%4 .word acc_param_ptr - Accent params pointer. */ .macro play_instr ldy env_ind+%%1 bmi outside_envelope //------------------------------------------------------------------------------------------------------------------------------------------------------ envelope: // audc = envelope[env_ind] lda (%%2),y sta audc+%%1 // if (accents[env_ind] == 0) lda (%%3),y bne acc jsr determine_table_transpositions dec env_ind+%%1 jmp update_variables acc: cmp #ACCENT_FRQ_STORE beq acc_1 //------------------------------------------------------------------------------------------------------------------------------------------------------ determine_accent_transpositions: // if (accents[env_ind] == 3) //lda (acc_ptr),y cmp #ACCENT_NOTE_TRANSP bne acc_2 acc_3: // tmp_note = notes + acc_params[env_ind]; lda notes+%%1 clc adc (%%4),y //sta tmp_note tax // tmp_freq = freq_add; lda freq_add+%%1 sta tmp_freq //rts jmp determine_acc_audf acc_2: // tmp_freq = freq_add + acc_params[env_ind]; lda freq_add+%%1 clc adc (%%4),y sta tmp_freq // tmp_note = notes; //lda notes+0 //sta tmp_note ldx notes+%%1 //rts //jmp determine_audf determine_acc_audf: jsr determine_audf dec env_ind+%%1 rts //------------------------------------------------------------------------------------------------------------------------------------------------------ acc_1: // audf = acc_params[env_ind]; lda (%%4),y sta audf+%%1 dec env_ind+%%1 rts //------------------------------------------------------------------------------------------------------------------------------------------------------ outside_envelope jsr determine_table_transpositions //jsr volume_decay //jmp update_variables //------------------------------------------------------------------------------------------------------------------------------------------------------ volume_decay ldy #INSTR_OFFSET_VOL_DECAY_DELAY lda (%%2),y beq exit_volume_decay dec vd_cnt+%%1 beq volume_test //rts jmp update_variables volume_test: lda audc+%%1 and #15 beq disable_instr dec audc+%%1 lda (%%2),y sta vd_cnt+%%1 //rts jmp update_variables disable_instr: sta vol_stat+%%1 rts exit_volume_decay //rts //------------------------------------------------------------------------------------------------------------------------------------------------------ update_variables frequency_slide lda freq_add+%%1 clc ldy #INSTR_OFFSET_FRQ_SLIDE adc (%%2),y sta freq_add+%%1 table_transposition dec trns_del+%%1 bne done_table_transposition inc trns_ind+%%1 ldy #INSTR_OFFSET_TRANSP_DELAY lda (%%2),y and #127 sta trns_del+%%1 done_table_transposition rts //------------------------------------------------------------------------------------------------------------------------------------------------------ determine_table_transpositions // no acc // uint8_t transp = instr[(trns_ind & 3) + 4 + 48]; lda trns_ind+%%1 and #(TRANSP_TBL_SIZE-1) clc adc #INSTR_OFFSET_TRANSP_TBL tay lda (%%2),y tax // if (instr[INSTR_OFFSET_TRANSP_DELAY] > 0) // transp_delay ldy #INSTR_OFFSET_TRANSP_DELAY lda (%%2),y bmi frequency_transposition note_transposition: // tmp_note = transp + notes txa //clc adc notes+%%1 //sta tmp_note tax // tmp_freq = freq_add; lda freq_add+%%1 sta tmp_freq //rts jmp determine_audf frequency_transposition // tmp_freq = transp + freq_add; txa //clc adc freq_add+%%1 sta tmp_freq // tmp_note = notes; //lda notes+0 //sta tmp_note ldx notes+%%1 //rts //jmp determine_audf //------------------------------------------------------------------------------------------------------------------------------------------------------ determine_audf // acc != 1 // audf = FrqTbl[tmp_note] + tmp_freq; //ldx tmp_note lda FrqTbl,x clc adc tmp_freq sta audf+%%1 .if (%%1 == 2 || %%1 == 3) rts .else // if ((audctls & filter_mask) == 0) // filter test lda audctls+%%1 .if (%%1 == 0) and #CHANNEL0_FILTER_MASK .else and #CHANNEL1_FILTER_MASK .endif bne filter rts filter: // if (accents[env_ind] == 0) ldy env_ind+%%1 lda (%%3),y bne filter_with_no_transposition filter_with_note_transposition: // audf+2 = FrqTbl[tmp_note + acc_params[0]] + tmp_freq + 0xff; //lda tmp_note txa clc ldy #ACC_OFFSET_FILTER_TRANSP adc (%%4),y tax lda FrqTbl,x clc adc tmp_freq clc adc #$ff sta audf+%%1+2 rts filter_with_no_transposition: // audf+2 = audf + 0xff; lda audf+%%1 clc adc #$ff sta audf+%%1+2 rts .endif .endm //****************************************************************************************************************************************************** .proc play_instr0 play_instr 0, instr0_ptr, acc0_ptr, acc_param0_ptr .endp //****************************************************************************************************************************************************** .proc play_instr1 play_instr 1, instr1_ptr, acc1_ptr, acc_param1_ptr .endp //****************************************************************************************************************************************************** .proc play_instr2 play_instr 2, instr2_ptr, acc2_ptr, acc_param2_ptr .endp //****************************************************************************************************************************************************** .proc play_instr3 play_instr 3, instr3_ptr, acc3_ptr, acc_param3_ptr .endp //****************************************************************************************************************************************************** .endl // NRefDltPlayer