dos33fsprogs/demos/second/pt3_lib_core.s
Vince Weaver 3a770f9913 second: urgh bug in music player, previous fix was broken
didn't handle the corner case of two carries, and the credits music
was crashing after pattern 13

this is an ugly fix but hopefully correct
2023-11-04 00:24:36 -04:00

2024 lines
45 KiB
ArmAsm

;===========================================
; Library to decode Vortex Tracker PT3 files
; in 6502 assembly for Apple ][ Mockingboard
;
; by Vince Weaver <vince@deater.net>
; Roughly based on the Formats.pas Pascal code from Ay_Emul
; Size Optimization -- Mem+Code (pt3_lib_end-note_a)
; + 3407 bytes -- original working implementation
; + 3302 bytes -- autogenerate the volume tables
; + 3297 bytes -- remove some un-needed bytes from struct
; + 3262 bytes -- combine some duplicated code in $1X/$BX env setting
; + 3253 bytes -- remove unnecessary variable
; + 3203 bytes -- combine common code in note decoder
; + 2937 bytes -- qkumba first pass
; + 2879 bytes -- qkumba second pass
; + 2839 bytes -- mask note command in common code
; + 2832 bytes -- combine $D0 and $E0 decode
; + 2816 bytes -- eliminate "decode_done" variable (2.75k)
; + 2817 bytes -- eliminate pt3_version. Slighly faster but also bigger
; + 2828 bytes -- fix some correctness issues
; + 2776 bytes -- init vars with loop (slower, but more correct and smaller)
; + 2739 bytes -- qkumba's crazy SMC everywhere patch
; + 2418+143 = 2561 bytes -- move NOTE structs to page0
; + 2423+143 = 2566 bytes -- fix vibrato code
; + 2554+143 = 2697 bytes -- generate all four tone tables
; + 2537+143 = 2680 bytes -- inline GetNoteFreq
; TODO
; move some of these flags to be bits rather than bytes?
; enabled could be bit 6 or 7 for fast checking
; NOTE_ENABLED,ENVELOPE_ENABLED,SIMPLE_GLISS,ENV_SLIDING,AMP_SLIDING?
; Header offsets
PT3_VERSION = $0D
PT3_HEADER_FREQUENCY = $63
PT3_SPEED = $64
PT3_LOOP = $66
PT3_PATTERN_LOC_L = $67
PT3_PATTERN_LOC_H = $68
PT3_SAMPLE_LOC_L = $69
PT3_SAMPLE_LOC_H = $6A
PT3_ORNAMENT_LOC_L = $A9
PT3_ORNAMENT_LOC_H = $AA
PT3_PATTERN_TABLE = $C9
; Use memset to set things to 0?
NOTE_VOLUME =0
NOTE_TONE_SLIDING_L =1
NOTE_TONE_SLIDING_H =2
NOTE_ENABLED =3
NOTE_ENVELOPE_ENABLED =4
NOTE_SAMPLE_POINTER_L =5
NOTE_SAMPLE_POINTER_H =6
NOTE_SAMPLE_LOOP =7
NOTE_SAMPLE_LENGTH =8
NOTE_TONE_L =9
NOTE_TONE_H =10
NOTE_AMPLITUDE =11
NOTE_NOTE =12
NOTE_LEN =13
NOTE_LEN_COUNT =14
NOTE_ADDR_L =15
NOTE_ADDR_H =16
NOTE_ORNAMENT_POINTER_L =17
NOTE_ORNAMENT_POINTER_H =18
NOTE_ORNAMENT_LOOP =19
NOTE_ORNAMENT_LENGTH =20
NOTE_ONOFF =21
NOTE_TONE_ACCUMULATOR_L =22
NOTE_TONE_ACCUMULATOR_H =23
NOTE_TONE_SLIDE_COUNT =24
NOTE_ORNAMENT_POSITION =25
NOTE_SAMPLE_POSITION =26
NOTE_ENVELOPE_SLIDING =27
NOTE_NOISE_SLIDING =28
NOTE_AMPLITUDE_SLIDING =29
NOTE_ONOFF_DELAY =30 ;ordering of DELAYs is hard-coded now
NOTE_OFFON_DELAY =31 ;ordering of DELAYs is hard-coded now
NOTE_TONE_SLIDE_STEP_L =32
NOTE_TONE_SLIDE_STEP_H =33
NOTE_TONE_SLIDE_DELAY =34
NOTE_SIMPLE_GLISS =35
NOTE_SLIDE_TO_NOTE =36
NOTE_TONE_DELTA_L =37
NOTE_TONE_DELTA_H =38
NOTE_TONE_SLIDE_TO_STEP =39
NOTE_STRUCT_SIZE=40
.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
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+?
convert_177_smc1:
sec ; 2
; 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
bcc no_scale_a ; 2/3
; 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
no_scale_a:
convert_177_smc2:
sec ; 2
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
bcc no_scale_b ; 2/3
; 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
no_scale_b:
convert_177_smc3:
sec ; 2
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
bcc no_scale_c ; 2/3
; 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
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
sta AY_REGISTERS+6 ; 3
convert_177_smc4:
sec ; 2
bcc no_scale_n ; 2/3
; 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
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
convert_177_smc5:
sec
bcc no_scale_e ; 2/3
; 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
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
; note, you might have slightly better performance if these are aligned
; so that loads don't have to cross page boundaries
NoteTable_high:
.res 96,0
NoteTable_low:
.res 96,0
VolumeTable:
.res 256,0
pt3_lib_end: