From 86fcf6b3ee6483869c94991a26ec317c07c4df06 Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Thu, 18 Jan 2024 00:04:39 -0500 Subject: [PATCH] pt3_lib_ch6: actually more or less works --- music/pt3_lib_6ch/Makefile | 10 +- music/pt3_lib_6ch/pt3_lib_common.s | 1972 --------------------- music/pt3_lib_6ch/pt3_lib_init.s | 4 +- music/pt3_lib_6ch/pt3_lib_irq_handler.s | 24 +- music/pt3_lib_6ch/pt3_test.s | 11 +- music/pt3_lib_6ch/release/pt3_lib_ch6.dsk | Bin 0 -> 143360 bytes music/pt3_lib_6ch/zp.inc | 5 +- 7 files changed, 22 insertions(+), 2004 deletions(-) create mode 100644 music/pt3_lib_6ch/release/pt3_lib_ch6.dsk diff --git a/music/pt3_lib_6ch/Makefile b/music/pt3_lib_6ch/Makefile index 7efd2ed6..1c588f8c 100644 --- a/music/pt3_lib_6ch/Makefile +++ b/music/pt3_lib_6ch/Makefile @@ -5,15 +5,15 @@ PNG2GR = ../../utils/gr-utils/png2gr TOKENIZE = ../../utils/asoft_basic-utils/tokenize_asoft EMPTY_DISK = ../../empty_disk -all: pt3_lib.dsk +all: pt3_lib_ch6.dsk $(DOS33): cd ../../utils/dos33fs-utils && make -pt3_lib.dsk: PT3_TEST HELLO - cp $(EMPTY_DISK)/empty.dsk pt3_lib.dsk - $(DOS33) -y pt3_lib.dsk SAVE A HELLO - $(DOS33) -y pt3_lib.dsk BSAVE -a 0x1000 PT3_TEST +pt3_lib_ch6.dsk: PT3_TEST HELLO + cp $(EMPTY_DISK)/empty.dsk pt3_lib_ch6.dsk + $(DOS33) -y pt3_lib_ch6.dsk SAVE A HELLO + $(DOS33) -y pt3_lib_ch6.dsk BSAVE -a 0x1000 PT3_TEST HELLO: hello.bas $(TOKENIZE) < hello.bas > HELLO diff --git a/music/pt3_lib_6ch/pt3_lib_common.s b/music/pt3_lib_6ch/pt3_lib_common.s index f6fe9a20..c2f2dead 100644 --- a/music/pt3_lib_6ch/pt3_lib_common.s +++ b/music/pt3_lib_6ch/pt3_lib_common.s @@ -70,1978 +70,6 @@ NOTE_TONE_SLIDE_TO_STEP =39 NOTE_STRUCT_SIZE=40 -.if 0 - -.ifdef PT3_USE_ZERO_PAGE -note_a = $80 -note_b = $80+(NOTE_STRUCT_SIZE*1) -note_c = $80+(NOTE_STRUCT_SIZE*2) - -begin_vars=$80 -end_vars=$80+(NOTE_STRUCT_SIZE*3) - - -.else ; !PT3_USE_ZERO_PAGE -begin_vars: - -note_a: ; reset? - - .byte $0 ; NOTE_VOLUME ; 0 ; Y - .byte $0 ; NOTE_TONE_SLIDING_L ; 1 ; Y - .byte $0 ; NOTE_TONE_SLIDING_H ; 2 ; Y - .byte $0 ; NOTE_ENABLED ; 3 ; Y - .byte $0 ; NOTE_ENVELOPE_ENABLED ; 4 ; Y - .byte $0 ; NOTE_SAMPLE_POINTER_L ; 5 ; Y - .byte $0 ; NOTE_SAMPLE_POINTER_H ; 6 ; Y - .byte $0 ; NOTE_SAMPLE_LOOP ; 7 ; Y - .byte $0 ; NOTE_SAMPLE_LENGTH ; 8 ; Y - .byte $0 ; NOTE_TONE_L ; 9 - .byte $0 ; NOTE_TONE_H ; 10 - .byte $0 ; NOTE_AMPLITUDE ; 11 - .byte $0 ; NOTE_NOTE ; 12 - .byte $0 ; NOTE_LEN ; 13 - .byte $0 ; NOTE_LEN_COUNT ; 14 - .byte $0 ; NOTE_ADDR_L ; 15 - .byte $0 ; NOTE_ADDR_H ; 16 - .byte $0 ; NOTE_ORNAMENT_POINTER_L ; 17 ; Y - .byte $0 ; NOTE_ORNAMENT_POINTER_H ; 18 ; Y - .byte $0 ; NOTE_ORNAMENT_LOOP ; 19 ; Y - .byte $0 ; NOTE_ORNAMENT_LENGTH ; 20 ; Y - .byte $0 ; NOTE_ONOFF ; 21 - .byte $0 ; NOTE_TONE_ACCUMULATOR_L ; 22 - .byte $0 ; NOTE_TONE_ACCUMULATOR_H ; 23 - .byte $0 ; NOTE_TONE_SLIDE_COUNT ; 24 - .byte $0 ; NOTE_ORNAMENT_POSITION ; 25 ; Y - .byte $0 ; NOTE_SAMPLE_POSITION ; 26 ; Y - .byte $0 ; NOTE_ENVELOPE_SLIDING ; 27 - .byte $0 ; NOTE_NOISE_SLIDING ; 28 - .byte $0 ; NOTE_AMPLITUDE_SLIDING ; 29 - .byte $0 ; NOTE_ONOFF_DELAY ; 30 - .byte $0 ; NOTE_OFFON_DELAY ; 31 - .byte $0 ; NOTE_TONE_SLIDE_STEP_L ; 32 - .byte $0 ; NOTE_TONE_SLIDE_STEP_H ; 33 - .byte $0 ; NOTE_TONE_SLIDE_DELAY ; 34 - .byte $0 ; NOTE_SIMPLE_GLISS ; 35 - .byte $0 ; NOTE_SLIDE_TO_NOTE ; 36 - .byte $0 ; NOTE_TONE_DELTA_L ; 37 - .byte $0 ; NOTE_TONE_DELTA_H ; 38 - .byte $0 ; NOTE_TONE_SLIDE_TO_STEP ; 39 - -note_b: - .byte $0 ; NOTE_VOLUME - .byte $0 ; NOTE_TONE_SLIDING_L - .byte $0 ; NOTE_TONE_SLIDING_H - .byte $0 ; NOTE_ENABLED - .byte $0 ; NOTE_ENVELOPE_ENABLED - .byte $0 ; NOTE_SAMPLE_POINTER_L - .byte $0 ; NOTE_SAMPLE_POINTER_H - .byte $0 ; NOTE_SAMPLE_LOOP - .byte $0 ; NOTE_SAMPLE_LENGTH - .byte $0 ; NOTE_TONE_L - .byte $0 ; NOTE_TONE_H - .byte $0 ; NOTE_AMPLITUDE - .byte $0 ; NOTE_NOTE - .byte $0 ; NOTE_LEN - .byte $0 ; NOTE_LEN_COUNT - .byte $0 ; NOTE_ADDR_L - .byte $0 ; NOTE_ADDR_H - .byte $0 ; NOTE_ORNAMENT_POINTER_L - .byte $0 ; NOTE_ORNAMENT_POINTER_H - .byte $0 ; NOTE_ORNAMENT_LOOP - .byte $0 ; NOTE_ORNAMENT_LENGTH - .byte $0 ; NOTE_ONOFF - .byte $0 ; NOTE_TONE_ACCUMULATOR_L - .byte $0 ; NOTE_TONE_ACCUMULATOR_H - .byte $0 ; NOTE_TONE_SLIDE_COUNT - .byte $0 ; NOTE_ORNAMENT_POSITION - .byte $0 ; NOTE_SAMPLE_POSITION - .byte $0 ; NOTE_ENVELOPE_SLIDING - .byte $0 ; NOTE_NOISE_SLIDING - .byte $0 ; NOTE_AMPLITUDE_SLIDING - .byte $0 ; NOTE_ONOFF_DELAY - .byte $0 ; NOTE_OFFON_DELAY - .byte $0 ; NOTE_TONE_SLIDE_STEP_L - .byte $0 ; NOTE_TONE_SLIDE_STEP_H - .byte $0 ; NOTE_TONE_SLIDE_DELAY - .byte $0 ; NOTE_SIMPLE_GLISS - .byte $0 ; NOTE_SLIDE_TO_NOTE - .byte $0 ; NOTE_TONE_DELTA_L - .byte $0 ; NOTE_TONE_DELTA_H - .byte $0 ; NOTE_TONE_SLIDE_TO_STEP - -note_c: - .byte $0 ; NOTE_VOLUME - .byte $0 ; NOTE_TONE_SLIDING_L - .byte $0 ; NOTE_TONE_SLIDING_H - .byte $0 ; NOTE_ENABLED - .byte $0 ; NOTE_ENVELOPE_ENABLED - .byte $0 ; NOTE_SAMPLE_POINTER_L - .byte $0 ; NOTE_SAMPLE_POINTER_H - .byte $0 ; NOTE_SAMPLE_LOOP - .byte $0 ; NOTE_SAMPLE_LENGTH - .byte $0 ; NOTE_TONE_L - .byte $0 ; NOTE_TONE_H - .byte $0 ; NOTE_AMPLITUDE - .byte $0 ; NOTE_NOTE - .byte $0 ; NOTE_LEN - .byte $0 ; NOTE_LEN_COUNT - .byte $0 ; NOTE_ADDR_L - .byte $0 ; NOTE_ADDR_H - .byte $0 ; NOTE_ORNAMENT_POINTER_L - .byte $0 ; NOTE_ORNAMENT_POINTER_H - .byte $0 ; NOTE_ORNAMENT_LOOP - .byte $0 ; NOTE_ORNAMENT_LENGTH - .byte $0 ; NOTE_ONOFF - .byte $0 ; NOTE_TONE_ACCUMULATOR_L - .byte $0 ; NOTE_TONE_ACCUMULATOR_H - .byte $0 ; NOTE_TONE_SLIDE_COUNT - .byte $0 ; NOTE_ORNAMENT_POSITION - .byte $0 ; NOTE_SAMPLE_POSITION - .byte $0 ; NOTE_ENVELOPE_SLIDING - .byte $0 ; NOTE_NOISE_SLIDING - .byte $0 ; NOTE_AMPLITUDE_SLIDING - .byte $0 ; NOTE_ONOFF_DELAY - .byte $0 ; NOTE_OFFON_DELAY - .byte $0 ; NOTE_TONE_SLIDE_STEP_L - .byte $0 ; NOTE_TONE_SLIDE_STEP_H - .byte $0 ; NOTE_TONE_SLIDE_DELAY - .byte $0 ; NOTE_SIMPLE_GLISS - .byte $0 ; NOTE_SLIDE_TO_NOTE - .byte $0 ; NOTE_TONE_DELTA_L - .byte $0 ; NOTE_TONE_DELTA_H - .byte $0 ; NOTE_TONE_SLIDE_TO_STEP -end_vars: -.endif - - -.ifndef PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION - - ; Set to 1MHz mode (no convert freq) - ; this saves a few 100 cycles? -pt3_toggle_freq_conversion: - lda convert_177_smc1 - eor #$20 - bne pt3_freq_common -pt3_enable_freq_conversion: - lda #$38 ; SEC - bne pt3_freq_common ; bra -pt3_disable_freq_conversion: - lda #$18 ; CLC -pt3_freq_common: - sta convert_177_smc1 - sta convert_177_smc2 - sta convert_177_smc3 - sta convert_177_smc4 - sta convert_177_smc5 - rts -.endif - -load_ornament0_sample1: - lda #0 ; 2 - jsr load_ornament ; 6 - ; fall through - - ;=========================== - ; Load Sample - ;=========================== - ; sample in A - ; which note offset in X - - ; Sample table pointers are 16-bits little endian - ; There are 32 of these pointers starting at $6a:$69 - ; Our sample starts at address (A*2)+that pointer - ; We point SAMPLE_H:SAMPLE_L to this - ; then we load the length/data values - ; and then leave SAMPLE_H:SAMPLE_L pointing to begnning of - ; the sample data - - ; Optimization: - ; see comments on ornament setting - -load_sample1: - lda #1 ; 2 - -load_sample: - - sty PT3_TEMP ; 3 - - ;pt3->ornament_patterns[i]= - ; (pt3->data[0x6a+(i*2)]<<8)|pt3->data[0x69+(i*2)]; - - asl ; A*2 ; 2 - tay ; 2 - - ; Set the initial sample pointer - ; a->sample_pointer=pt3->sample_patterns[a->sample]; - - lda PT3_LOC+PT3_SAMPLE_LOC_L,Y ; 4+ - sta SAMPLE_L ; 3 - - lda PT3_LOC+PT3_SAMPLE_LOC_L+1,Y ; 4+ - - ; assume pt3 file is at page boundary - adc #>PT3_LOC ; 2 - sta SAMPLE_H ; 3 - - ; Set the loop value - ; a->sample_loop=pt3->data[a->sample_pointer]; - - ldy #0 ; 2 - lda (SAMPLE_L),Y ; 5+ - sta note_a+NOTE_SAMPLE_LOOP,X ; 5 - - ; Set the length value - ; a->sample_length=pt3->data[a->sample_pointer]; - - iny ; 2 - lda (SAMPLE_L),Y ; 5+ - sta note_a+NOTE_SAMPLE_LENGTH,X ; 5 - - ; Set pointer to beginning of samples - - lda SAMPLE_L ; 3 - adc #$2 ; 2 - sta note_a+NOTE_SAMPLE_POINTER_L,X ; 5 - lda SAMPLE_H ; 3 - adc #$0 ; 2 - sta note_a+NOTE_SAMPLE_POINTER_H,X ; 5 - - ldy PT3_TEMP ; 3 - - rts ; 6 - ;============ - ; 76 - - - ;=========================== - ; Load Ornament - ;=========================== - ; ornament value in A - ; note offset in X - - ; Ornament table pointers are 16-bits little endian - ; There are 16 of these pointers starting at $aa:$a9 - ; Our ornament starts at address (A*2)+that pointer - ; We point ORNAMENT_H:ORNAMENT_L to this - ; then we load the length/data values - ; and then leave ORNAMENT_H:ORNAMENT_L pointing to begnning of - ; the ornament data - - ; Optimization: - ; Loop and length only used once, can be located negative - ; from the pointer, but 6502 doesn't make addressing like that - ; easy. Can't self modify as channels A/B/C have own copies - ; of the var. - -load_ornament: - - sty PT3_TEMP ; save Y value ; 3 - - ;pt3->ornament_patterns[i]= - ; (pt3->data[0xaa+(i*2)]<<8)|pt3->data[0xa9+(i*2)]; - - asl ; A*2 ; 2 - tay ; 2 - - ; a->ornament_pointer=pt3->ornament_patterns[a->ornament]; - - lda PT3_LOC+PT3_ORNAMENT_LOC_L,Y ; 4+ - sta ORNAMENT_L ; 3 - - lda PT3_LOC+PT3_ORNAMENT_LOC_L+1,Y ; 4+ - - ; we're assuming PT3 is loaded to a page boundary - - adc #>PT3_LOC ; 2 - sta ORNAMENT_H ; 3 - - lda #0 ; 2 - sta note_a+NOTE_ORNAMENT_POSITION,X ; 5 - - tay ; 2 - - ; Set the loop value - ; a->ornament_loop=pt3->data[a->ornament_pointer]; - lda (ORNAMENT_L),Y ; 5+ - sta note_a+NOTE_ORNAMENT_LOOP,X ; 5 - - ; Set the length value - ; a->ornament_length=pt3->data[a->ornament_pointer]; - iny ; 2 - lda (ORNAMENT_L),Y ; 5+ - sta note_a+NOTE_ORNAMENT_LENGTH,X ; 5 - - ; Set the pointer to the value past the length - - lda ORNAMENT_L ; 3 - adc #$2 ; 2 - sta note_a+NOTE_ORNAMENT_POINTER_L,X ; 5 - lda ORNAMENT_H ; 3 - adc #$0 ; 2 - sta note_a+NOTE_ORNAMENT_POINTER_H,X ; 5 - - ldy PT3_TEMP ; restore Y value ; 3 - - rts ; 6 - - ;============ - ; 83 - - - - ;===================================== - ; Calculate Note - ;===================================== - ; note offset in X - -calculate_note: - - lda note_a+NOTE_ENABLED,X ; 4+ - bne note_enabled ; 2/3 - - sta note_a+NOTE_AMPLITUDE,X ; 5 - jmp done_note ; 3 - -note_enabled: - - lda note_a+NOTE_SAMPLE_POINTER_H,X ; 4+ - sta SAMPLE_H ; 3 - lda note_a+NOTE_SAMPLE_POINTER_L,X ; 4+ - sta SAMPLE_L ; 3 - - lda note_a+NOTE_ORNAMENT_POINTER_H,X ; 4+ - sta ORNAMENT_H ; 3 - lda note_a+NOTE_ORNAMENT_POINTER_L,X ; 4+ - sta ORNAMENT_L ; 3 - - - lda note_a+NOTE_SAMPLE_POSITION,X ; 4+ - asl ; 2 - asl ; 2 - tay ; 2 - - ; b0 = pt3->data[a->sample_pointer + a->sample_position * 4]; - lda (SAMPLE_L),Y ; 5+ - sta sample_b0_smc+1 ; 4 - - ; b1 = pt3->data[a->sample_pointer + a->sample_position * 4 + 1]; - iny ; 2 - lda (SAMPLE_L),Y ; 5+ - sta sample_b1_smc+1 ; 4 - - ; a->tone = pt3->data[a->sample_pointer + a->sample_position*4+2]; - ; a->tone+=(pt3->data[a->sample_pointer + a->sample_position*4+3])<<8; - ; a->tone += a->tone_accumulator; - iny ; 2 - lda (SAMPLE_L),Y ; 5+ - adc note_a+NOTE_TONE_ACCUMULATOR_L,X ; 4+ - sta note_a+NOTE_TONE_L,X ; 4 - - iny ; 2 - lda (SAMPLE_L),Y ; 5+ - adc note_a+NOTE_TONE_ACCUMULATOR_H,X ; 4+ - sta note_a+NOTE_TONE_H,X ; 4 - - ;============================= - ; Accumulate tone if set - ; (if sample_b1 & $40) - - bit sample_b1_smc+1 - bvc no_accum ; (so, if b1&0x40 is zero, skip it) - - sta note_a+NOTE_TONE_ACCUMULATOR_H,X - lda note_a+NOTE_TONE_L,X ; tone_accumulator=tone - sta note_a+NOTE_TONE_ACCUMULATOR_L,X - -no_accum: - - ;============================ - ; Calculate tone - ; j = a->note + (pt3->data[a->ornament_pointer + a->ornament_position] - clc ;;can be removed if ADC ACCUMULATOR_H cannot overflow - ldy note_a+NOTE_ORNAMENT_POSITION,X - lda (ORNAMENT_L),Y - adc note_a+NOTE_NOTE,X - - ; if (j < 0) j = 0; - bpl note_not_negative - lda #0 - - ; if (j > 95) j = 95; -note_not_negative: - cmp #96 - bcc note_not_too_high ; blt - - lda #95 - -note_not_too_high: - - ; w = GetNoteFreq(j,pt3->frequency_table); - - tay ; for GetNoteFreq later - - ; a->tone = (a->tone + a->tone_sliding + w) & 0xfff; - - clc - lda note_a+NOTE_TONE_SLIDING_L,X - adc note_a+NOTE_TONE_L,X - sta temp_word_l1_smc+1 - - lda note_a+NOTE_TONE_H,X - adc note_a+NOTE_TONE_SLIDING_H,X - sta temp_word_h1_smc+1 - - clc ;;can be removed if ADC SLIDING_H cannot overflow -temp_word_l1_smc: - lda #$d1 - adc NoteTable_low,Y ; GetNoteFreq - sta note_a+NOTE_TONE_L,X -temp_word_h1_smc: - lda #$d1 - adc NoteTable_high,Y ; GetNoteFreq - and #$0f - sta note_a+NOTE_TONE_H,X - - ;===================== - ; handle tone sliding - - lda note_a+NOTE_TONE_SLIDE_COUNT,X - bmi no_tone_sliding ; if (a->tone_slide_count > 0) { - beq no_tone_sliding - - dec note_a+NOTE_TONE_SLIDE_COUNT,X ; a->tone_slide_count--; - bne no_tone_sliding ; if (a->tone_slide_count==0) { - - - ; a->tone_sliding+=a->tone_slide_step - clc ;;can be removed if ADC freq_h cannot overflow - lda note_a+NOTE_TONE_SLIDING_L,X - adc note_a+NOTE_TONE_SLIDE_STEP_L,X - sta note_a+NOTE_TONE_SLIDING_L,X - tay ; save NOTE_TONE_SLIDING_L in y - lda note_a+NOTE_TONE_SLIDING_H,X - adc note_a+NOTE_TONE_SLIDE_STEP_H,X - sta note_a+NOTE_TONE_SLIDING_H,X - - ; a->tone_slide_count = a->tone_slide_delay; - lda note_a+NOTE_TONE_SLIDE_DELAY,X - sta note_a+NOTE_TONE_SLIDE_COUNT,X - - lda note_a+NOTE_SIMPLE_GLISS,X - bne no_tone_sliding ; if (!a->simplegliss) { - - ; FIXME: do these need to be signed compares? - -check1: - lda note_a+NOTE_TONE_SLIDE_STEP_H,X - bpl check2 ; if ( ((a->tone_slide_step < 0) && - - ; (a->tone_sliding <= a->tone_delta) || - - ; 16 bit signed compare - tya ; y has NOTE_TONE_SLIDING_L - cmp note_a+NOTE_TONE_DELTA_L,X ; NUM1-NUM2 - lda note_a+NOTE_TONE_SLIDING_H,X - sbc note_a+NOTE_TONE_DELTA_H,X - bvc sc_loser1 ; N eor V - eor #$80 -sc_loser1: - bmi slide_to_note ; then A (signed) < NUM (signed) and BMI will branch - - ; equals case - tya ; y has NOTE_TONE_SLIDING_L - cmp note_a+NOTE_TONE_DELTA_L,X - bne check2 - lda note_a+NOTE_TONE_SLIDING_H,X - cmp note_a+NOTE_TONE_DELTA_H,X - beq slide_to_note - -check2: - lda note_a+NOTE_TONE_SLIDE_STEP_H,X - bmi no_tone_sliding ; ((a->tone_slide_step >= 0) && - - ; (a->tone_sliding >= a->tone_delta) - - ; 16 bit signed compare - tya ; y has NOTE_TONE_SLIDING_L - cmp note_a+NOTE_TONE_DELTA_L,X ; num1-num2 - lda note_a+NOTE_TONE_SLIDING_H,X - sbc note_a+NOTE_TONE_DELTA_H,X - bvc sc_loser2 ; N eor V - eor #$80 -sc_loser2: - bmi no_tone_sliding ; then A (signed) < NUM (signed) and BMI will branch - -slide_to_note: - ; a->note = a->slide_to_note; - lda note_a+NOTE_SLIDE_TO_NOTE,X - sta note_a+NOTE_NOTE,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 - - -no_tone_sliding: - - ;========================= - ; Calculate the amplitude - ;========================= -calc_amplitude: - ; get base value from the sample (bottom 4 bits of sample_b1) - -sample_b1_smc: - lda #$d1 ; a->amplitude= (b1 & 0xf); - and #$f - - ;======================================== - ; if b0 top bit is set, it means sliding - - ; adjust amplitude sliding - - bit sample_b0_smc+1 ; if ((b0 & 0x80)!=0) { - bpl done_amp_sliding ; so if top bit not set, skip - tay - - ;================================ - ; if top bits 0b11 then slide up - ; if top bits 0b10 then slide down - - ; if ((b0 & 0x40)!=0) { - lda note_a+NOTE_AMPLITUDE_SLIDING,X - sec - bvc amp_slide_down - -amp_slide_up: - ; if (a->amplitude_sliding < 15) { - ; a pain to do signed compares - sbc #15 - bvc asu_signed - eor #$80 -asu_signed: - bpl done_amp_sliding ; skip if A>=15 - inc note_a+NOTE_AMPLITUDE_SLIDING,X ; a->amplitude_sliding++; - bne done_amp_sliding_y - -amp_slide_down: - ; if (a->amplitude_sliding > -15) { - ; a pain to do signed compares - sbc #$f1 ; -15 - bvc asd_signed - eor #$80 -asd_signed: - bmi done_amp_sliding ; if A < -15, skip subtract - - dec note_a+NOTE_AMPLITUDE_SLIDING,X ; a->amplitude_sliding--; - -done_amp_sliding_y: - tya - -done_amp_sliding: - - ; a->amplitude+=a->amplitude_sliding; - clc - adc note_a+NOTE_AMPLITUDE_SLIDING,X - - ; clamp amplitude to 0 - 15 - -check_amp_lo: - bmi write_clamp_amplitude - -check_amp_hi: - cmp #16 - bcc write_amplitude ; blt - lda #15 - .byte $2C -write_clamp_amplitude: - lda #0 -write_amplitude: - sta note_amp_smc+1 - -done_clamp_amplitude: - - ; We generate the proper table at runtime now - ; so always in Volume Table - ; a->amplitude = PT3VolumeTable_33_34[a->volume][a->amplitude]; - ; a->amplitude = PT3VolumeTable_35[a->volume][a->amplitude]; - - lda note_a+NOTE_VOLUME,X ; 4+ - asl ; 2 - asl ; 2 - asl ; 2 - asl ; 2 -note_amp_smc: - ora #$d1 ; 4+ - - tay ; 2 - lda VolumeTable,Y ; 4+ - sta note_a+NOTE_AMPLITUDE,X ; 5 - -done_table: - - -check_envelope_enable: - ; Bottom bit of b0 indicates our sample has envelope - ; Also make sure envelopes are enabled - - - ; if (((b0 & 0x1) == 0) && ( a->envelope_enabled)) { -sample_b0_smc: - lda #$d1 - lsr - tay - bcs envelope_slide - - lda note_a+NOTE_ENVELOPE_ENABLED,X - beq envelope_slide - - - ; Bit 4 of the per-channel AY-3-8910 amplitude specifies - ; envelope enabled - - lda note_a+NOTE_AMPLITUDE,X ; a->amplitude |= 16; - ora #$10 - sta note_a+NOTE_AMPLITUDE,X - - -envelope_slide: - - ; Envelope slide - ; If b1 top bits are 10 or 11 - - lda sample_b0_smc+1 - asl - asl - asl ; b0 bit 5 to carry flag - lda #$20 - bit sample_b1_smc+1 ; b1 bit 7 to sign flag, bit 5 to zero flag - php - bpl else_noise_slide ; if ((b1 & 0x80) != 0) { - tya - ora #$f0 - bcs envelope_slide_down ; if ((b0 & 0x20) == 0) { - -envelope_slide_up: - ; j = ((b0>>1)&0xF) + a->envelope_sliding; - and #$0f - clc - -envelope_slide_down: - - ; j = ((b0>>1)|0xF0) + a->envelope_sliding - adc note_a+NOTE_ENVELOPE_SLIDING,X - sta e_slide_amount_smc+1 ; j - -envelope_slide_done: - - plp - beq last_envelope ; if (( b1 & 0x20) != 0) { - - ; a->envelope_sliding = j; - sta note_a+NOTE_ENVELOPE_SLIDING,X - -last_envelope: - - ; pt3->envelope_add+=j; - - clc -e_slide_amount_smc: - lda #$d1 - adc pt3_envelope_add_smc+1 - sta pt3_envelope_add_smc+1 - - jmp noise_slide_done ; skip else - -else_noise_slide: - ; Noise slide - ; else { - - ; pt3->noise_add = (b0>>1) + a->noise_sliding; - tya - clc - adc note_a+NOTE_NOISE_SLIDING,X - sta pt3_noise_add_smc+1 - - plp - beq noise_slide_done ; if ((b1 & 0x20) != 0) { - - ; noise_sliding = pt3_noise_add - sta note_a+NOTE_NOISE_SLIDING,X - -noise_slide_done: - ;====================== - ; set mixer - - lda sample_b1_smc+1 ; pt3->mixer_value = ((b1 >>1) & 0x48) | pt3->mixer_value; - lsr - and #$48 - - ora PT3_MIXER_VAL ; 3 - sta PT3_MIXER_VAL ; 3 - - - ;======================== - ; increment sample position - - inc note_a+NOTE_SAMPLE_POSITION,X ; a->sample_position++; - - lda note_a+NOTE_SAMPLE_POSITION,X - cmp note_a+NOTE_SAMPLE_LENGTH,X - - bcc sample_pos_ok ; blt - - lda note_a+NOTE_SAMPLE_LOOP,X - sta note_a+NOTE_SAMPLE_POSITION,X - -sample_pos_ok: - - ;======================== - ; increment ornament position - - inc note_a+NOTE_ORNAMENT_POSITION,X ; a->ornament_position++; - lda note_a+NOTE_ORNAMENT_POSITION,X - cmp note_a+NOTE_ORNAMENT_LENGTH,X - - bcc ornament_pos_ok ; blt - - lda note_a+NOTE_ORNAMENT_LOOP,X - sta note_a+NOTE_ORNAMENT_POSITION,X -ornament_pos_ok: - - -done_note: - ; set mixer value - ; this is a bit complex (from original code) - ; after 3 calls it is set up properly - lsr PT3_MIXER_VAL - -handle_onoff: - ldy note_a+NOTE_ONOFF,X ;if (a->onoff>0) { - beq done_onoff - - dey ; a->onoff--; - - bne put_offon ; if (a->onoff==0) { - lda note_a+NOTE_ENABLED,X - eor #$1 ; toggle note_enabled - sta note_a+NOTE_ENABLED,X - - beq do_offon -do_onoff: - ldy note_a+NOTE_ONOFF_DELAY,X ; if (a->enabled) a->onoff=a->onoff_delay; - jmp put_offon -do_offon: - ldy note_a+NOTE_OFFON_DELAY,X ; else a->onoff=a->offon_delay; -put_offon: -.ifdef PT3_USE_ZERO_PAGE - sty note_a+NOTE_ONOFF,X -.else - lda note_a+NOTE_ONOFF,X - tay -.endif - -done_onoff: - - rts ; 6 - - - - - - - ;===================================== - ; 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) - jsr decode_note - - ; decode_note(&pt3->b,&(pt3->b_addr),pt3); - ldx #(NOTE_STRUCT_SIZE*1) - jsr decode_note - - ; decode_note(&pt3->c,&(pt3->c_addr),pt3); - ldx #(NOTE_STRUCT_SIZE*2) - ;;jsr decode_note ; fall through - -; if (pt3->a.all_done && pt3->b.all_done && pt3->c.all_done) { -; return 1; -; } - -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 ; 2/3 - -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 - - - ;============ - ; 24 - -note_decode_loop: - lda note_a+NOTE_LEN,X ; re-up length count ; 4+ - sta note_a+NOTE_LEN_COUNT,X ; 5 - - 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 - - ; FIXME: use a jump table?? - ; further reflection, that would require 32-bytes of addresses - ; in addition to needing X or Y to index the jump table. hmmm - - and #$f0 ; 2 - - ; cmp #$00 - bne decode_case_1X ; 2/3 - ;============= - ; 14 - -decode_case_0X: - ;============================== - ; $0X set special effect - ;============================== - ; -1 -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; ; 5 - - dec pt3_pattern_done_smc+1 ; 6 - - jmp note_done_decoding ; 3 - -decode_case_1X: - ;============================== - ; $1X -- Set Envelope Type - ;============================== - - cmp #$10 ; 2 - bne decode_case_2X ; 2/3 - ;============ - ; 5 - - ; -1 - 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 - ;============================== - - cmp #$40 ; 2 - bcs decode_case_4X ; branch greater/equal ; 3 - ; -1 - 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 - ;============================== -; cmp #$40 ; already set ; - bne decode_case_5X ; 3 - ; -1 - 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: - ;============================== - ; $5X-$AX set note - ;============================== - cmp #$B0 ; 2 - bcs decode_case_bX ; branch greater/equal ; 3 - - ; -1 - 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 - ;============================================ -; cmp #$b0 ; already set from before - bne decode_case_cX ; 3 - ; -1 - lda note_command_bottom_smc+1 ; 4 - beq decode_case_b0 ; 3 - ; -1 - 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 - ;============================== - cmp #$c0 ; check top nibble $C ; 2 - bne decode_case_dX ; 3 - ; -1 - 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: - ;============================== - ; $DX/$EX -- change sample - ;============================== - ; D0 = special case (end note) - ; D1-EF = set sample to (value - $D0) - - cmp #$f0 ; check top nibble $D/$E ; 2 - beq decode_case_fX ; 3 - ; -1 - - 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 ; 5 - sta note_a+NOTE_AMPLITUDE_SLIDING,X ; amplitude_sliding=0 ; 5 - sta note_a+NOTE_NOISE_SLIDING,X ; noise_sliding=0 ; 5 - sta note_a+NOTE_ENVELOPE_SLIDING,X ; envelope_sliding=0 ; 5 - sta note_a+NOTE_ORNAMENT_POSITION,X ; ornament_position=0 ; 5 - sta note_a+NOTE_TONE_SLIDE_COUNT,X ; tone_slide_count=0 ; 5 - sta note_a+NOTE_TONE_SLIDING_L,X ; tone_sliding=0 ; 5 - sta note_a+NOTE_TONE_SLIDING_H,X ; 5 - sta note_a+NOTE_TONE_ACCUMULATOR_L,X ; tone_accumulator=0 ; 5 - sta note_a+NOTE_TONE_ACCUMULATOR_H,X ; 5 - sta note_a+NOTE_ONOFF,X ; onoff=0; ; 5 - - rts ; 6 - ;============ - ; 69 - - - - - - - ;===================================== - ; Set Pattern - ;===================================== - ; FIXME: inline this? we do call it from outside - ; in the player note length code - -is_done: - ; done with song, set it to non-zero - sta DONE_SONG ; 3 - rts ; 6 - -pt3_set_pattern: - - ; Lookup current pattern in pattern table -current_pattern_smc: - ldy #$d1 ; 2 - lda PT3_LOC+PT3_PATTERN_TABLE,Y ; 4+ - - ; if value is $FF we are at the end of the song - cmp #$ff ; 2 - beq is_done ; 2/3 - - ;============ - ; 20 if end - -not_done: - - ; set up the three pattern address pointers - - ; BUG BUG BUG - ; pattern offset can be bigger than 128, and if we multiply - ; by two to get word size it will overflow - ; for example I have a .pt3 where pattern #48 ($30*3=$90) is used - -; asl ; mul pattern offset by two, as word sized ; 2 -; tay ; 2 - - ; point PATTERN_H/PATTERN_L to the pattern address table - -; clc ; 2 -; lda PT3_LOC+PT3_PATTERN_LOC_L ; 4 -; sta PATTERN_L ; 3 -; lda PT3_LOC+PT3_PATTERN_LOC_H ; 4 -; adc #>PT3_LOC ; assume page boundary ; 2 -; sta PATTERN_H ; 3 - - clc - sta PATTERN_L - adc PT3_LOC+PT3_PATTERN_LOC_L - php ; save carry as we might generate two - clc - adc PATTERN_L - sta PATTERN_L - - lda PT3_LOC+PT3_PATTERN_LOC_H ; 4 - adc #>PT3_LOC ; assume page boundary ; 2 - plp ; restore carry - adc #0 - sta PATTERN_H ; 3 - -; clc -; tya -; adc PATTERN_L -; adc PATTERN_L -; sta PATTERN_L -; lda #0 -; adc PATTERN_H -; sta PATTERN_H - - ldy #0 - - ; First 16-bits points to the Channel A address - lda (PATTERN_L),Y ; 5+ - sta note_a+NOTE_ADDR_L ; 4 - iny ; 2 - lda (PATTERN_L),Y ; 5+ - adc #>PT3_LOC ; assume page boundary ; 2 - sta note_a+NOTE_ADDR_H ; 4 - iny ; 2 - - ; Next 16-bits points to the Channel B address - lda (PATTERN_L),Y ; 5+ - sta note_b+NOTE_ADDR_L ; 4 - iny ; 2 - lda (PATTERN_L),Y ; 5+ - adc #>PT3_LOC ; assume page boundary ; 2 - sta note_b+NOTE_ADDR_H ; 4 - iny ; 2 - - ; Next 16-bits points to the Channel C address - lda (PATTERN_L),Y ; 5+ - sta note_c+NOTE_ADDR_L ; 4 - iny ; 2 - lda (PATTERN_L),Y ; 5+ - adc #>PT3_LOC ; assume page boundary ; 2 - sta note_c+NOTE_ADDR_H ; 4 - - ; clear out the noise channel - lda #0 ; 2 - sta pt3_noise_period_smc+1 ; 4 - - ; Set all three channels as active - ; FIXME: num_channels, may need to be 6 if doing 6-channel pt3? - lda #3 ; 2 - sta pt3_pattern_done_smc+1 ; 4 - - rts ; 6 - - - - ;===================================== - ; pt3 make frame - ;===================================== - ; update pattern or line if necessary - ; then calculate the values for the next frame - - ;========================== - ; pattern done early! - -early_end: - inc current_pattern_smc+1 ; increment pattern ; 6 - sta current_line_smc+1 ; 4 - sta current_subframe_smc+1 ; 4 - -check_subframe: - lda current_subframe_smc+1 ; 4 - bne pattern_good ; 2/3 - - ; load a new pattern in - jsr pt3_set_pattern ;6+? - - lda DONE_SONG ; 3 - beq pattern_good ; 2/3 - rts ; 6 - -pt3_make_frame: - - ; see if we need a new pattern - ; we do if line==0 and subframe==0 - ; allow fallthrough where possible -current_line_smc: - lda #$d1 ; 2 - beq check_subframe ; 2/3 - -pattern_good: - - ; see if we need a new line - -current_subframe_smc: - lda #$d1 ; 2 - bne line_good ; 2/3 - - ; decode a new line - jsr pt3_decode_line ; 6+? - - ; check if pattern done early -pt3_pattern_done_smc: - lda #$d1 ; 2 - beq early_end ; 2/3 - -line_good: - - ; Increment everything - - inc current_subframe_smc+1 ; subframe++ ; 6 - lda current_subframe_smc+1 ; 4 - - ; if we hit pt3_speed, move to next -pt3_speed_smc: - eor #$d1 ; 2 - bne do_frame ; 2/3 - -next_line: - sta current_subframe_smc+1 ; reset subframe to 0 ; 4 - - inc current_line_smc+1 ; and increment line ; 6 - lda current_line_smc+1 ; 4 - - eor #64 ; always end at 64. ; 2 - bne do_frame ; is this always needed? ; 2/3 - -next_pattern: - sta current_line_smc+1 ; reset line to 0 ; 4 - - inc current_pattern_smc+1 ; increment pattern ; 6 - -do_frame: - ; AY-3-8910 register summary - ; - ; R0/R1 = A period low/high - ; R2/R3 = B period low/high - ; R4/R5 = C period low/high - ; R6 = Noise period - ; R7 = Enable XX Noise=!CBA Tone=!CBA - ; R8/R9/R10 = Channel A/B/C amplitude M3210, M=envelope enable - ; R11/R12 = Envelope Period low/high - ; R13 = Envelope Shape, 0xff means don't write - ; R14/R15 = I/O (ignored) - - ldx #0 ; needed ; 2 - stx PT3_MIXER_VAL ; 3 - stx pt3_envelope_add_smc+1 ; 4 - - ;;ldx #(NOTE_STRUCT_SIZE*0) ; Note A ; 2 - jsr calculate_note ; 6+? - ldx #(NOTE_STRUCT_SIZE*1) ; Note B ; 2 - jsr calculate_note ; 6+? - ldx #(NOTE_STRUCT_SIZE*2) ; Note C ; 2 - jsr calculate_note ; 6+? - - ; Load up the Frequency Registers - - lda note_a+NOTE_TONE_L ; Note A Period L ; 4 - sta AY_REGISTERS+0 ; into R0 ; 3 - - lda note_a+NOTE_TONE_H ; Note A Period H ; 4 - sta AY_REGISTERS+1 ; into R1 ; 3 - lda note_a+NOTE_TONE_L ; Note A Period L ; 4 - -.ifndef PT3_DISABLE_FREQ_CONVERSION - -.ifndef PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION -convert_177_smc1: - - sec ; 2 - bcc no_scale_a ; 2/3 -.endif - - ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 - - ; conversion costs 100 cycles! - - ; first multiply by 8 - asl ; 2 - rol AY_REGISTERS+1 ; 5 - asl ; 2 - rol AY_REGISTERS+1 ; 5 - asl ; 2 - rol AY_REGISTERS+1 ; 5 - - ; add in original to get 9 - clc ; 2 - adc note_a+NOTE_TONE_L ; 4 - sta AY_REGISTERS+0 ; 3 - lda note_a+NOTE_TONE_H ; 4 - adc AY_REGISTERS+1 ; 3 - - ; divide by 16 to get proper value - ror ; 2 - ror AY_REGISTERS+0 ; 5 - ror ; 2 - ror AY_REGISTERS+0 ; 5 - ror ; 2 - ror AY_REGISTERS+0 ; 5 - ror ; 2 - ror AY_REGISTERS+0 ; 5 - and #$0f ; 2 - sta AY_REGISTERS+1 ; 3 -.endif - -no_scale_a: - - lda note_b+NOTE_TONE_L ; Note B Period L ; 4 - sta AY_REGISTERS+2 ; into R2 ; 3 - - lda note_b+NOTE_TONE_H ; Note B Period H ; 4 - sta AY_REGISTERS+3 ; into R3 ; 3 - lda note_b+NOTE_TONE_L ; Note B Period L ; 4 - -.ifndef PT3_DISABLE_FREQ_CONVERSION - -.ifndef PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION - -convert_177_smc2: - sec ; 2 - bcc no_scale_b ; 2/3 -.endif - ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 - - ; first multiply by 8 - asl ; 2 - rol AY_REGISTERS+3 ; 5 - asl ; 2 - rol AY_REGISTERS+3 ; 5 - asl ; 2 - rol AY_REGISTERS+3 ; 5 - - ; add in original to get 9 - clc ; 2 - adc note_b+NOTE_TONE_L ; 4 - sta AY_REGISTERS+2 ; 3 - lda note_b+NOTE_TONE_H ; 4 - adc AY_REGISTERS+3 ; 3 - - ; divide by 16 to get proper value - ror ; 2 - ror AY_REGISTERS+2 ; 5 - ror ; 2 - ror AY_REGISTERS+2 ; 5 - ror ; 2 - ror AY_REGISTERS+2 ; 5 - ror ; 2 - ror AY_REGISTERS+2 ; 5 - and #$0f ; 2 - sta AY_REGISTERS+3 ; 3 -.endif - -no_scale_b: - - lda note_c+NOTE_TONE_L ; Note C Period L ; 4 - sta AY_REGISTERS+4 ; into R4 ; 3 - lda note_c+NOTE_TONE_H ; Note C Period H ; 4 - sta AY_REGISTERS+5 ; into R5 ; 3 - lda note_c+NOTE_TONE_L ; Note C Period L ; 4 - -.ifndef PT3_DISABLE_FREQ_CONVERSION - -.ifndef PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION -convert_177_smc3: - sec ; 2 - bcc no_scale_c ; 2/3 -.endif - - ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 - - ; first multiply by 8 - asl ; 2 - rol AY_REGISTERS+5 ; 5 - asl ; 2 - rol AY_REGISTERS+5 ; 5 - asl ; 2 - rol AY_REGISTERS+5 ; 5 - - ; add in original to get 9 - clc ; 2 - adc note_c+NOTE_TONE_L ; 4 - sta AY_REGISTERS+4 ; 3 - lda note_c+NOTE_TONE_H ; 4 - adc AY_REGISTERS+5 ; 3 - - ; divide by 16 to get proper value - ror ; 2 - ror AY_REGISTERS+4 ; 5 - ror ; 2 - ror AY_REGISTERS+4 ; 5 - ror ; 2 - ror AY_REGISTERS+4 ; 5 - ror ; 2 - ror AY_REGISTERS+4 ; 5 - and #$0f ; 2 - sta AY_REGISTERS+5 ; 3 -.endif - -no_scale_c: - - - ; Noise - ; frame[6]= (pt3->noise_period+pt3->noise_add)&0x1f; - clc ; 2 -pt3_noise_period_smc: - lda #$d1 ; 2 -pt3_noise_add_smc: - adc #$d1 ; 2 - and #$1f ; 2 - -.ifndef PT3_DISABLE_ENABLE_FREQ_CONVERSION - - sta AY_REGISTERS+6 ; 3 - -.ifndef PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION - -convert_177_smc4: - sec ; 2 - bcc no_scale_n ; 2/3 -.endif - - ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 - - ; first multiply by 8 - asl ; 2 - asl ; 2 - asl ; 2 - - ; add in original to get 9 - adc AY_REGISTERS+6 ; 3 - - ; divide by 16 to get proper value - ror ; 2 - ror ; 2 - ror ; 2 - ror ; 2 - and #$1f ; 2 -.endif - -no_scale_n: - - sta AY_REGISTERS+6 ; 3 - - ;======================= - ; Mixer - - ; PT3_MIXER_VAL is already in AY_REGISTERS+7 - - ;======================= - ; Amplitudes - - lda note_a+NOTE_AMPLITUDE ; 3 - sta AY_REGISTERS+8 ; 3 - lda note_b+NOTE_AMPLITUDE ; 3 - sta AY_REGISTERS+9 ; 3 - lda note_c+NOTE_AMPLITUDE ; 3 - sta AY_REGISTERS+10 ; 3 - - ;====================================== - ; Envelope period - ; result=period+add+slide (16-bits) - clc ; 2 -pt3_envelope_period_l_smc: - lda #$d1 ; 2 -pt3_envelope_add_smc: - adc #$d1 ; 2 - tay ; 2 -pt3_envelope_period_h_smc: - lda #$d1 ; 2 - adc #0 ; 2 - tax ; 2 - - clc ; 2 - tya ; 2 -pt3_envelope_slide_l_smc: - adc #$d1 ; 2 - sta AY_REGISTERS+11 ; 3 - txa ; 2 -pt3_envelope_slide_h_smc: - adc #$d1 ; 2 - sta AY_REGISTERS+12 ; 3 - - -.ifndef PT3_DISABLE_FREQ_CONVERSION - -.ifndef PT3_DISABLE_SWITCHABLE_FREQ_CONVERSION - -convert_177_smc5: - sec - bcc no_scale_e ; 2/3 -.endif - ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 - - tay ; 2 - ; first multiply by 8 - lda AY_REGISTERS+11 ; 3 - asl ; 2 - rol AY_REGISTERS+12 ; 5 - asl ; 2 - rol AY_REGISTERS+12 ; 5 - asl ; 2 - rol AY_REGISTERS+12 ; 5 - - ; add in original to get 9 - clc ; 2 - adc AY_REGISTERS+11 ; 3 - sta AY_REGISTERS+11 ; 3 - tya ; 2 - adc AY_REGISTERS+12 ; 3 - - ; divide by 16 to get proper value - ror ; 2 - ror AY_REGISTERS+11 ; 5 - ror ; 2 - ror AY_REGISTERS+11 ; 5 - ror ; 2 - ror AY_REGISTERS+11 ; 5 - ror ; 2 - ror AY_REGISTERS+11 ; 5 - and #$0f ; 2 - sta AY_REGISTERS+12 ; 3 -.endif - -no_scale_e: - - ;======================== - ; Envelope shape - -pt3_envelope_type_smc: - lda #$d1 ; 2 -pt3_envelope_type_old_smc: - cmp #$d1 ; 2 - sta pt3_envelope_type_old_smc+1; copy old to new ; 4 - bne envelope_diff ; 2/3 -envelope_same: - lda #$ff ; if same, store $ff ; 2 -envelope_diff: - sta AY_REGISTERS+13 ; 3 - - - - ;============================== - ; end-of-frame envelope update - ;============================== - -pt3_envelope_delay_smc: - lda #$d1 ; 2 - beq done_do_frame ; assume can't be negative? ; 2/3 - ; do this if envelope_delay>0 - dec pt3_envelope_delay_smc+1 ; 6 - bne done_do_frame ; 2/3 - ; only do if we hit 0 -pt3_envelope_delay_orig_smc: - lda #$d1 ; reset envelope delay ; 2 - sta pt3_envelope_delay_smc+1 ; 4 - - clc ; 16-bit add ; 2 - lda pt3_envelope_slide_l_smc+1 ; 4 -pt3_envelope_slide_add_l_smc: - adc #$d1 ; 2 - sta pt3_envelope_slide_l_smc+1 ; 4 - lda pt3_envelope_slide_h_smc+1 ; 4 -pt3_envelope_slide_add_h_smc: - adc #$d1 ; 2 - sta pt3_envelope_slide_h_smc+1 ; 4 - -done_do_frame: - - rts ; 6 -.endif - ; note, you might have slightly better performance if these are aligned ; so that loads don't have to cross page boundaries diff --git a/music/pt3_lib_6ch/pt3_lib_init.s b/music/pt3_lib_6ch/pt3_lib_init.s index 64519f64..b5bde3ea 100644 --- a/music/pt3_lib_6ch/pt3_lib_init.s +++ b/music/pt3_lib_6ch/pt3_lib_init.s @@ -73,7 +73,6 @@ zero_song_structs_loop_2: lda PT3_LOC_2+PT3_LOOP ; 4 sta pt3_loop_smc_2+1 ; 4 -.if 0 ;=============================== ; pt3_init_song_first: @@ -137,8 +136,6 @@ zero_song_structs_loop: lda PT3_LOC+PT3_LOOP ; 4 sta pt3_loop_smc+1 ; 4 -.endif - ;======================== ;======================== @@ -290,6 +287,7 @@ not_ascii_number: adc #$20 ; BIT->JMP 2C->4C ; 2 version_greater_than_or_equal_6: sta version_smc_2 ; 4 + sta version_smc ; 4 pick_volume_table: diff --git a/music/pt3_lib_6ch/pt3_lib_irq_handler.s b/music/pt3_lib_6ch/pt3_lib_irq_handler.s index db80f6f4..f083c6c4 100644 --- a/music/pt3_lib_6ch/pt3_lib_irq_handler.s +++ b/music/pt3_lib_6ch/pt3_lib_irq_handler.s @@ -6,10 +6,12 @@ pt3_irq_smc1: bit MOCK_6522_T1CL ; clear 6522 interrupt by reading T1C-L ; 4 lda DONE_PLAYING ; 3 - beq pt3_play_music_2 ; if song done, don't play music ; 3/2nt + beq pt3_play_music_2 ; if song done, don't play music; 3/2nt jmp done_pt3_irq_handler ; 3 ;============ - ; 13 + + ;================================== + ; play song2 pt3_play_music_2: @@ -19,9 +21,9 @@ pt3_play_music_2: ; handle song over condition lda DONE_SONG_2 - beq pt3_play_music ; if not done, continue + beq pt3_play_music ; if not done, continue to other song - lda LOOP_2 ; see if looping + lda LOOP_2 ; see if looping, if so continue(?) beq pt3_play_music pt3_loop_smc_2: @@ -32,23 +34,16 @@ pt3_loop_smc_2: lda #$0 sta current_line_smc_2+1 sta current_subframe_smc_2+1 - sta DONE_SONG_2 ; undo the next song + sta DONE_SONG_2 ; undo the done song ; beq done_pt3_irq_handler_2 ; branch always beq pt3_play_music_2 ; branch always -;move_to_next: -; ; same as "press right" -; ldx #$20 -; jmp quiet_exit - pt3_play_music: -.if 0 jsr pt3_make_frame - ; handle song over condition lda DONE_SONG beq mb_write_frame ; if not done, continue @@ -74,7 +69,7 @@ move_to_next: ; same as "press right" ldx #$20 jmp quiet_exit -.endif + ;====================================== ; Write frames to Mockingboard ;====================================== @@ -82,7 +77,7 @@ move_to_next: ; the decode code mb_write_frame: -.if 0 + @@ -135,7 +130,6 @@ mb_no_write: ;============ ; 7 mb_skip_13: -.endif ;================================== ; do 6522 #2 diff --git a/music/pt3_lib_6ch/pt3_test.s b/music/pt3_lib_6ch/pt3_test.s index 227ffcc3..a48d1a59 100644 --- a/music/pt3_lib_6ch/pt3_test.s +++ b/music/pt3_lib_6ch/pt3_test.s @@ -12,7 +12,7 @@ ; Location the files load at. ; If you change this, you need to update the Makefile -;PT3_LOC = song +PT3_LOC = song PT3_LOC_2 = song2 ; the below will make for more compact code, at the expense @@ -248,6 +248,7 @@ found_message: .asciiz "FOUND SLOT#4" .endif .include "pt3_lib_common.s" +.include "pt3_lib_core.s" .include "pt3_lib_core2.s" .include "pt3_lib_init.s" .include "pt3_lib_mockingboard_setup.s" @@ -262,10 +263,10 @@ found_message: .asciiz "FOUND SLOT#4" ; this can be fixed but some changes would have ; to be made throughout the player code song: -;.incbin "music/dya_dance_1.pt3" -.incbin "../pt3_player/music/EA.PT3" +.incbin "music/dya_dance_1.pt3" +;.incbin "../pt3_player/music/EA.PT3" .align 256 song2: -;.incbin "music/dya_dance_2.pt3" -.incbin "../pt3_player/music/EA.PT3" +.incbin "music/dya_dance_2.pt3" +;.incbin "../pt3_player/music/EA.PT3" diff --git a/music/pt3_lib_6ch/release/pt3_lib_ch6.dsk b/music/pt3_lib_6ch/release/pt3_lib_ch6.dsk new file mode 100644 index 0000000000000000000000000000000000000000..b2b2b2b88565c1de4ed4e02cf3a27833146f4537 GIT binary patch literal 143360 zcmeI44O~=J{{Qct8HQm18AgF&RJ@2Hny$3=fYmCR7}%s{*7j)A13I#rnrO6&k}-

@&3w=>>* z?s@p0r}z1u^F8-mUb3!1{xI3(x@rP`VLtn4;ePullXOuxyG_Sm)K#|WQe(Wg>FmF0 zYSSUsKB(!UZu3>MMlokE>Tq!@FLKXZ+%1)!>Vh z&pG4WW7lo+H2C_;Ut4N7m$j8VuwDL|`LQ2&_xHl<+?fjk%e{L+kcf|g&wBYh+rvsu z)Rdg75m^3w4S%vmYN_ErtPxcF=^8=9pQ)+%w5F)|bCUe`npK35C@_xm8b_Xnd#0wW z?fH^ZHI|0mfM6+fNT1fEo%x*PhmZffrV05v3xe;Na5deO_3qg8Flhx)9ISxGo-|(4{PKPthzjo@m8r*OxBb%t5oYD zg)w}fM*7B+)yAKyu>$#;u?_NfR;IT~-@CPj+0I&7hGD;}VQLswb?PDsG3;lDnKH(y zDsqpSFp8-q_c~@*Ys+1wXUdqha%?@K_xsp%b!zhuJdD>v?4rW(_d@?Aoz2sfhRnR!l&7grE>*db{Sj4cc^Y?i$ByWo9JA9k$i#iVV@F@&Kb|bV zfrkkN3|0>bxk~gF_%>f<6`jtq=tlX%@$s_i_$^RHIbdZ|QwDGeefyB#+@Z<+`?dT3 z{M_?@actPQdCN-`Qsvf{tG9W!*X-E2tJeGK?zi?f?K|-HJ4fFA+j~df|KQm1PfwgY zb^6TN&%XHb?_Yg=?wfDF`~Jekw(4qUOZ6M|U2b)~=v-EoW$LbT2o%3j>$3e}YhVwgTWBC^WeBgznM?Du=Q6zxme&e`D`G zeflQI&HMWuJj5j)?w>SZVDg}pmJdJr_>;jyu4)}R>{r86ul{x5^AXovd)@VE=SSXf z<4wOAHJa5&_lOwEsSH}xZ&YErr2aayO2z1ysQx+~!wg{zDP|R;(nafas+4GUWdEz# z5EGX|#KTk}Lk*1XS4@a1#iUA2QgOf5M5}tKbZi3GV|Y?3u86oHqX#9WUZ0d|H0x4_ z;|hju=zq;kL$1|bJAB9x;tYf{*r2<5_~2;bL=ndj9UUEIG_q01fs)KBMe_R2JU76n z4c2067eh7X8*aEkHROgHjOJiPrbJ_+@hWD}udlj>(cLge#}4lZ^cti!jUIi?V8aU; zX;;5opc@+>{#E#Z#Nr~(rVD2{^-U`iIhH9`r8{k5tS)if;Ql&ihW3+Hb=p{Kfio=K zq#vMpAW33)0$`iD}9Tag?kO+#9vHFlP}uStJLr zOdP7kG6Cn$a9m@!x?0yb;*2KFn2d25qcbOjj0!FcwlI-9Sc~xWNm9Zu*vd4ZI)A2d|vC+6ra~P zS$C%BjB3sd^j?(-uPt*;CMt32yv3y5^4D13Y+|rlB{I{NX)8G&LekVl@>!jCrO&B# zXRf=BY4GXgubpc5P4-)yDtD&SfI_uvGE(t?5-asKbKP4hkNf8 zT~$d+8M8c@d;Jx(z%(Gu83|NHJ}WommC}GtESbJp`|wi99Yi?$Pty-l$2j92)8XT3r#uktprj;2TlcW{f7abH}= ztZ$IdJNxbUaQ(UuYKsi#gc^I6&}z>F`<>ea)!v=k0wdvbhU_@9e%)zo;?5~MNF<-C zakdII*u8PzZLNw?BCv16^Mm!mU$Bj{OsKI+E$ih_LF0^AlWA@GV7+`+y0{+cX(tT) z#q~`s>ybWK?PnmvI71-gX9F6)3c)s~20^Drh{l5r!)8xp`eEm}bDQP6tt%J*h4oe}hf~%s8;TY1FHddoZdf2bBU(jf{VvbR4bdC>Z~WE9+c#P_&fPfCW!&`V z7v)W#ZF;zDZdq>Gi88aR-WBC~&sA3TTvLZYoPD8&)>C>`9lymHOiSFY+%g zF8hAd_nWd0Gc7kA{Nmtst;aTf{4vv_Zs{JYb!lBQ426!HBEE=_PSlXSHJ>9&Cu?|{ zC^0QroE*(>7t8eCha8SO9gXriORYmlCs7gJ-?c}jRT-u2qD)q(5WOKpuy8NCOgirN za`2oc@>9f=9R7AORXuB(W7ho+y&*oB{fYMMWqrNRh>itfqv4$77Wv=8!fcVM#Xwjl z=h$p>GHcQ?ZKMZ9nJXLay?uj2RujoPHt=7nvt0b&)zXv={1<9LD~CuAivcbFplB(0 z!D`v)g=Xtms4e)6Q{o^LJLZI-*+h@0J(-NrR-PiQn$IsgJQ*lBDz)@A*e=%34stH3NNret*j~>A#fz-A?{hH4n+qN^rayf zI>wn}xyQ*p^FknU-2$<}cT?$?2*}^1$WbUtFpxYE&IFzOmh_D1cLsV&3-FL{C5b+g zs4|R3AET7aq_H@-vl>h=GN$6CfnNSEfIj zj$Ss(=L6fl6NJ=TB2iK{l;Tm5R9+@MD(dy+t_(TMHSWlrLTRxpAp0pc3g6XHH8ddJ;`#` zuP@b|2u(fiH&lI8rh#~){A~y_STtp>ah7KueEOadv`%QCk`| z+sC-Z+0(h3w{R1;aON!-5q_7)QodPHwZK4fBy$7zy;8^4Ehve!S7{pLVJs9`?vF^} zp5GEMa9g(ozUIU&fzL^v*cYur*syHTABL5LaS9sl(2LT1vEp%&Yk3h3shFjBlo(6f zi(Gk`uyok#9_(4w7J0KG&i#H9QNY2htSxz5ES@hSqVjREV!mjZw~2e;Ma=G5kBeB> z1Dxw^gW9rZv!FsPX|AouW|X_Cj@eL?%eH9-ipLobNw6GiEO*^@>@AY;)}}@z@UMF^ zQMb)fx25*(p z#Y&FH_H5pZR+%x0OCA;{fa(Sp%-90a@eDCzmfyRqq_t|Yw)Wfv%_NPw+u@pE-{ZQ~ zzIWDL4lcGM8vg9J?IY}mE&Jc$vU@1LgHc?vOIf_h)P$FX0VUo?x0Gt3l9p>+&=N9y z+CWd3md8Yg*Mo|2l(mbT;8}%qXh$GKnoe}|BGw=&(t*AR;Y8t|dr{EhDed+k!K#?+ zz^~Bt?V>Y*d(K%F=B*JOxgw0c;3M@mZxT3|bga89);0*%h%l{tnU+8gzQ9MGTK*~W zw4!O!B$3}HN|Qz2BSO`o)N+r`W}oV4wkFV9dQ6mcm2<>EbkiKM=`j)0ERaT4#=tQv z7W=B{Ff16DE`5QTkobv$mKZfm$|y_)LA@7dtVsw#%ls{+s=-TPpJ0pvVU1Y4!U|wQ zVWTt2X!O{;ur(05c|^<6K&Z`Rbsb6l#)j8|@5_j#sosdq2AO$k`Gw%vcc|za9;@UL z?PH{EqCMTpX5eHDsxr(3bx2=oAv`gBTpuO|Xb@qAl%2GU78= zDU$XvHha3|*cNQ?5s8b?4Y%2I?Q?DR`Sv+hoBcNXTQZjb~Fl-fWq4NBxwHF?AVtXT0&-DOpqNCr`Q~Yiib{rb&}Cn#N3? zoH4O(O2))7x7FV~X6hKrq`%%ascGUQIFqNmm^Jam+s52IKFgAEV7#0)@!(ie(O6RB zSX6gc#+0n_N0`L9&bcq(=Gy;Rnf_WXx4*W(CMmD~+|;@0bJGjb3v@Ud(=hNeiBEF# z@Ne$hbqDtBfBm&L{QJ^5gHEGjea=#bxyZ1L3U?^(wxvwh-4+?jTgFVh<<2p;WN;HF zP36YiH73h4W~`-t3Su+3j44wl?I-=q;3iL*G&SSqsTp+{6UT9r#&hGwOnr@XLdiCM z(w!6Q?wVw|^R^7`wyZmD8#DFhhOFU}g89c~-Ek{7{!YstMI88U&8WNMo{3Y(+#O7x zIEfp3=lJm%Q|{=hUJ;#o&*ZM+Q2#Ac#@xnDoivHFjG1yvJ!$T^tSM*{ck`s%ZW}YP zj%U>I@dHMTvfTIR?7YIY;;v!OF-+h>?+a@6-}NcHo?(nE6V56)v_PT!BAAByJ$v>! z|Gl5O5QU^OugUVB`UCskJlI;Zt9sWq#P&A5}UcZ=W zwLDVIrMzTWTFt%p5{$vep2}A}5+0)uGOg;~rgD)fBcqE^PKkt!V^XTytOU`+OX#QK6*1lW0 zYqoN4yez1O9r6ScxpT;D;-ah4nhtxgXFlSQ&U(;(=`#XBZ}8oyT_)6J+|D& zt&}R(S2r}spGpTj6$d>Cr&TrMC+Y3*k&@BmRl-qVO7XcWcZtv(`|)CtoPuPW-~10 zFd5cPiQCca!N)ohq4ZkA-CY&@koQ_OwDVLo=H2P)vNX)1UDd7fw`CD6#erT<^$usX z9S0@mHTjg8MDf}`NPTZbO<%w?_ICtHziN|mr1a|4=W zl@57Q5q)cW^wN%KzlVH-Y$Tr}E0yuJ>d36Rbt`lsr7Enl*;BFKWASXo!SL9CP9!RU z5Lt9I0TnqYec7p6Gd8mbYXTma{#XQ@A=3KuYl-r$7N2`@CZ~;5h;@9ImCkwi6E)I#5Bb#mk%#}HM*7&pe@VVCx7Of@ z2nR*@>zj2~5ZO zMZ8BnrF{^I#P>XOPdEZm2#l$Rq2iv1hJZIkX=x0WhV_@*HJ*sO6u5nv9 zH~+Q*@*NNN-d4zBSw&G;SvkYjisRr~jOjS;zCohu5cL`aDzWPeZF=3)gjXZ7V4d5F)etLj9W)m} z9WXomVUOwTVGmguZtq+f`la@FGMMA;u}9zBChHq~3G$a%%rK^y<$tT%hknVg5$CgZ zF|2Jw=qm`NSxwUV>X}Jm%U9Ud5~1fTPgFx+1Bp!mPima0Of%|p4-O4V182{Y#(w6J z0?;;w|D3$zVa+|a)g+y&o_)GH5VrTT>e6098{X)#BwvpvZ>YvZH_aq-9`fZlYbn#1 zc6?p!K>1qC|8-wi7d6UfOUdD}6sYC{9(iT3dYQFmqUef%$7WL2QRK&&Z`t=tz9x%7 z!=6C3`~}JjL}7k^?uq}pI#|}1R#-3oIP}p23&vxtPmy*W^EeHnblfwcO1-MIhY6!U zyVZl(RkKfcavnS8dF&I1sqpQ>lx2%l<^75m<^4=;9;Ki$Ba$D z>y25^$gx6*Y{ZFa56hX|Ck4H%#T$*OlugsY#^Ws^6CWs2$QKH0bR@&Hj#rT#Q(GIW zfApaLwbu{qf9-W={oX@+_Pywv$GiH=f}o#s#ce&3Oftr(MJ=-7p!;1*K&zju=| z|J}UZ^L4cC-*~G2jXeKTSy&9^MOMo!5YesYm_IYmc69GjG|IMHaXQi&#>%qMT_NhD z1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZ zN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=p zpaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq z0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`< z5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)ht zC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPY zfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle z1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<68Pr{L`SI^=HgGu z|Gq=O#4=2hfq@(RwY4xzTid^PlM+bY%l+C4{`oR5m;axPQ~i^2|GQ$*okRJ*+s7a4 zS@bVl1)?$if62dS%`dt43=8iX`aOI0Ij{6nF}+xge6ae1gY^dvUS4Q?M`83y2~Yx*03|>PPy&PPy&*7p7< zEAOT1rS7Ha71B%FE3}ucS6DB7ukcEgoT^l{;F5pj`mJ>sHFjEOa=Olp(H6k_^s{zO%6@44%~6BxVAUdrt; zaH9;|;(=9KF2`WG%HW!wcEZ3n8n{IUZpT1QJ%C#6njKEb^ zf7g;0_;t%kIa0W)GU2-xDKJnNAtwqsE&f377Ofmt>H98V5VBiN1|rh9eiy5>!VFm@ zJYxTy^*^aywCw*~0@68C7AI?kH0Y6uKWE~visv7U=l>AT*Tq`}hWk@bfi29@m+zRF zY=3x1PBI>kI2rfDj%H&6-2Fz^!!uQmeMZ+KGZ}|$+>~Qgq=jU=b%#i#9}*bH$HsEE z)o~O#td94ME-P~U&FFdvIo=~VynBtZ)^W&aSry}zjrK>p`;7L7z3&T1B1NN2p?PrBP(zG?EBAhCOq(2eB8rq{h`#u#Q4T-x zxl*v^{ZEZ+P5GZ1Q$ziR(hwnPsKD@{G2TXy(d)yS%Xc(J1EA16p}Z>6+hh#ryoZco z21H4Q7)S^l2 zrTShI24{vmVSnP{Cq|6I(f!6!t+(0e_{4beW25(DEK!9~cAXBDp6< zGmCZXH3qb`twzh1nA#IY?+3-&d|)-r~xjVVhZ&SWQ3TpQ~>Z1k(DKJmV7Xe)2qUzuVQAl@I~eXN=v&8X8cP&Y;R_bDT03xtFAv4C<`5w3!Stm#QzGGBz|6<~W;owccB0wwnGrE~28_V&IExu5CB&igmV4ubX6PiGJ;pTeXT}R}8NC7H zg*`^Y0`EyS#78(*g&aHCDGnSl_i&>Z+oXqH2{DkyN^Z)cU{tY*0<1kb)?l3p@prOR4m zMD{Xf`4c5C$8&G?#8iPZ8IE+oXrC(`G}`CEmsUDJpFA&knP#`AWY5c)KDYB490kem zjP}Q+?~V5P@F{ub>yyc4n%$m~JwIpq1X1 z+ZU~Zvlx|>yB6t_7Y8p>_9FYDwb_evrZ4KeP|0G ze`R(gk{09-H#_>9hxY6PYdYNQO*A`xWj57bZFbMfA7H-3n#P;+6U{u|+uPrqZ#MJK z_HMVPiRKP#+TZL=H7nLM)l94@a-^9Z@#b=`*_=Pp>@b_XH<%r%X77z=SksYaVoiTz zM&gB0X2s~in*PQNYic$h9c6|!9cd=k^b(P1?@eaM05hPZlA=+jVoj&@A=dO-b9ZZc zt+|^uz19p%cdfa*HNCTs*90=1)-(x4Db}=)8P+t(3~QQXmZtRyTGO6=x?9r`X8uoo zh|L^fCf1Zm~xcF-`M&T%DhVkrYcKq6W@oKa8 zYV*;dB;qPF26+66+F|CPHSKNAVjaEBu%_3UEnoGiz258{W`;FQHWO>A?~5e@4zZ^C z1aGogv8Dse{Hz3U<2NM$JHIi(JJ9S%HhTw|?Iqq6v;7b568`4Cu%=mkJFTfc0oF9x z+@#?wx&+0VX7z=_{=1=zi_i6jkbw0U2RUP3CrMdUM~PHQi=b ztZ5%}u(tL#HCR&mc39IJ%@Z8M%)OI`cwk3rYjR_2bkNf>2J(IYdXs89bk6cgc&H!NYQy@9@%m`ebsMX1AwgKb14Rp!0$OdP>SN+m}fb%=V|@Q}R5mPbQaXc6&

c?ZbzaExv^39bFO(iP+wJfvdF=XRa+zkgr)1l6rWbZz$YbxYrpw!{>GF1Kx?D;! z+l!=I&Gwbje6xMk53T8HVoi$`Yr0yWTpYYi*{kiV*Jc;zOkdr3p^{?7n%1-(p~}qVX3wC`?d2pltj@WW|FvL6?F` zXkWp144e?fo6LN=nYWoa$&4|8GZ~Kb=qPBVG<_6*WB^WZW|A7Pf&La7DZGL3Rq@#1 z1-En99P&NQctgSN4_koXrqTWc-?7m`4UIfDF%zrhY z@{v)2xs8E|je$YV@EyC7*6E{n97tN16J2EZyh$I;r6&pJ_ui6?zc%6U;j?A zzqX4JnN$BD^P*DJ)^0Kv_@|Lwk8QFt*Zt{$sgLJ3;q31$m+8f`md6A;tj>COJc==U^qwi8culv*#p{XlC}@B%~A> zj2#r?T5bRN`$oJF_ow(x*YBI%qeo;!cz7`Byun&FGnc)XZ6Z?&ZqU&)1gb&CS&fnY^uAg3!jR z>Ueb>zpRdbx(;g<&(sMT;xK#x@fFlrOW3-Sr|N`I#3Ja_6fwLM!HV4v>J_SebkRco@3XS=dx)$!@B>HdN830JoNKKbJx-^x#Ze9qLz zAA00>*^hH0esw+f$eh`8XU=*kcjlbOxH;3gpZq=iW8&`AYLBYh~xLQpgdU(%+z6q}`!C zt?d)~VCbJipVuAHO%1zK|BSw8cvQrch~6PHL)M41Mt;^~SdZ2o>yuWH^EYU>`~&CigI?ROi{M09uc z(xs%6bN27cZJx6~c)K-o-0ae&Zq?HLtApvPMcwM??w|7y@wp_1IXtF`)UprN;CZ-7 zsnD%zFOHpeaBqHIqr5k_F@JA7^51}6^2QpVGXDM>#HRc5e`Ep~|jaqXzKL{!caiyW#=SBw`SMDuD4-4>I z*;uHwp0s-9-qnqTNGVWyxO#7KFocw1MMg;@A(GNexD-IT_{YkD%n!=x)ZEK;N*SPb z_q#O+s}{L6 z2t5gRzguS`l%WNenIYZCHg{`WSA%r9!Ks1*(2o3WlLoDMr(0tdLg7$#6ZD$Y%`RBz zRxOwhu0&`a+PlCv+c#HfcbDh_w+_wpb*g%>2|+Udg#VK1)UMw;P|>2T8fA*1_wZ}U zZ2Ms}plD~2c(PWI&Ij-I(Q&hgMk-m8E+;?;e_`ID;GnRJ@-Z-Z z3w>DS-f?T@yHy3?gj-e6ocqo)-!iy`?e~geKqM_xT7J0(G`m@}`NvyGICSqgn@iLo z_mBce30z8wdFSRPw|be+tu0!)+N~;Hz0%DVt+W@CHZEOVXh&#u(Ml{Og-eSImzI=v zHjs=}Cqgs~)wCSs$xxR}HvjQC;_UQOZ=M*Ql|5{gL-QdSa)6(j}Wm40>w3EN3L z!icw%a3P6D81aRjCD=&}QkD^k0+NF8GJin_{UiG>!Jm7C1Tpem`k};hj!(M)$g6S_ zm^&iUt-F~~*}Iv3u(r8x!|&V8%(-tjbN!6n%#c~TnLhL3N8AwKev*8RZ8wuNXEzh$ zJCy5d4-dgiAY)F_lje9CGsMjpuJ+`(V9v3Rl7jX>{`mhVqeq4nsIM@1`*JTocqE<- z9)<{m+BJ0h9uE#3p8xvL9l`X%xWOU#{`(_GRAAn2W;n(@@v)y8J~GArFH?_9u>Z@% zBh%~0M((Gk-493ZvXky#opbxJ)6DgefB&`NbE{!re7l&jx*~>c(>+a`#l(4nICF{f zJK{V_oCo3j|2(ioWXg4{r$t@sDZ1Xdp8n{-Di)>0&0b-3-@nfWi_(34|5w#4SVH&p z{YTov7LfidC96K#k5zvu!od;>NDNZ)iNqq3f^fc{UEC>=PhuAP*(Zra9!X)DVBS0u zvyh}f0x1iXJk$Mb9wPQl_mh{J;GW|LMrHp$7!@Ukys?o*scU5l?#}nMzf+OTM*Dsp zJ4r3slXWcr%iQcg8a1Wc`}f@s)ZIHyx_6~+FCtxflHfp>fP-{8Si&L_gOtm4dJk4< z(q{rAmy|G{z(5%B^E*q(B{4{uM_mX&^zSmE7<^L>dw`K*5t6RGSE-~!Nu8?eou$EjAxMmS{mTv9i!A+u# z>BBG)WY<*UWf-VATtDkWo8Z|~+#X^X5+V}8H005w@3SdcNlY-kLpqpu+FyY=3RD4-oo}!FsK9s37H#r~l{&GL+fT~(lA ziwZ*66$M(nE`(wwWVl2&Y6QzhL8?cAij8E{EXrVem7lG}(o8n@w}xJwzPM-&@1_@Yv?89CPC{xj}p+wVW&`AoO_kMNUw4Q}LS)#xQd zdP8oNhV2Jm0`9&H^Av8CnpuwX&!=%e#W4LirUbt3H;*v6i@V*c;V1X1_Iu^UHfH7b zZA=lKMTlF0|Im5`{!`=Sc#_iXh_@rX5YIw{pGN%CNMD90YGn%AZ#+h)vr$7X{^Z>Q zGUSf@!5x9H{wMA*r2p8hL;8>1p-BI!TMLIaMRUu)!!S(^}N5QPdz+z zL+Hq`r^42TJrh1$|55myh|eQNM2-wQrvFy|ZPf1#UPE^DiRkghIT1HRO^Z4ld2f$( zJx26e)a#jE7uYP_^Sais;rid|P0T1&x?7b!YhLccy!^a{x$|aq_bU;33uiIpYr#Ju z>t<*9@_Y+@vwV#EI{Ba<`<+JlEqRZ;S8kH`$@nqu8|8xyEE2FH~C_SpgL}ct(YJI4a9`oABZ_53hO)5n6;0gVVf#Y7c57J4v6i z)AT9(QXdO>Xgq{EQ^~HA$Ph{7C#k)BsG_TeuH#)doA)G6f>9CPE=l#|h?P`J&VG^L zHj>KaD_5=*uOSr?vxZD)1~FhyB8F^`JKBL=a#yQJf{&f6w2wX(D7pP+Z89L7_`W zJVaKj6^&CoWLOjrLHQQ(`QqL=N_0%~bexy(h4SVzDu&Et6|)zL+~v@YUrC0f?!~@^$X`i@r2gwN!S)B+6KsF5obGK4%3P`5|9<;} z?IdoyrW0jchPx2An_bzZtwBvE$xvZM#LceiN+z0qX=3rEiI5FJaUu3#30;~_PD2zu zCFcf;haAEx9&*IqO>={q-YHM?v^`_H{HjakgPQ&$^5m?sTlqoxRo!bR<^Q`2xYl>j ztt!2&1^fBd^)TO0+s;0WO_lCu@Auh!Ihe_F@FXV6rugS|hVtMebJ?ay%);{#+&OS( z`AA--c?>MkqNVQS0(^xkSo9>Zv05BdjlqJYBFmFEr-JF>Oq`m$}2uT75H6?_9;558n*bMqJ`2v7{%#RPs%e_(>{RyMiGG zP0CyBj#%baEi3dDEb}e(75Ek}g?WB*@sm%WBlGhX7v?RSPZYPK`AS2`>78=s#k%!m zGlE3&!AJSp6Kti@2PMwe*-OPojtZ5t8RZC2IS&lxK&6metQ?6wfdZ9^$mv$F?@E>A zYf>IseL2EE4p^%+$PMOgZTTo&VNcA9Vr;s|wzow?HVP2yYArr~LK-^&U9hD8%1|Bj z|Km>ZXBvvjZcEL~VzOPiXBTsabr-(N?ZS7tT}%>=@kimFbKfp}m)nK!a=Y+d4t~T9 z!EL*L7e35EZq6=znA?SqZ*WKiNhFy`f>}w}#l*FzktazT`)IJB;W@jQ#JSLg!bOEE ze7b(^}lO^pSr?O9(b=H+v4>df z*i3YXtiZu3srbmzcTgD=wN+GEQJNnpdY4jnsg&YVCaW8^on@B(Xj_yoxxuQ)7uukY zeC<_s^-2NE&j5M|Ms7Gp^qNNi{9%AD^%a)*3Vm)(F*c~ht9|6FKfbT~R^v&c+?r?b zg0jY~DRtvbOlI(rrqgL(LCEV1`iHJ5jL8RzV0X#H4*H0F3U*wPBj2i(&(Pgc6|#h@ z@|{`%6ig+btAjozRmrRPlpN$MU9e>G9>p?iFz8Byh{)v>DcC)?27g`PbL)!Nw0Gh^ zq9{r;$!GT;c1KxWyHF@{N}S>&AODpc|9uT<-o>zdjM!^^2jx5hzB6#QMP^52|{ zSk18JxmDd)J-3>yd!;K&|6w}1)hiXQ-?yRBWM1MgWR^C9y%MV@wEw}mtc}6&1m_0X zj{ct}t6NR}SfssG3QL489RW152#qYdLL=Q;((0ey!kL%{$QJHOFJax|S7IGohP@7X zdHctgGn~Qv6-z{e?3X*vVaORwa|5)v1ggxIwyAxJDS?X!xWNm?1s)h0xUe;FS9FW9 zcZ&(i2V-EQ5QK$-TcEhXn5Erfg7U!_Oeh2~r-G4igE4tZj7@PD68B)ea$fWXM!nLwb)kzD@k19Cv&$DE=eO2`EVC002g6LoQI>3>wK5p zBmB(R3fk0{#Rh(h+tg3ukJ5?99pXC(^&@lqipOjHe)dCsR>j~W1cPH|eYhH*K0@#b zBov3zVK{^h$8l;TKBM5jA7P?#aNQGk3=Xm5aF|Wz@PGX1`O0UxG=&nN1SkPYfD)ht zC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPY zfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle z1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZ zN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=p zpaduZN`Mle1SkPYfD)htC;>`<5}*Vq0ZM=ppaduZN`Mle1SkPYfD)htC;>`<5}*Vq d0ZM=ppaduZN`Mle1SkPYfD)htD1j>w_