dos33fsprogs/pt3_player/pt3_lib.s
Vince Weaver ae3d819970 pt3: init zeroed vars with loop
switching songs, sometimes they'd start wrong due to vars not
being initialized.  Easier to just zero everything than try to
figure out which field we weren't getting.  Also makes code smaller.
2019-06-08 01:39:50 -04:00

2349 lines
52 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)
; 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
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
;====================================
; Global vars that must be preserved
current_subframe: .byte $0
current_line: .byte $0
current_pattern: .byte $0
pt3_pattern_done: .byte $0
pt3_noise_period: .byte $0 ; Y
pt3_noise_add: .byte $0 ; Y
pt3_envelope_period_l: .byte $0 ; Y
pt3_envelope_period_h: .byte $0 ; Y
pt3_envelope_slide_l: .byte $0
pt3_envelope_slide_h: .byte $0
pt3_envelope_slide_add_l:.byte $0
pt3_envelope_slide_add_h:.byte $0
pt3_envelope_add: .byte $0
pt3_envelope_type: .byte $0 ; Y
pt3_envelope_type_old: .byte $0
pt3_envelope_delay: .byte $0
pt3_envelope_delay_orig:.byte $0
pt3_mixer_value: .byte $0
end_vars:
;==========================
; local variables
note_command: ; shared space with sample_b0
sample_b0: .byte $0
note_command_bottom: ; shared space with sample_b1
sample_b1: .byte $0
temp_word_l: .byte $0
temp_word_h: .byte $0
spec_command: .byte $0
freq_l: .byte $00
freq_h: .byte $00
e_slide_amount: .byte $0
prev_note: .byte $0
prev_sliding_l: .byte $0
prev_sliding_h: .byte $0
z80_h: .byte $0
z80_l: .byte $0
z80_d: .byte $0
z80_e: .byte $0
;===========================
; 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_ornament0:
lda #0 ; 2
load_ornament:
sty 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 TEMP ; restore Y value ; 3
rts ; 6
;============
; 83
;===========================
; 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 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 TEMP ; 3
rts ; 6
;============
; 76
;====================================
; pt3_init_song
;====================================
;
; TODO: change to a memset type instruction?
; it will save bytes only if the labels are adjacent
; it will add a lot more cycles, though
pt3_init_song:
lda #$0
sta DONE_SONG ; 3
ldx #(end_vars-begin_vars)
zero_song_structs_loop:
dex
sta note_a,X
bne zero_song_structs_loop
lda #$f ; 2
sta note_a+NOTE_VOLUME ; 4
sta note_b+NOTE_VOLUME ; 4
sta note_c+NOTE_VOLUME ; 4
.if 0
lda #$0 ; 2
sta note_a+NOTE_TONE_SLIDING_L ; 4
sta note_b+NOTE_TONE_SLIDING_L ; 4
sta note_c+NOTE_TONE_SLIDING_L ; 4
sta note_a+NOTE_TONE_SLIDING_H ; 4
sta note_b+NOTE_TONE_SLIDING_H ; 4
sta note_c+NOTE_TONE_SLIDING_H ; 4
sta note_a+NOTE_ENABLED ; 4
sta note_b+NOTE_ENABLED ; 4
sta note_c+NOTE_ENABLED ; 4
sta note_a+NOTE_ENVELOPE_ENABLED ; 4
sta note_b+NOTE_ENVELOPE_ENABLED ; 4
sta note_c+NOTE_ENVELOPE_ENABLED ; 4
sta note_a+NOTE_SAMPLE_POSITION ; 4
sta note_b+NOTE_SAMPLE_POSITION ; 4
sta note_c+NOTE_SAMPLE_POSITION ; 4
sta pt3_noise_period ; 4
sta pt3_noise_add ; 4
sta pt3_envelope_period_l ; 4
sta pt3_envelope_period_h ; 4
sta pt3_envelope_type ; 4
.endif
; default ornament/sample in A
ldx #(NOTE_STRUCT_SIZE*0) ; 2
jsr load_ornament ; 6+93
jsr load_sample1 ; 6+86
; default ornament/sample in B
ldx #(NOTE_STRUCT_SIZE*1) ; 2
jsr load_ornament0 ; 6+93
jsr load_sample1 ; 6+86
; default ornament/sample in C
ldx #(NOTE_STRUCT_SIZE*2) ; 2
jsr load_ornament0 ; 6+93
jsr load_sample1 ; 6+86
;=======================
; load default speed
lda PT3_LOC+PT3_SPEED ; 4
sta pt3_speed_smc+1 ; 4
;=======================
; load loop
lda PT3_LOC+PT3_LOOP ; 4
sta pt3_loop_smc+1 ; 4
;======================
; calculate version
ldx #6 ; 2
lda PT3_LOC+PT3_VERSION ; 4
sec ; 2
sbc #'0' ; 2
cmp #9 ; 2
bcs not_ascii_number ; bge ; 2/3
tax ; 2
not_ascii_number:
; adjust version<6 SMC code in the slide code
; FIXME: I am sure there's a more clever way to do this
lda #$2C ; BIT ; 2
cpx #$6 ; 2
bcc version_less_than_6 ; blt ; 3
; carry is set
adc #$1F ; BIT->JMP 2C->4C ; 2
version_less_than_6:
sta version_smc ; 4
pick_volume_table:
;=======================
; Pick which volume number, based on version
; if (PlParams.PT3.PT3_Version <= 4)
cpx #5 ; 2
; carry clear = 3.3/3.4 table
; carry set = 3.5 table
;==========================
; VolTableCreator
;==========================
; Creates the appropriate volume table
; based on z80 code by Ivan Roshin ZXAYHOBETA/VTII10bG.asm
;
; Called with carry==0 for 3.3/3.4 table
; Called with carry==1 for 3.5 table
; 177f-1932 = 435 bytes, not that much better than 512 of lookup
VolTableCreator:
; Init initial variables
lda #$0
sta z80_d
ldy #$11
; Set up self modify
ldx #$2A ; ROL for self-modify
bcs vol_type_35
vol_type_33:
; For older table, we set initial conditions a bit
; different
dey
tya
ldx #$ea ; NOP for self modify
vol_type_35:
sty z80_l ; l=16 or 17
sta z80_e ; e=16 or 0
stx vol_smc ; set the self-modify code
ldy #16 ; skip first row, all zeros
ldx #16 ; c=16
vol_outer:
clc ; add HL,DE
lda z80_l
adc z80_e
sta z80_e
lda #0
adc z80_d
sta z80_d ; carry is important
; sbc hl,hl
lda #0
adc #$ff
eor #$ff
vol_write:
sta z80_h
pha
vol_inner:
pla
pha
vol_smc:
nop ; nop or ROL depending
lda z80_h
adc #$0 ; a=a+carry;
sta VolumeTable,Y
iny
pla ; add HL,DE
adc z80_e
pha
lda z80_h
adc z80_d
sta z80_h
inx ; inc C
txa ; a=c
and #$f
bne vol_inner
pla
lda z80_e ; a=e
cmp #$77
bne vol_m3
inc z80_e
vol_m3:
txa ; a=c
bne vol_outer
vol_done:
rts
;=====================================
; 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 ; 4
; b1 = pt3->data[a->sample_pointer + a->sample_position * 4 + 1];
iny ; 2
lda (SAMPLE_L),Y ; 5+
sta sample_b1 ; 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
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);
jsr GetNoteFreq
; a->tone = (a->tone + a->tone_sliding + w) & 0xfff;
clc
ldy note_a+NOTE_TONE_SLIDING_L,X
tya
adc note_a+NOTE_TONE_L,X
sta note_a+NOTE_TONE_L,X
lda note_a+NOTE_TONE_H,X
adc note_a+NOTE_TONE_SLIDING_H,X
sta note_a+NOTE_TONE_H,X
clc ;;can be removed if ADC SLIDING_H cannot overflow
lda note_a+NOTE_TONE_L,X
adc freq_l
sta note_a+NOTE_TONE_L,X
lda note_a+NOTE_TONE_H,X
adc freq_h
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
tya
adc note_a+NOTE_TONE_SLIDE_STEP_L,X
sta note_a+NOTE_TONE_SLIDING_L,X
tay
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 ; NUM1-NUM2
cmp note_a+NOTE_TONE_DELTA_L,X ;
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
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 ; NUM1-NUM2
cmp note_a+NOTE_TONE_DELTA_L,X ;
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)
lda sample_b1 ; a->amplitude= (b1 & 0xf);
and #$f
;========================================
; if b0 top bit is set, it means sliding
; adjust amplitude sliding
bit sample_b0 ; 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 #0 ; 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)) {
lda sample_b0
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
asl
asl
asl ; b0 bit 5 to carry flag
lda #$20
bit sample_b1 ; 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 ; 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
lda e_slide_amount
adc pt3_envelope_add
sta pt3_envelope_add
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
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 ; pt3->mixer_value = ((b1 >>1) & 0x48) | pt3->mixer_value;
lsr
and #$48
ora pt3_mixer_value
sta pt3_mixer_value
;========================
; 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_value
handle_onoff:
lda note_a+NOTE_ONOFF,X ;if (a->onoff>0) {
beq done_onoff
dec note_a+NOTE_ONOFF,X ; a->onoff--;
bne done_onoff ; if (a->onoff==0) {
lda note_a+NOTE_ENABLED,X
eor #$1 ; toggle
sta note_a+NOTE_ENABLED,X
.byte $a9 ;mask do_onoff
do_onoff:
dex ; select ONOFF
;lda note_a+NOTE_ONOFF_DELAY,X ; if (a->enabled) a->onoff=a->onoff_delay;
do_offon:
lda note_a+NOTE_OFFON_DELAY,X ; else a->onoff=a->offon_delay;
put_offon:
sta note_a+NOTE_ONOFF,X
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 ; 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 ; 4
lda note_a+NOTE_TONE_SLIDING_H,X ; store prev sliding ; 4+
sta prev_sliding_h ; 4
lda note_a+NOTE_TONE_SLIDING_L,X ; 4+
sta prev_sliding_l ; 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 ; save termporarily ; 4
and #$0f ; 2
sta note_command_bottom ; 4
lda note_command ; 4
; 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
lda note_command_bottom ; 4
; 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 ; 4
bne decode_case_0X_not_zero ; 2/3
;=============
; 12
; 00 case
; means end of pattern
; -1
sta note_a+NOTE_LEN_COUNT,X ; len_count=0; ; 5
dec pt3_pattern_done ; 6
jmp note_done_decoding ; 3
decode_case_0X_not_zero:
jmp done_decode_loop ; 3
decode_case_1X:
;==============================
; $1X -- Set Envelope Type
;==============================
cmp #$10 ; 2
bne decode_case_2X ; 2/3
;============
; 5
; -1
lda note_command_bottom ; 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
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 ; 3
adc #$e0 ; same as subtract $20 ; 2
sta pt3_noise_period ; 3
jmp done_decode_loop ; 3
;===========
; 15
decode_case_4X:
;==============================
; $4X -- set ornament
;==============================
; cmp #$40 ; already set ;
bne decode_case_5X ; 3
; -1
lda note_command_bottom ; 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 ; 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 ; 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 ; 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 ; 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 ; 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:
lda spec_command ; 4
;==============================
; 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);
lda note_a+NOTE_NOTE,X
jsr GetNoteFreq
lda freq_l
sta temp_word_l
lda freq_h
sta temp_word_h
lda prev_note
jsr GetNoteFreq
sec
lda temp_word_l
sbc freq_l
sta note_a+NOTE_TONE_DELTA_L,X
lda temp_word_h
sbc freq_h
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
; a->note=prev_note;
lda prev_note
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
lda prev_sliding_l
sta note_a+NOTE_TONE_SLIDING_L,X
lda prev_sliding_h
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_need
; 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
no_need:
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
sta pt3_envelope_delay_orig
; low value
lda (PATTERN_L),Y ; load byte, set as low
iny
sta pt3_envelope_slide_add_l
; high value
lda (PATTERN_L),Y ; load byte, set as high
iny
sta pt3_envelope_slide_add_h
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 ; 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 ; 4
; get next byte
iny ; 2
lda (PATTERN_L),Y ; 5+
sta pt3_envelope_period_h ; 4
iny ; 2
lda (PATTERN_L),Y ; 5+
sta pt3_envelope_period_l ; 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 ; envelope_delay=0 ; 4
sta pt3_envelope_slide_l ; envelope_slide=0 ; 4
sta pt3_envelope_slide_h ; 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
ldy current_pattern ; 4
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
;============
; 22 if end
not_done:
; set up the three pattern address pointers
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
; 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 ; 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 ; 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 ; increment pattern ; 6
sta current_line ; 4
sta current_subframe ; 4
check_subframe:
lda current_subframe ; 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
lda current_line ; 4
beq check_subframe ; 2/3
pattern_good:
; see if we need a new line
lda current_subframe ; 4
bne line_good ; 2/3
; decode a new line
jsr pt3_decode_line ; 6+?
; check if pattern done early
lda pt3_pattern_done ; 4
beq early_end ; 2/3
line_good:
; Increment everything
inc current_subframe ; subframe++ ; 6
lda current_subframe ; 4
; if we hit pt3_speed, move to next
pt3_speed_smc:
eor #0 ; 2
bne do_frame ; 2/3
next_line:
sta current_subframe ; reset subframe to 0 ; 4
inc current_line ; and increment line ; 6
lda current_line ; 4
eor #64 ; always end at 64. ; 2
bne do_frame ; is this always needed? ; 2/3
next_pattern:
sta current_line ; reset line to 0 ; 4
inc current_pattern ; 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)
lda #0 ; needed ; 2
sta pt3_mixer_value ; 4
sta pt3_envelope_add ; 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
lda pt3_noise_period ; 4
adc pt3_noise_add ; 4
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
lda pt3_mixer_value ; 4
sta AY_REGISTERS+7 ; 3
;=======================
; Amplitudes
lda note_a+NOTE_AMPLITUDE ; 4
sta AY_REGISTERS+8 ; 3
lda note_b+NOTE_AMPLITUDE ; 4
sta AY_REGISTERS+9 ; 3
lda note_c+NOTE_AMPLITUDE ; 4
sta AY_REGISTERS+10 ; 3
;======================================
; Envelope period
; result=period+add+slide (16-bits)
clc ; 2
lda pt3_envelope_period_l ; 4
adc pt3_envelope_add ; 4
tay ; 2
lda pt3_envelope_period_h ; 4
adc #0 ; 2
sta temp_word_h ; 4
clc ; 2
tya ; 2
adc pt3_envelope_slide_l ; 4
sta AY_REGISTERS+11 ; 3
lda temp_word_h ; 4
adc pt3_envelope_slide_h ; 4
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
lda pt3_envelope_type ; 4
cmp pt3_envelope_type_old ; 4
sta pt3_envelope_type_old ; 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
;==============================
lda pt3_envelope_delay ; 4
beq done_do_frame ; assume can't be negative? ; 2/3
; do this if envelope_delay>0
dec pt3_envelope_delay ; 6
bne done_do_frame ; 2/3
; only do if we hit 0
lda pt3_envelope_delay_orig ; reset envelope delay ; 4
sta pt3_envelope_delay ; 4
clc ; 16-bit add ; 2
lda pt3_envelope_slide_l ; 4
adc pt3_envelope_slide_add_l ; 4
sta pt3_envelope_slide_l ; 4
lda pt3_envelope_slide_h ; 4
adc pt3_envelope_slide_add_h ; 4
sta pt3_envelope_slide_h ; 4
done_do_frame:
rts ; 6
;======================================
; GetNoteFreq
;======================================
; Return frequency from lookup table
; Which note is in A
; return in freq_l/freq_h
; FIXME: self modify code
GetNoteFreq:
sty TEMP ; 3
tay ; 2
lda PT3_LOC+PT3_HEADER_FREQUENCY ; 4
cmp #1 ; 2
bne freq_table_2 ; 2/3
lda PT3NoteTable_ST_high,Y ; 4+
sta freq_h ; 4
lda PT3NoteTable_ST_low,Y ; 4+
sta freq_l ; 4
ldy TEMP ; 3
rts ; 6
;===========
; 40
freq_table_2:
lda PT3NoteTable_ASM_34_35_high,Y ; 4+
sta freq_h ; 4
lda PT3NoteTable_ASM_34_35_low,Y ; 4+
sta freq_l ; 4
ldy TEMP ; 3
rts ; 6
;===========
; 41
; Table #1 of Pro Tracker 3.3x - 3.5x
PT3NoteTable_ST_high:
.byte $0E,$0E,$0D,$0C,$0B,$0B,$0A,$09
.byte $09,$08,$08,$07,$07,$07,$06,$06
.byte $05,$05,$05,$04,$04,$04,$04,$03
.byte $03,$03,$03,$03,$02,$02,$02,$02
.byte $02,$02,$02,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$01,$01,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
PT3NoteTable_ST_low:
.byte $F8,$10,$60,$80,$D8,$28,$88,$F0
.byte $60,$E0,$58,$E0,$7C,$08,$B0,$40
.byte $EC,$94,$44,$F8,$B0,$70,$2C,$FD
.byte $BE,$84,$58,$20,$F6,$CA,$A2,$7C
.byte $58,$38,$16,$F8,$DF,$C2,$AC,$90
.byte $7B,$65,$51,$3E,$2C,$1C,$0A,$FC
.byte $EF,$E1,$D6,$C8,$BD,$B2,$A8,$9F
.byte $96,$8E,$85,$7E,$77,$70,$6B,$64
.byte $5E,$59,$54,$4F,$4B,$47,$42,$3F
.byte $3B,$38,$35,$32,$2F,$2C,$2A,$27
.byte $25,$23,$21,$1F,$1D,$1C,$1A,$19
.byte $17,$16,$15,$13,$12,$11,$10,$0F
; Table #2 of Pro Tracker 3.4x - 3.5x
PT3NoteTable_ASM_34_35_high:
.byte $0D,$0C,$0B,$0A,$0A,$09,$09,$08
.byte $08,$07,$07,$06,$06,$06,$05,$05
.byte $05,$04,$04,$04,$04,$03,$03,$03
.byte $03,$03,$02,$02,$02,$02,$02,$02
.byte $02,$01,$01,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
PT3NoteTable_ASM_34_35_low:
.byte $10,$55,$A4,$FC,$5F,$CA,$3D,$B8
.byte $3B,$C5,$55,$EC,$88,$2A,$D2,$7E
.byte $2F,$E5,$9E,$5C,$1D,$E2,$AB,$76
.byte $44,$15,$E9,$BF,$98,$72,$4F,$2E
.byte $0F,$F1,$D5,$BB,$A2,$8B,$74,$60
.byte $4C,$39,$28,$17,$07,$F9,$EB,$DD
.byte $D1,$C5,$BA,$B0,$A6,$9D,$94,$8C
.byte $84,$7C,$75,$6F,$69,$63,$5D,$58
.byte $53,$4E,$4A,$46,$42,$3E,$3B,$37
.byte $34,$31,$2F,$2C,$29,$27,$25,$23
.byte $21,$1F,$1D,$1C,$1A,$19,$17,$16
.byte $15,$14,$12,$11,$10,$0F,$0E,$0D
;PT3VolumeTable_33_34:
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1
;.byte $0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$2,$2,$2,$2,$2
;.byte $0,$0,$0,$0,$1,$1,$1,$1,$2,$2,$2,$2,$3,$3,$3,$3
;.byte $0,$0,$0,$0,$1,$1,$1,$2,$2,$2,$3,$3,$3,$4,$4,$4
;.byte $0,$0,$0,$1,$1,$1,$2,$2,$3,$3,$3,$4,$4,$4,$5,$5
;.byte $0,$0,$0,$1,$1,$2,$2,$3,$3,$3,$4,$4,$5,$5,$6,$6
;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7
;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$5,$5,$6,$6,$7,$7,$8
;.byte $0,$0,$1,$1,$2,$3,$3,$4,$5,$5,$6,$6,$7,$8,$8,$9
;.byte $0,$0,$1,$2,$2,$3,$4,$4,$5,$6,$6,$7,$8,$8,$9,$A
;.byte $0,$0,$1,$2,$3,$3,$4,$5,$6,$6,$7,$8,$9,$9,$A,$B
;.byte $0,$0,$1,$2,$3,$4,$4,$5,$6,$7,$8,$8,$9,$A,$B,$C
;.byte $0,$0,$1,$2,$3,$4,$5,$6,$7,$7,$8,$9,$A,$B,$C,$D
;.byte $0,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E
;.byte $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E,$F
;PT3VolumeTable_35:
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
;.byte $0,$0,$0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1
;.byte $0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$2,$2,$2,$2
;.byte $0,$0,$0,$1,$1,$1,$1,$1,$2,$2,$2,$2,$2,$3,$3,$3
;.byte $0,$0,$1,$1,$1,$1,$2,$2,$2,$2,$3,$3,$3,$3,$4,$4
;.byte $0,$0,$1,$1,$1,$2,$2,$2,$3,$3,$3,$4,$4,$4,$5,$5
;.byte $0,$0,$1,$1,$2,$2,$2,$3,$3,$4,$4,$4,$5,$5,$6,$6
;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7
;.byte $0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7,$8
;.byte $0,$1,$1,$2,$2,$3,$4,$4,$5,$5,$6,$7,$7,$8,$8,$9
;.byte $0,$1,$1,$2,$3,$3,$4,$5,$5,$6,$7,$7,$8,$9,$9,$A
;.byte $0,$1,$1,$2,$3,$4,$4,$5,$6,$7,$7,$8,$9,$A,$A,$B
;.byte $0,$1,$2,$2,$3,$4,$5,$6,$6,$7,$8,$9,$A,$A,$B,$C
;.byte $0,$1,$2,$3,$3,$4,$5,$6,$7,$8,$9,$A,$A,$B,$C,$D
;.byte $0,$1,$2,$3,$4,$5,$6,$7,$7,$8,$9,$A,$B,$C,$D,$E
;.byte $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E,$F
VolumeTable:
.res 256,0
pt3_lib_end: