;======================================================================== ; EVERYTHING IS CYCLE COUNTED ;======================================================================== ;===================================== ; Decode Note ;===================================== ; X points to the note offset ; Note! These timings are out of date (FIXME) ; Timings (from ===>) ; 00: 14+30 ; 0X: 14+15 ; 10: 14+5 +124 ; 1X: 14+5 +193 ; 2X/3X: 14+5 +17 ; 4X: 14+5+5 + 111 ; 5X-BX: 14+5+5+ 102 ; CX: ; DX/EX: ; FX: stop_decoding: ; we are still running, decrement and early return dec note_a+NOTE_LEN_COUNT,X ; 7 rts ; 6 ;===================================== ; Decode Line ;===================================== pt3_decode_line: ; decode_note(&pt3->a,&(pt3->a_addr),pt3); ldx #(NOTE_STRUCT_SIZE*0) ; 2 jsr decode_note ; 6+?? ; decode_note(&pt3->b,&(pt3->b_addr),pt3); ldx #(NOTE_STRUCT_SIZE*1) ; 2 jsr decode_note ; 6+?? ; decode_note(&pt3->c,&(pt3->c_addr),pt3); ldx #(NOTE_STRUCT_SIZE*2) ; 2 ;;jsr decode_note ; fall through ; if (pt3->a.all_done && pt3->b.all_done && pt3->c.all_done) { ; return 1; ; } ;================================= ;================================= ; decode note ;================================= ;================================= decode_note: ; Init vars ldy #0 ; 2 sty spec_command_smc+1 ; 4 ; Skip decode if note still running lda note_a+NOTE_LEN_COUNT,X ; 4 cmp #2 ; 2 bcs stop_decoding ; blt, assume not negative ; 3 ; -1 keep_decoding: lda note_a+NOTE_NOTE,X ; store prev note ; 4 sta prev_note_smc+1 ; 4 lda note_a+NOTE_TONE_SLIDING_H,X ; store prev sliding ; 4 sta prev_sliding_h_smc+1 ; 4 lda note_a+NOTE_TONE_SLIDING_L,X ; 4 sta prev_sliding_l_smc+1 ; 4 note_decode_loop: lda note_a+NOTE_LEN,X ; re-up length count ; 4 sta note_a+NOTE_LEN_COUNT,X ; 4 lda note_a+NOTE_ADDR_L,X ; 4 sta PATTERN_L ; 3 lda note_a+NOTE_ADDR_H,X ; 4 sta PATTERN_H ; 3 ; get next value lda (PATTERN_L),Y ; 5 sta note_command_smc+1 ; save termporarily ; 4 and #$0f ; 2 sta note_command_bottom_smc+1 ; 4 note_command_smc: lda #$d1 ; 2 ; Set up jump table that runs same speed on 6502 and 65c02 sty PT3_TEMP ; 3 and #$f0 ; 2 lsr ; 2 lsr ; 2 lsr ; 2 tay ; 2 lda decode_jump_table+1,y ; 4 pha ; 3 lda decode_jump_table,y ; 4 pha ; 3 ldy PT3_TEMP ; 3 rts ; 6 ;============= ; 23 decode_jump_table: .word decode_case_0X-1,decode_case_1X-1,decode_case_2X-1 .word decode_case_3X-1,decode_case_4X-1,decode_case_5X-1 .word decode_case_6X-1,decode_case_7X-1,decode_case_8X-1 .word decode_case_9X-1,decode_case_AX-1,decode_case_BX-1 .word decode_case_CX-1,decode_case_DX-1,decode_case_EX-1 .word decode_case_FX-1 decode_case_0X: ;============================== ; $0X set special effect ;============================== note_command_bottom_smc: lda #$d1 ; 2 ; we can always store spec as 0 means no spec ; FIXME: what if multiple spec commands? ; Doesn't seem to happen in practice ; But AY_emul has code to handle it sta spec_command_smc+1 ; 4 bne decode_case_0X_not_zero ; 2/3 ;============= ; 8 ; 00 case ; means end of pattern ; -1 sta note_a+NOTE_LEN_COUNT,X ; len_count=0; ; 4 dec pt3_pattern_done_smc+1 ; 6 jmp note_done_decoding ; 3 decode_case_1X: ;============================== ; $1X -- Set Envelope Type ;============================== lda note_command_bottom_smc+1 ; 4 bne decode_case_not_10 ; 3 decode_case_10: ; 10 case - disable ; -1 sta note_a+NOTE_ENVELOPE_ENABLED,X ; A is 0 ; 5 beq decode_case_1x_common ; branch always ; 3 decode_case_not_10: ; -1 jsr set_envelope ; 6+64 decode_case_1x_common: iny ; 2 lda (PATTERN_L),Y ; 5+ lsr ; 2 jsr load_sample ; 6+86 lda #0 ; 2 sta note_a+NOTE_ORNAMENT_POSITION,X ; ornament_position=0 ; 5 decode_case_0X_not_zero: jmp done_decode_loop ; 3 decode_case_2X: decode_case_3X: ;============================== ; $2X/$3X set noise period ;============================== lda note_command_smc+1 ; 4 adc #$e0 ; same as subtract $20 ; 2 sta pt3_noise_period_smc+1 ; 3 jmp done_decode_loop ; 3 ;=========== ; 16 decode_case_4X: ;============================== ; $4X -- set ornament ;============================== lda note_command_bottom_smc+1; set ornament to bottom nibble; 4 jsr load_ornament ; 6+93 jmp done_decode_loop ; 3 ;============ ; 110 decode_case_5X: decode_case_6X: decode_case_7X: decode_case_8X: decode_case_9X: decode_case_AX: ;============================== ; $5X-$AX set note ;============================== lda note_command_smc+1 ; 4 adc #$b0 ; 2 sta note_a+NOTE_NOTE,X ; note=(current_val-0x50); ; 5 jsr reset_note ; 6+69 lda #1 ; 2 sta note_a+NOTE_ENABLED,X ; enabled=1 ; 5 bne note_done_decoding ; 3 decode_case_BX: ;============================================ ; $BX -- note length or envelope manipulation ;============================================ lda note_command_bottom_smc+1 ; 4 beq decode_case_b0 ; 3 ; -1 sec sbc #1 ; envelope_type=(current_val&0xf)-1; ; 2 bne decode_case_bx_higher ; 3 decode_case_b1: ; Set Length ; get next byte iny ; 2 lda (PATTERN_L),Y ; 5 sta note_a+NOTE_LEN,X ; 5 sta note_a+NOTE_LEN_COUNT,X ; 5 bcs done_decode_loop ; branch always ; 3 decode_case_b0: ; Disable envelope sta note_a+NOTE_ENVELOPE_ENABLED,X ; 5 sta note_a+NOTE_ORNAMENT_POSITION,X ; 5 beq done_decode_loop ; 3 decode_case_bx_higher: jsr set_envelope ; 6+64 bcs done_decode_loop ; branch always ; 3 decode_case_CX: ;============================== ; $CX -- set volume ;============================== lda note_command_bottom_smc+1 ; 4 bne decode_case_cx_not_c0 ; 3 ; -1 decode_case_c0: ; special case $C0 means shut down the note sta note_a+NOTE_ENABLED,X ; enabled=0 ; 5 jsr reset_note ; 6+69 beq note_done_decoding ; branch always ; 3 decode_case_cx_not_c0: sta note_a+NOTE_VOLUME,X ; volume=current_val&0xf; 5 bne done_decode_loop ; branch always ; 3 decode_case_DX: decode_case_EX: ;============================== ; $DX/$EX -- change sample ;============================== ; D0 = special case (end note) ; D1-EF = set sample to (value - $D0) lda note_command_smc+1 ; 4 sec ; 2 sbc #$d0 ; 2 beq note_done_decoding ; 3 decode_case_not_d0: ; -1 jsr load_sample ; load sample in bottom nybble ; 6+?? bcc done_decode_loop; branch always ; 3 ;======================== ; d0 case means end note ;decode_case_d0: ; jmp note_done_decoding ;============================== ; $FX - change ornament/sample ;============================== decode_case_FX: ; disable envelope lda #0 ; 2 sta note_a+NOTE_ENVELOPE_ENABLED,X ; 5 ; Set ornament to low byte of command lda note_command_bottom_smc+1 ; 4 jsr load_ornament ; ornament to load in A ; 6+? ; Get next byte iny ; point to next byte ; 2 lda (PATTERN_L),Y ; 5 ; Set sample to value/2 lsr ; divide by two ; 2 jsr load_sample ; sample to load in A ; 6+? ; fallthrough done_decode_loop: iny ; point to next byte ; 2 jmp note_decode_loop ; 3 note_done_decoding: iny ; point to next byte ; 2 ;================================= ; handle effects ;================================= ; Note, the AYemul code has code to make sure these are applied ; In the same order they appear. We don't bother? handle_effects: spec_command_smc: lda #$d1 ; 2 ;============================== ; Effect #1 -- Tone Down ;============================== effect_1: cmp #$1 ; 2 bne effect_2 ; 3 ; -1 sta note_a+NOTE_SIMPLE_GLISS,X ; 5 lsr ; 2 sta note_a+NOTE_ONOFF,X ; 5 lda (PATTERN_L),Y ; load byte, set as slide delay ; 5 iny ; 2 sta note_a+NOTE_TONE_SLIDE_DELAY,X ; 5 sta note_a+NOTE_TONE_SLIDE_COUNT,X ; 5 lda (PATTERN_L),Y ; load byte, set as slide step low ; 5 iny ; 2 sta note_a+NOTE_TONE_SLIDE_STEP_L,X ; 5 lda (PATTERN_L),Y ; load byte, set as slide step high ; 5 iny ; 2 sta note_a+NOTE_TONE_SLIDE_STEP_H,X ; 5 jmp no_effect ; 3 ;============================== ; Effect #2 -- Portamento ;============================== effect_2: cmp #$2 ; 2 beq effect_2_small ; 3 ; -1 jmp effect_3 ; 3 effect_2_small: ; FIXME: make smaller lda #0 ; 2 sta note_a+NOTE_SIMPLE_GLISS,X ; 5 sta note_a+NOTE_ONOFF,X ; 5 lda (PATTERN_L),Y ; load byte, set as delay ; 5 iny ; 2 sta note_a+NOTE_TONE_SLIDE_DELAY,X ; 5 sta note_a+NOTE_TONE_SLIDE_COUNT,X ; 5 iny ; 2 iny ; 2 iny ; 2 lda (PATTERN_L),Y ; load byte, set as slide_step high ; 5 php ; 3 ; 16-bit absolute value bpl slide_step_positive1 ; 3 ;-1 eor #$ff ; 2 slide_step_positive1: sta note_a+NOTE_TONE_SLIDE_STEP_H,X ; 5 dey ; 2 lda (PATTERN_L),Y ; load byte, set as slide_step low ; 5 plp ; 4 clc ; 2 bpl slide_step_positive2 ; 3 ;-1 eor #$ff ; 2 sec ; 2 slide_step_positive2: adc #$0 ; 2 sta note_a+NOTE_TONE_SLIDE_STEP_L,X ; 5 bcc skip_step_inc1 ; 3 inc note_a+NOTE_TONE_SLIDE_STEP_H,X ; 7 skip_step_inc1: iny ; moved here as it messed with flags ; 2 iny ; 2 ; a->tone_delta=GetNoteFreq(a->note,pt3)- ; GetNoteFreq(prev_note,pt3); sty PT3_TEMP ; save Y prev_note_smc: ldy #$d1 lda NoteTable_low,Y ; GetNoteFreq sta temp_word_l2_smc+1 lda NoteTable_high,Y ; GetNoteFreq sta temp_word_h2_smc+1 ldy note_a+NOTE_NOTE,X lda NoteTable_low,Y ; GetNoteFreq sec temp_word_l2_smc: sbc #$d1 sta note_a+NOTE_TONE_DELTA_L,X lda NoteTable_high,Y ; GetNoteFreq temp_word_h2_smc: sbc #$d1 sta note_a+NOTE_TONE_DELTA_H,X ; a->slide_to_note=a->note; lda note_a+NOTE_NOTE,X sta note_a+NOTE_SLIDE_TO_NOTE,X ldy PT3_TEMP ; restore Y ; a->note=prev_note; lda prev_note_smc+1 sta note_a+NOTE_NOTE,X ; implement file version 6 and above slide behavior ; this is done by SMC at song init time version_smc: jmp weird_version ; (JMP to BIT via smc) ; 3 prev_sliding_l_smc: lda #$d1 sta note_a+NOTE_TONE_SLIDING_L,X prev_sliding_h_smc: lda #$d1 sta note_a+NOTE_TONE_SLIDING_H,X weird_version: ; annoying 16-bit subtract, only care if negative ; if ((a->tone_delta - a->tone_sliding) < 0) { sec lda note_a+NOTE_TONE_DELTA_L,X sbc note_a+NOTE_TONE_SLIDING_L,X lda note_a+NOTE_TONE_DELTA_H,X sbc note_a+NOTE_TONE_SLIDING_H,X bpl no_effect ; a->tone_slide_step = -a->tone_slide_step; lda note_a+NOTE_TONE_SLIDE_STEP_L,X eor #$ff clc adc #$1 sta note_a+NOTE_TONE_SLIDE_STEP_L,X lda note_a+NOTE_TONE_SLIDE_STEP_H,X eor #$ff adc #$0 sta note_a+NOTE_TONE_SLIDE_STEP_H,X jmp no_effect ;============================== ; Effect #3 -- Sample Position ;============================== effect_3: cmp #$3 bne effect_4 lda (PATTERN_L),Y ; load byte, set as sample position iny sta note_a+NOTE_SAMPLE_POSITION,X bne no_effect ; branch always ;============================== ; Effect #4 -- Ornament Position ;============================== effect_4: cmp #$4 bne effect_5 lda (PATTERN_L),Y ; load byte, set as ornament position iny sta note_a+NOTE_ORNAMENT_POSITION,X bne no_effect ; branch always ;============================== ; Effect #5 -- Vibrato ;============================== effect_5: cmp #$5 bne effect_8 lda (PATTERN_L),Y ; load byte, set as onoff delay iny sta note_a+NOTE_ONOFF_DELAY,X sta note_a+NOTE_ONOFF,X lda (PATTERN_L),Y ; load byte, set as offon delay iny sta note_a+NOTE_OFFON_DELAY,X lda #0 sta note_a+NOTE_TONE_SLIDE_COUNT,X sta note_a+NOTE_TONE_SLIDING_L,X sta note_a+NOTE_TONE_SLIDING_H,X beq no_effect ; branch always ;============================== ; Effect #8 -- Envelope Down ;============================== effect_8: cmp #$8 bne effect_9 ; delay lda (PATTERN_L),Y ; load byte, set as speed iny sta pt3_envelope_delay_smc+1 sta pt3_envelope_delay_orig_smc+1 ; low value lda (PATTERN_L),Y ; load byte, set as low iny sta pt3_envelope_slide_add_l_smc+1 ; high value lda (PATTERN_L),Y ; load byte, set as high iny sta pt3_envelope_slide_add_h_smc+1 bne no_effect ; branch always ;============================== ; Effect #9 -- Set Speed ;============================== effect_9: cmp #$9 bne no_effect lda (PATTERN_L),Y ; load byte, set as speed iny sta pt3_speed_smc+1 no_effect: ;================================ ; add y into the address pointer clc tya adc note_a+NOTE_ADDR_L,X sta note_a+NOTE_ADDR_L,X lda #0 adc note_a+NOTE_ADDR_H,X sta note_a+NOTE_ADDR_H,X sta PATTERN_H rts ;======================================= ; Set Envelope ;======================================= ; pulls out common code from $1X and $BX ; commands ; A = new envelope type set_envelope: sta pt3_envelope_type_smc+1 ; 4 ; give fake old to force update? maybe only needed if printing? ; pt3->envelope_type_old=0x78; lda #$78 ; 2 sta pt3_envelope_type_old_smc+1 ; 4 ; get next byte iny ; 2 lda (PATTERN_L),Y ; 5+ sta pt3_envelope_period_h_smc+1 ; 4 iny ; 2 lda (PATTERN_L),Y ; 5+ sta pt3_envelope_period_l_smc+1 ; 4 lda #1 ; 2 sta note_a+NOTE_ENVELOPE_ENABLED,X ; envelope_enabled=1 ; 5 lsr ; 2 sta note_a+NOTE_ORNAMENT_POSITION,X ; ornament_position=0 ; 5 sta pt3_envelope_delay_smc+1 ; envelope_delay=0 ; 4 sta pt3_envelope_slide_l_smc+1 ; envelope_slide=0 ; 4 sta pt3_envelope_slide_h_smc+1 ; 4 rts ; 6 ;=========== ; 64 ;======================== ; reset note ;======================== ; common code from the decode note code reset_note: lda #0 ; 2 sta note_a+NOTE_SAMPLE_POSITION,X ; sample_position=0 ; 4 sta note_a+NOTE_AMPLITUDE_SLIDING,X ; amplitude_sliding=0 ; 4 sta note_a+NOTE_NOISE_SLIDING,X ; noise_sliding=0 ; 4 sta note_a+NOTE_ENVELOPE_SLIDING,X ; envelope_sliding=0 ; 4 sta note_a+NOTE_ORNAMENT_POSITION,X ; ornament_position=0 ; 4 sta note_a+NOTE_TONE_SLIDE_COUNT,X ; tone_slide_count=0 ; 4 sta note_a+NOTE_TONE_SLIDING_L,X ; tone_sliding=0 ; 4 sta note_a+NOTE_TONE_SLIDING_H,X ; 4 sta note_a+NOTE_TONE_ACCUMULATOR_L,X ; tone_accumulator=0 ; 4 sta note_a+NOTE_TONE_ACCUMULATOR_H,X ; 4 sta note_a+NOTE_ONOFF,X ; onoff=0; ; 4 rts ; 6 ;============ ; 52