-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspc_program.asm
More file actions
3493 lines (3336 loc) · 171 KB
/
spc_program.asm
File metadata and controls
3493 lines (3336 loc) · 171 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; FF6 SPC ASM by m06, 2019
arch spc700
; -raw
base $000200
; Engine start
EngineStart:
CLRP ; Clear direct page flag
DI ; Clear interrupt flag
MOV X,#$FF ; Set X to #$FF
MOV SP,X ; Set stack pointer to $FF
MOV A,#$00 ; Zero A
MOV X,A ; Zero X
L0208: MOV (X+),A ; Zero $00+X; increase X
CMP X,#$F0 ; Pointing to $F0?
BNE L0208 ; Loop if not
DECW $C6 ; Paused and Current Song = #$FF
MOV A,#$00 ; Zero A...
MOV Y,#$0C ; Point to MVL
CALL SetDSPReg ; Master volume left: 0
MOV Y,#$1C ; Point to MVR
CALL SetDSPReg ; Master volume right: 0
MOV Y,#$2C ; Point to EVOLL
CALL SetDSPReg ; Echo volume left: 0
MOV Y,#$3C ; Point to EVOLR
CALL SetDSPReg ; Echo volume right: 0
MOV Y,#$2D ; Point to pitch mod
CALL SetDSPReg ; Pitch modulation: all voices off
MOV Y,#$3D ; Point to noise
CALL SetDSPReg ; Noise: all voices off
MOV Y,#$4D ; Point to echo
CALL SetDSPReg ; Echo: all voices off
MOV Y,#$5D ; Point to DIR; sample pointer table
MOV A,#$1B ; Address parameter
CALL SetDSPReg ; Source directory: $1B00
MOV Y,#$07 ; Point to voice0 GAIN
MOV X,#$A0 ; Parameter
L023F: MOV $F2,Y ; Set DSP register
MOV $F3,X ; Store parameter
MOV A,Y ; Transfer Y to A
CLRC ; Clear Carry for addition
ADC A,#$10 ; Point to next voice
MOV Y,A ; Transfer A to Y
BPL L023F ; Loop unless 8 voices done
MOV $F1,#$30 ; Clear all 4 ports, stop all 3 timers
MOV $FA,#$27 ; Timer0 target: 39) (4.875ms
MOV $FB,#$80 ; Timer1 target: 128) (16ms
MOV $FC,#$05 ; Timer2 target: 5
MOV $F1,#$07 ; Enable all 3 timers
MOV $8C,#$05 ; Echo Delay = #$05
CALL InitEcho ; Initialize Echo
MOV A,#$3F ; Parameter: 127
MOV Y,#$0C ; Point to MVL
CALL SetDSPReg ; Master volume left: 127
MOV Y,#$1C ; Point to MVR
CALL SetDSPReg ; Master volume right: 127
DEC $24 ; Mute all voices...?
MOV $C8,#$07 ; Master Evelope Frame Counter = 7
DEC $A8 ; Decrement SFX Volume Envelope Counter
MainLoop:
L0272: CALL ProcessPorts ; Process Ports
MOV Y,$FD ; Counter-0, 4.875 milliseconds
BEQ L0272 ; Loop if zero
BBS2 $85,L027F ; If ?
BBC6 $86,L0283 ; If disable echo? skip 2 instructions
L027F: MOV Y,#$05 ; Start from $1999
BRA L0285 ; Skip next instruction
L0283: MOV Y,#$11 ; Start from $19A5
L0285: MOV A,$1994+Y ; Register table
MOV $F2,A ; Set DSP register
MOV A,$19A5+Y ; Register Data table
MOV X,A ; Transfer A to X
MOV A,(X) ; Read from DP
MOV $F3,A ; Store to register
DBNZ Y,L0285 ; Decrease Y, loop unless zero
MOV $24,Y ; Clear key-off for all voices...?
MOV $22,Y ; Clear key-on for all voices...?
BBS7 $86,L02AE ; If waveform output mode
BBS3 $85,L029F+1 ; If master envelope output mode
MOV Y,$52 ; Y = Enabled Voices Bitmask
L029F: MOV A,$ADEB ; ? pointless, use up cycles?
MOV A,$DD ; A = Enabled Conditional Jump Bitmask
MOVW $F6,YA ; Store to Port $2142
MOV Y,$7B ; Y = Output Code
MOV A,#$00 ; Zero A
MOVW $F4,YA ; Store to Port $2140
BRA L02B1 ; Skip next instruction
L02AE: CALL WaveformOutputMode ; Waveform Output Mode
L02B1: MOVW YA,$D9 ; Paused Voices, Song and Game SFX
BNE L0300 ; Skip
DEC $C8 ; Decrement Master Envelope Frame Counter
BNE L02BF ; Skip 2 instructions if counting
MOV $C8,#$07 ; Master Envelope Frame Counter = 7
CALL ProcessMasterEnv ; Process Master Envelopes
L02BF: CALL ProcessSongScript ; Process Song Script
MOV X,#$00 ; Zero X
MOV $8F,#$01 ; Current Voice = 1
MOV A,$83 ; A = Game Sound Effect Voices
OR A,$84 ; OR System Sound Effect Voices
EOR A,#$FF ; Invert SFX voices
AND A,$52 ; Filter with Enabled Voices
AND A,$23 ; Filter with Key-On Voices for paused song
MOV $A0,A ; Set Active Voices
BRA L02DE ; Skip 5 instructions
L02D5: MOV $A3,X ; Set Voice Pointer
CALL ProcessVibTrem ; Process Vibrato and Tremolo
L02DA: INC X ; Increment Voice Pointer
INC X ; Increment Voice Pointer
ASL $8F ; Next Voice
L02DE: LSR $A0 ; Shift Active Voices
BCS L02D5 ; Loop if Next Voice is active
BNE L02DA ; Loop unless All Voices Done
MOV X,#$1E ; X = Voice Pointer for Last Voice
MOV $8F,#$80 ; Current Voice = bit 7, Last Voice
MOV A,$83 ; A = Game Sound Effect Voices
OR A,$84 ; with System Sound Effect Voices
MOV $A0,A ; Set Active voices
BRA L02FA ; Skip 5 instructions
L02F1: MOV $A3,X ; Set Voice Pointer
CALL ProcessVibTrem ; Process Vibrato and Tremolo
L02F6: DEC X ; Increment Voice Pointer
DEC X ; Increment Voice Pointer
LSR $8F ; Next Voice
L02FA: ASL $A0 ; Shift Active Voices
BCS L02F1 ; Loop if Next Voice is active
BNE L02F6 ; Loop unless All Voices Done
L0300: MOV A,#$00 ; Zero A, Volume Update
MOV Y,A ; Zero Y, Pitch Update
MOVW $DB,YA ; Disable Volume and Pitch Update in DSP
JMP L0272 ; Loop
; Process Song Script
ProcessSongScript:
BBS5 $86,L032A ; If fast forward mode skip 18 instructions
MOV A,$46 ; A = Current Tempo high
MOV Y,$B7 ; Y = Tempo Ratio
BEQ L0323 ; If zero skip 11 instructions
MUL YA ; Current Tempo * Tempo Ratio
MOV A,Y ; Transfer Y to A
BBS7 $B7,L0320 ; If tempo ratio signed skip 6 instructions
ASL A ; Double calculated tempo
CLRC ; Clear Carry for addition
ADC A,$46 ; Add Current Tempo low
BCC L0323 ; If carry clear skip 4 instructions
MOV A,#$FF ; A = #$FF, tempo boundary
BRA L0323 ; Skip 2 instructions
L0320: BNE L0323 ; Skip 1 instruction
INC A ; Increment A
L0323: CLRC ; Clear Carry for addition
ADC A,$47 ; Add Tempo Counter
MOV $47,A ; Store Tempo Counter
BCC L037D ;
L032A: MOV X,#$00 ; Zero X
MOV $8F,#$01 ; Current Voices bitmask = #$01
MOV ($A0),($52) ; Set Active Voices with Enabled Voices
BRA L036D ; Skip 27 instructions
L0334: MOV $A3,X ; Set Voice Pointer
DEC $25+X ; Decrement Note Duration Counter
BNE L033F ; If note still counting skip 2 instructions
CALL HandleSongScript ; Handle Song Script Code
BRA L0366 ; Skip 18 instructions
L033F: MOV Y,#$00 ; Zero Y
CMP X,#$10 ; X > #$10
BCS L0345+1 ; If SFX skip next 1/2 instruction
L0345: MOV A,#$FC ; .. $FC INC Y
MOV A,#$02 ; A = #$02
CBNE $25+X,L0366 ; If loop count != 2 skip 12 instructions
MOV A,$0059+Y ; A = Disable Key-Off at End of Note
AND A,$8F ; Isolate Current Voice
BNE L0366 ; If active skip 9 instructions
MOV A,Y ; Transfer Y to A
BNE L0363 ; Skip if SFX voice
MOV A,$8F ; A = Current Voice
TCLR $0023,A ; Clear Key-On for Paused Song
MOV A,$83 ; A = Game Sound Effect Voices
OR A,$84 ; with System Sound Effect Voices
AND A,$8F ; Select Current Voice
BNE L0366 ; Skip next instruction if active
L0363: OR ($24),($8F) ; $24 = muted voices
L0366: CALL ProcessVolPanEnv ; Process Volume, Pan and Pansweep Envelopes
L0369: INC X ; Increment Voice Pointer
INC X ; Increment Voice Pointer
ASL $8F ; bitmask for current voice <<
L036D: LSR $A0 ; Active Voices >>
BCS L0334 ; Loop while carried voice is active
BNE L0369 ; Loop while active voices are left
CMP X,#$18 ;
BCS L0394 ; Return if?
CALL ProcessTempoEchoEnv ; Process Tempo, Echo and Filter Envelopes
CALL ProcessPorts ; Process Ports
L037D: MOV A,#$78 ; A = #$78
CLRC ; Clear Carry for addition
ADC A,$48 ; Add SFX Tempo Counter
MOV $48,A ; Store SFX Tempo Counter
BCC L0394 ; Return if
MOV X,#$18
MOV $8F,#$10
MOV A,$83 ; A = Game Sound Effect Voices
OR A,$84 ; with System Sound Effect Voices
XCN A ; Swap high 4 and low 4 bits
MOV $A0,A ; Set Active Voices
BRA L036D ; Loop
L0394: RET ; Return
; Process Tempo, Echo and Filter Envelopes
ProcessTempoEchoEnv:
MOV A,$49 ; A = Tempo Envelope Counter
BEQ L03A1 ; Skip if zero
DEC $49 ; Decrement Tempo Envelope Counter
MOVW YA,$4A ; A = Tempo Envelope Change Rate
ADDW YA,$45 ; Add Current Tempo
MOVW $45,YA ; Store Current Tempo
L03A1: MOV A,$50 ; A = Echo Volume Envelope Counter
BEQ L03AD ; Skip if zero
DEC $50 ; Decrement Echo Volume Envelope Counter
MOVW YA,$4E ; Echo Volume Envelope Change Rate
ADDW YA,$4C ; Add Echo Volume
MOVW $4C,YA ; Set Echo Volume
L03AD: MOV A,$78 ; A = Echo Feedback Envelope Counter
BEQ L03B9 ; Skip if zero
DEC $78 ; Decrement Echo Feedback Envelope Counter
MOVW YA,$79 ; A = Echo Feedback Envelope Change Rate
ADDW YA,$75 ; Add Echo Feedback
MOVW $75,YA ; Store Echo Feedback
L03B9: MOV A,$77 ; A = Filter Envelope Counter
BEQ L03D8 ; Return if zero
DEC $77 ; Decrement Filter Envelope Counter
MOV X,#$10 ; X = #$10
L03C1: MOV A,$00FF+X ; A = Filter Envelope Change Rate high
MOV Y,A ; Transfer A to Y
MOV A,$00FE+X ; A = Filter Envelope Change Rate low
MOVW $98,YA ; $98 = Filter Envelope Change Rate
MOV A,$63+X ; A = Filter Data low
MOV Y,$64+X ; Y = Filter Data high
ADDW YA,$98 ; Add Change Rate to Data
MOV $63+X,A ; Set FIlter Data low
MOV $64+X,Y ; Set Filter Data high
DEC X ; Decrement X
DEC X ; Decrement X, next voice
BNE L03C1 ; Loop for all voices
L03D8: RET ; Return
; Handle Song Script Code
HandleSongScript:
CALL NextOpCode ; Next Script Op Code
CMP A,#$C4 ; Is Track Command
BCC L03E5 ; If not skip to Notes
CALL HandleTrkCmds ; Handle Track Commands
BRA HandleSongScript ; Loop
L03E5: MOV Y,$25+X ; Y = Ticks Left for Current Voice
BNE L03F5 ; If Ticks Left, skip to handle Note
MOV Y,#$00 ; Zero Y
MOV X,#$0E ; X = 13
DIV YA,X ; A = Note, Y = Duration Pointer
MOV X,$A3 ; X = Voice Pointer
MOV A,$17D1+Y ; A = Duration in Ticks
MOV $25+X,A ; Set as Ticks Left for Current Voice
L03F5: CMP $A2,#$A8 ; Is a note value
BCC HandleNote ; Handle Note
CMP $A2,#$B6 ; Is a Tie
BCS L0402 ; Return if not
JMP HandleTie ; Handle Tie
L0402: RET ; Return
; Handle Note Value
HandleNote:
MOV A,$A2 ; Current Op Code, Note and Duration
MOV Y,#$00 ; Zero Y
MOV X,#$0E ; X = 13
DIV YA,X ; A / 13, A = Note, Y = Duration Pointer
MOV $A2,A ; Set Current Note to $A2
MOV X,$A3 ; X = Voice Pointer
MOV A,$F600+X ; A = Octave
MOV Y,#$0C ; Y = 12
MUL YA ; Octave * 12
CLRC ; Clear Carry for addition
ADC A,$A2 ; Add Current Note
CLRC ; Clear Carry for addition
ADC A,$F721+X ; Add Transposition
SETC ; Set Carry for subtraction
SBC A,#$0A ; Subtract 10
MOV $F761+X,A ; Set as Absolute Pitch
CALL PitchCalculation ; Note Pitch Calculation, VxPITCH
MOV A,$C0 ; A = Calculated VxPITCH low
MOV $F8E0+X,A ; Set Calculated VxPITCH to $F8E0+X
MOV A,$C1 ; A = Calculated VxPITCH high
MOV $F8E1+X,A ; Set Calculated VxPITCH to $F8E1+X
MOV A,$0151+X ; A = Vibrato Amplitude
BEQ L0438 ; Skip if no Vibrato
MOV $A2,A ; $A2 = Vibrato Amplitude
CALL ResetVibrato ; Reset Vibrato
L0438: MOV A,$0170+X ; A = Tremolo
BEQ L0444 ; Skip if no Tremolo
MOV $A2,A ; $A2 = Tremolo
CALL ResetTremolo ; Reset Tremolo
MOV A,#$00 ; Zero A
L0444: MOV $F8C0+X,A ; Zero Vibrato Value low
MOV $F8C1+X,A ; Zero Vibrato Value high
MOV $F860+X,A ; Zero Tremolo Value low
MOV $F861+X,A ; Zero Tremolo Value high
MOV $F780+X,A ; Zero Pitch Envelope Change Rate low
MOV $F781+X,A ; Zero Pitch Envelope Change Rate high
CMP X,#$10 ; X > #$10
BCS L047A ; If SFX skip 15 instructions
OR ($23),($8F) ; Set Key-On, paused song for Current Voice
MOV A,$83 ; A = Game SFX Voices
OR A,$84 ; with System SFX Voices
AND A,$8F ; Isolate Current Voice
BNE L0499 ; If SFX Voice
MOV A,$8F ; A = Current Voice
AND A,$5F ; And Drum Roll Enabled
BNE L048D ; If so skip ? instructions
MOV A,$8F ; A = Current Voice
AND A,$5B ; And Slur Enabled
BEQ L048D ; If not skip ? instructions
AND A,$5D ; And Slur Active
BNE L0490 ; If so skip 12 instructions
OR ($5D),($8F) ; Set Active Slur for Current Voice
BRA L048D ; Skip 9 instructions
L047A: MOV A,$8F ; A = Current Voice
AND A,$60 ; And SFX Drum Roll Enabled
BNE L048D ; If so skip 6 instructions
MOV A,$8F ; A = Current Voice
AND A,$5C ; And SFX Slur Enabled
BEQ L048D ; If not skip 3 instructions
AND A,$5E ; And SFX Slur Active
BNE L0490 ; If so skip 2 instructions
OR ($5E),($8F) ; Set Active Slur SFX for Current Voice
L048D: OR ($22),($8F) ; Set Key-On for Current Voice
L0490: OR ($DB),($8F) ; Set Enable Volume Update in DSP for Current Voice
OR ($DC),($8F) ; Set Enable Pitch Update in DSP for Current Voice
CALL InstrumentUpdateDSP ; Update DSP SRCN for Voice with Instrument
; Continue Note Processing, Tie starts from here.
HandleTie:
L0499: CALL HandleTrkCmdsSwitch ; Handle some Track Commands
MOV Y,A ; Transfer A to Y
MOV A,$8F ; A = Current Voice
CMP X,#$10 ; X > #$10
BCS L04C4 ; If SFX skip 14 instructions
CMP Y,#$B6 ; Y > #$B6
BCS L04B0 ; If so skip 4 instructions
CMP Y,#$A8 ; Y < #$A8
BCC L04B6 ; If so skip 4 instructions
TSET $0059,A ; Set Disable Key-Off at End of Note
BRA L04E3 ; Skip 21 instructions
L04B0: TCLR $005B,A ; Disable Slur
TCLR $005F,A ; Disable Drum Roll
L04B6: TCLR $0059,A ; Clear Disable Key-Off at End of Note
MOV A,$5B ; A = Enable Slur
OR A,$5F ; with Enable Drum Roll
AND A,$8F ; Isolate Current Voice
TSET $0059,A ; Set Disable Key-Off at End of Note
BRA L04E3 ; Skip 13 instructions
L04C4: CMP Y,#$B6 ; Y > #$B6
BCS L04D1 ; If so skip 4 instructions
CMP Y,#$A8 ; Y < #$A8
BCC L04D7 ; If so skip 4 instructions
TSET $005A,A ; Set Disable Key-Off at End of Note SFX
BRA L04E3 ; Skip 7 instructions
L04D1: TCLR $005C,A ; Disable Slur SFX
TCLR $0060,A ; Disable Drum Roll SFX
L04D7: TCLR $005A,A ; Clear Disable Key Off at End of Note SFX
MOV A,$5C ; A = Enable Slur SFX
OR A,$60 ; with Enable Drum Roll SFX
AND A,$8F ; Isolate Current Voice
TSET $005A,A ; Set Disable Key-Off at End of Note SFX
L04E3: MOV A,$0150+X ; A = Pitch Envelope Target
BEQ L0553 ; If none skip instructions
CLRC ; Clear Carry for addition
ADC A,$F761+X ; Add Absolute Pitch
MOV $F761+X,A ; Set Absolute Pitch
CALL PitchCalculation ; Note Pitch Calculation, VxPITCH
MOV A,$F8E1+X ; A = Last Calculated Frequency Value high
MOV Y,A ; Transfer A to Y
MOV A,$F8E0+X ; A = Last Calculated Frequency Value low
MOVW $98,YA ; $98 = Last Calculated Frequency Value
MOVW YA,$C0 ; YA = Calculated Frequency Value
SETC ; Set Carry for subtraction
SUBW YA,$98 ; Subtract Last Calculated Frequency Value
MOVW $98,YA ; $98 = Calculated Frequency Value Diff
PUSH P ; Save Flags
BCS L050D ; If lower frequency skip 3 instructions
EOR $98,#$FF ; Invert Calculated Frequency Value Diff low
EOR $99,#$FF ; Invert Calculated Frequency Value Diff high
INCW $98 ; Increment Calculated Frequency Value Diff
L050D: MOV A,$F720+X ; A = Pitch Envelope Duration
BNE L0517 ; If envelope skip 2 instructions
MOV $9A,#$00 ; $9A = 0
BRA L0529 ; Skip 11 instructions
L0517: MOV X,A ; Transfer A to X
MOV A,$99 ; A = Calculated Frequency Value Diff high
MOV Y,#$00 ; Zero Y
DIV YA,X ; YA / X = Frequency Change Rate high
MOV $9A,A ; $9A = Frequency Change Rate high
MOV A,$98 ; A = Calculated Frequency Value Diff low
DIV YA,X ; YA / X = Frequency Change Rate low
MOV $99,A ; $99 = Frequency Change Rate low
MOV A,#$00 ; Zero A
DIV YA,X ; YA / X = Frequency Change Rate remainder
MOV $98,A ; $98 = Frequency Change Rate remainder
L0529: POP P ; Restore Flags
BCS L053B ; If frequency diff negative skip 6 instructions
EOR $98,#$FF ; Invert Frequency Change Rate remainder
EOR $99,#$FF ; Invert Frequency Change Rate low
EOR $9A,#$FF ; Invert Frequency Change Rate high
INCW $98 ; Increment $98
BNE L053B ; If value skip next instruction
INC $9A ; Increment Frequency Change Rate high
L053B: MOV X,$A3 ; X = Voice Pointer
MOVW YA,$99 ; YA = Frequency Change Rate
MOV $F780+X,A ; Set Pitch Envelope Change Rate low
MOV A,Y ; Transfer Y to A
MOV $F781+X,A ; Set Pitch Envelope Change Rate high
MOV A,$98 ; A = Frequency Change Rate remainder
MOV $F8A0+X,A ; Set Pitch Envelope Change Rate Fraction
MOV A,#$00 ; Zero A
MOV $0150+X,A ; Zero Pitch Envelope Target
MOV $F8A1+X,A ; Zero Pitch Envelope Change Rate Fraction high
L0553: RET ; Return
; Note Pitch Calculation:
; Converts the tracker note value to a value that VxPITCHL/H can use
;
; Looking at Chrono Trigger disassmebly FF6 seems to have a bug, missing instruction,
; this results in the fine tune (low byte of instrument pitch) being slightly miscalculated.
;
; A rough overview of the routine:
; Input: A (note value
; 1. A gets divided by 0x0C (12 decimal). The remainder is the note (Y), and the quotient is the octave (A
; 2. (Voice Pitch Multiplier + Detune) * Note Pitch Multiplier = VxPITCHL/H
; 3. VxPITCHL/H gets shifted by octave.
; Additional pitch manipulation (Vibrato and Master Pitch) happens in the DSP Pitch routine.
PitchCalculation:
MOV X,#$0C ; Divisor: 12
MOV Y,#$00 ; Zero Y
DIV YA,X ; Y = Note, A = Octave
MOV X,$A3 ; Transfer Voice Pointer to X
MOV $A1,A ; Transfer Octave to $A1
MOV A,Y ; Transfer Note to A
ASL A ; Double A
MOV Y,A ; Transfer A to Y; Note*2
MOV A,TuningTable+Y ; Transfer Note Pitch Multiplier Low to A
MOV $C2,A ; Store in $C2
MOV A,TuningTable+1+Y ; Transfer Note Pitch Multiplier High to A
MOV $C3,A ; Store in $C3
MOV Y,A ; Transfer A to Y
MOV A,$F740+X ; Transfer Voice Pitch Multiplier Low to A
CLRC ; Clear Carry for addition
ADC A,$F760+X ; Add Detune to A
PUSH P ; Push Flags
PUSH A ; Push Voice Pitch Multiplier Low and Detune
MUL YA ; Note Pitch Multiplier High * Voice Pitch Multiplier Low and Detune
MOVW $C0,YA ; Transfer Calculated Frequency Value to $C0
MOV Y,$C2 ; Transfer Note Pitch Multiplier Low to Y
POP A ; Pull Voice Pitch Multiplier Low and Detune
MUL YA ; Note Pitch Multipler Low * Voice Pitch Multiplier Low and Detune
MOV A,Y ; Transfer Calculated Frequency Value High to A
MOV Y,#$00 ; Zero Y
ADDW YA,$C0 ; Add Calculated Frequency Values
MOVW $C0,YA ; Transfer Calculated Frequency Value to $C0
MOV A,$F741+X ; Transfer Voice Pitch Multiplier High to A, overwrites Calculated Frequency Value Low
BEQ L058F ; Skip if #$00
MUL YA ; Calculated Frequency Value High * Voice Pitch Multiplier High
MOV A,Y ; Transfer Y to A, Calculated Frequency Value High to A
MOV Y,#$00 ; Zero Y
ADDW YA,$C0 ; Add Calculated Frequency Values
BRA L0591 ; Skip next instruction
L058F: MOV A,$C0 ; Transfer Calculated Frequency Value to A
L0591: POP P ; Pull Flags
BMI L0596 ; Branch if Voice Pitch Multiplier Low is signed negative
ADDW YA,$C2 ; Add Note Pitch Multiplier H&L to Calculated Frequency Value
L0596: MOVW $C0,YA ; Store Calculated Frequency Value to $C0
MOV A,#$04 ; Transfer #$04 to A, default octave value
MOV Y,$A1 ; Transfer Octave to Y
BMI L05AC ; Branch if Octave is negative
CMP A,$A1 ; Compare #$04 to Octave
BCS L05B1 ; Branch if Carry set?
L05A2: ASL $C0 ; << $C0, increment octave
ROL $C1 ; << $C1, increment octave with carry from $C0
INC A ; Increment A
CBNE $A1,L05A2 ; A != Target Octave then branch to $05A2, loop
BRA L05B4 ; Exit) (RET...
L05AC: LSR $C1 ; >> $C1, decrement octave
ROR $C0 ; >> $C0, decrement octave with carry from $C1
DEC A ; Decrement A
L05B1: CBNE $A1,L05AC ; A != Target Octave then branch to $05AC, loop
L05B4: RET ; Return
; Handle Track Commands from JMP Table
HandleTrkCmds:
SBC A,#$C4 ; A = Track Command #
ASL A ; Double A
MOV Y,A ; Transfer A to Y
MOV A,$1882+Y ; Pointer to JMP Table, high byte
PUSH A ; Push on stack
MOV A,$1881+Y ; Pointer to JMP Table, low byte
PUSH A ; Push on stack
MOV A,Y ; Transfer Y to A
LSR A ; Halve A
MOV Y,A ; Transfer A to Y
MOV A,$18F9+Y ; Number of parameter bytes
BEQ L05D3 ; Return if none
; Next Script Op Code
NextOpCode:
MOV A,($02+X) ; A = Script Op Code
MOV $A2,A ; $A2 = Script Op Code
INC $02+X ; Increment Script Pointer
BNE L05D3 ; Return if not $00
INC $03+X ; Carry to high byte
L05D3: RET ; Return
; Handle some Track Commands
HandleTrkCmdsSwitch:
MOV A,$02+X ; A = Current Script Pointer low
MOV Y,$03+X ; Y = Current Script Pointer high
MOVW $90,YA ; $90 = Current Script Pointer
MOV A,$26+X ; A = Pointer to Loop Count
MOV $C5,A ; $C5 = Current Pointer to Loop Count
L05DE: MOV Y,#$00 ; Zero Y
L05E0: MOV A,($90)+Y ; A = Current Operation
CMP A,#$C4 ; A < #$C4, Note Command)
BCC L0647 ; Exit
INCW $90 ; Point to first parameter of track command
CMP A,#$EB ; == #$EB End of Song Script
BEQ L0647 ; Exit
CMP A,#$F6 ; == #$F6 Jump to xxxx
BNE L05F5 ; If not skip 2 instructions
CALL TrkSwitch_JumpToXXXX ; Switch #$F6: Jump to xxxx
BRA L05DE ; Loop
L05F5: CMP A,#$E3 ; == #$E3 Loop End
BNE L05FE ; If not skip 2 instructions
CALL $1725 ; Switch $E3: Loop End
BRA L05DE ; Loop
L05FE: CMP A,#$F5 ; == #$F5 Jump to yyyy when loop count reaches xx
BNE L0607 ; If not skip 2 instructions
CALL $1695 ; Switch $F5: Jump to yyyy when loop count reaches xx
BRA L05DE ; Loop
L0607: CMP A,#$E5 ; == #$E5 Disable Slur
BNE L0610 ; If not skip 2 instructions
CALL $15CF ; Track Command $E5: Disable Slur
BRA L05DE ; Loop
L0610: CMP A,#$E7 ; == #$E7 Disable Drum Roll
BNE L0619 ; If not skip 2 instructions
CALL $15F3 ; Track Command $E7: Disable Drum Roll
BRA L05DE ; Loop
L0619: CMP A,#$E9 ; == #$E9 Play Game SFX, voice A
BNE L0622 ; If not skip 2 instructions
CALL $1633 ; Switch $E9: Play Game SFX, voice A
BRA L05DE ; Loop
L0622: CMP A,#$EA ; == #$EA Play Game SFX, voice B
BNE L062B ; If not skip 2 instructions
CALL $1639 ; Switch $EA: Play Game SFX, voice B
BRA L05DE ; Loop
L062B: CMP A,#$DC ; == #$DC Set Instrument
BNE L0637 ; If not skip 3 instructions
CALL $15CF ; Track Command $E5: Disable Slur
CALL $15F3 ; Track Command $E7: Disable Drum Roll
BRA L05DE ; Loop
L0637: SETC ; Set Carry for subtraction
SBC A,#$C4 ; Index from #$C4
MOV Y,A ; Transfer A to Y
MOV A,$18F9+Y ; Number of bytes for Track Command
BEQ L05DE ; Loop if no parameters
MOV Y,A ; Transfer A to Y
L0641: INCW $90 ; Next parameter
DBNZ Y,L0641 ; Decrease Y, loop unless zero
BRA L05E0 ; Loop
L0647: RET ; Return
; Store A to DSP register Y
SetDSPReg:
MOV $F2,Y ; Set register address
MOV $F3,A ; Write parameter
RET ; Return
; Enable all timers, reset input from ports 0 and 1
EnableTimersResetPorts:
MOV $F1,#$17 ; Enable all 3 timers, reset input from ports 0 and 1
MOV $F1,#$07 ; Enable all 3 timers...
RET ; Return
; Process Volume, Pan and Pansweep Envelopes
ProcessVolPanEnv:
SETP ; Set direct page flag
MOV A,$11+X ; A = SFX Song Script Pointer low ?
BEQ L065B ; If zero skip next instruction
DEC $11+X ; Decrement
L065B: MOV A,$31+X
BEQ L0661 ; If zero skip next instruction
DEC $31+X
L0661: CLRP ; Clear direct page flag
MOV A,$F6A0+X ; A = Volume Envelope Counter
BEQ L068D ; If no envelope skip 16 instructions
DEC A ; Decrement A
MOV $F6A0+X,A ; Set Volume Envelope Counter
MOV A,$F620+X ; A = Voice Volume low
MOV $98,A ; Set $98
MOV A,$F621+X ; A = Voice Volume high
MOV $99,A ; Set $99
MOV A,$F641+X ; A = Voice Envelope Change Rate high
MOV Y,A ; Transfer A to Y
MOV A,$F640+X ; A = Voice Envelope Change Rate low
ADDW YA,$98 ; Add Voice Volume
MOV $F620+X,A ; Set Voice Volume low
MOV A,Y ; Transfer Y to A
CMP A,$F621+X ; Check if Volume Changed
MOV $F621+X,A ; Set Voice Volume high
BEQ L068D ; If no volume skip next instruction
OR ($DB),($8F) ; Enable Volume Update in DSP for Current Voice
L068D: MOV A,$F6A1+X ; A = Pan Envelope Counter
BEQ L06B8 ; If no envelope skip 16 instructions
DEC A ; Decrement A
MOV $F6A1+X,A ; Set Pan Envelope Counter
MOV A,$F660+X ; A = Voice Pan low
MOV $98,A ; Set $98
MOV A,$F661+X ; A = Voice Pan high
MOV $99,A ; Set $99
MOV A,$F681+X ; A = Pan Envelope Change Rate high
MOV Y,A ; Transfer A to Y
MOV A,$F680+X ; A = Pan Envelope Change Rate low
ADDW YA,$98 ; Add Voice Pan
MOV $F660+X,A ; Set Voice Pan low
MOV A,Y ; Transfer Y to A
CMP A,$F661+X ; Check if Pan Changed
MOV $F661+X,A ; Set Voice Pan high
BEQ L06B8 ; If no change skip next instruction
OR ($DB),($8F) ; Enable Volume Update in DSP for Current Voice
L06B8: MOV A,$F8A0+X ; A = Pitch Envelope Change Rate fraction
MOV $98,A ; Set $98
MOV A,$F780+X ; A = Pitch Envelope Change Rate low
MOV $99,A ; Set $99
MOV A,$F781+X ; A = Pitch Envelope Change Rate high
MOV $9A,A ; Set $9A
MOVW YA,$98 ; YA = Pitch Envelope Change Rate low and fraction
BNE L06CF ; If envelope skip 2 instructions
MOV A,$9A ; A = Pitch Envelope Change Rate high
BEQ L06FD ; If zero skip 19 instructions
L06CF: MOV A,$F720+X ; A = Pitch Envelope Duration
DEC A ; Decrement A
BNE L06DE ; If counting skip 3 instructions
MOV $F780+X,A ; Zero Pitch Envelope Change Rate low
MOV $F781+X,A ; Zero Pitch Envelope Change Rate high
MOV $F8A0+X,A ; Zero Pitch Envelope Change Rate fraction
L06DE: MOV $F720+X,A ; Set Pitch Envelope Duration
CLRC ; Clear Carry
MOV A,$F8E0+X ; A = Calculated Frequency Value low
MOV Y,A ; Transfer A to Y
MOV A,$F8A1+X ; A = Pitch Envelope Change Rate fraction high
ADDW YA,$98 ; Add Pitch Envelope Change Rate low and fraction
MOV $F8A1+X,A ; Set Pitch Envelope Change Rate fraction high
MOV A,Y ; Transfer Y to A
MOV $F8E0+X,A ; Set Calculataed Frequency Value low
MOV A,$9A ; A = Pitch Envelope Change Rate high
ADC A,$F8E1+X ; Add Calculated Frequency Value high
MOV $F8E1+X,A ; Set Calculated Frequency Value high
OR ($DC),($8F) ; Enable Pitch Update in DSP for Current Voice
L06FD: MOV A,$0171+X ; A = Pansweep
BEQ L0744 ; If no pansweep exit
MOV A,$F7A0+X ; A = Pansweep Change Rate low
MOV $98,A ; Set $98
MOV A,$F7A1+X ; A = Pansweep Change Rate high
MOV $99,A ; Set $99
MOV A,$F881+X ; A = Pansweep Value high
MOV Y,A ; Transfer A to Y
MOV $9A,A ; Set $9A
MOV A,$F880+X ; A = Pansweep Value low
ADDW YA,$98 ; Add Pansweep Change Rate
MOV $F880+X,A ; Set Pansweep Value low
MOV A,Y ; Transfer Y to A
CMP A,$F881+X ; Detect if Pansweep Value changed
MOV $F881+X,A ; Set Pansweep Value high
BEQ L0726 ; If no change skip next instruction
OR ($DB),($8F) ; Enable Volume Update in DSP for Current Voice
L0726: MOV A,$F701+X ; A = Pansweep Cycle Counter
DEC A ; Decrement A
BNE L0741 ; If counting skip 8 instructions)
EOR $98,#$FF ; Invert Pansweep Change Rate low
EOR $99,#$FF ; Invert Pansweep Change Rate high
INCW $98 ; Increment Pansweep Change Rate
MOV A,$98 ; A = Pansweep Change Rate low
MOV $F7A0+X,A ; Set Pansweep Change Rate low
MOV A,$99 ; A = Pansweep Change Rate high
MOV $F7A1+X,A ; Set Pansweep Change Rate high
MOV A,$F700+X ; A = Pansweep Cycle Duration
L0741: MOV $F701+X,A ; Set Pansweep Cycle Counter
L0744: RET ; Return
; Process Vibrato and Tremolo
ProcessVibTrem:
MOV A,$0151+X ; A = Vibrato Amplitude
BEQ L0751 ; If zero skip 3 instructions
MOV $A2,A ; $A2 = Vibrato Amplitude
MOV A,$0111+X ; A = Vibrato Predelay Counter
BEQ ProcessVibrato ; If zero skip next instruction
L0751: JMP ProcessTremolo ; Jump to Tremolo
ProcessVibrato:
MOV A,$F7E1+X ; A = Vibrato Change Rate high
MOV Y,A ; Transfer A to Y
MOV A,$F7E0+X ; A = Vibrato Change Rate low
MOVW $C0,YA ; $C0 = Vibrato Change Rate
MOV A,$F841+X ; A = Vibrato Value high
MOV Y,A ; Transfer A to Y
MOV A,$F840+X ; A = Vibrato Value low
ADDW YA,$C0 ; Add Vibrato Value to Vibrato Change Rate
MOVW $C2,YA ; Store as Note Pitch Multiplier
MOV $F840+X,A ; Set Vibrato Value low
MOV A,Y ; Transfer Y to A
CMP A,$F841+X ; A == Vibrato Value high
BEQ L07C8 ; If zero skip instructions
MOV $F841+X,A ; Set Vibrato Value high
ASL $C2 ; Double Note Pitch Multiplier low
ROL $C3 ; Double Note Pitch Multiplier high with carry
MOV A,$F8E1+X ; A = Calculated Frequency Value high
MOV Y,#$0F ; Y = #$0F
MUL YA ; Multiply
MOVW $98,YA ; Set to $98 word, Vibrato Max Range
MOV A,$F8E0+X ; A = Calculated Frequency Value low
MOV Y,#$0F ; Y = #$0F
MUL YA ; Multiply
MOV A,Y ; Transfer Y to A
MOV Y,#$00 ; Zero Y
ADDW YA,$98 ; Add Vibrato Max Range
MOVW $98,YA ; Set Vibrato Max Range
MOV A,$C3 ; A = Note Pitch Multiplier high
MUL YA ; Multiply
MOVW $9A,YA ; Set to $9A word, new Calculated Vibrato Value
MOV Y,$98 ; Y = Vibrato Max Range low
MOV A,$C3 ; A = Note Pitch Multiplier high
MUL YA ; Multiply
MOV A,Y ; Transfer Y to A
MOV Y,#$00 ; Zero Y
ADDW YA,$9A ; Add new Calculated Vibrato Value
MOVW $9A,YA ; Set new Calculated Vibrato Value
MOV Y,$C2 ; Y = Note Pitch Multiplier low
MOV A,$99 ; A = Vibrato Max Range high
MUL YA ; Multiply
MOV A,Y ; Transfer Y to A
MOV Y,#$00 ; Zero Y
ADDW YA,$9A ; Add new Calculated Vibrato Value
MOVW $9A,YA ; Set new Calculated Vibrato Value
BBC7 $C3,L07B1 ; If not signed skip next instruction
SUBW YA,$98 ; Subtract Vibrato Max Range
MOVW $9A,YA ; Set new Calculated Vibrato Value
L07B1: MOV A,$F8C1+X ; A = Calculated Vibrato Value high
MOV Y,A ; Transfer A to Y
MOV A,$F8C0+X ; A = Calculated Vibrato Value low
CMPW YA,$9A ; New Vibrato Value == Last Vibrato Value
BEQ L07C8 ; If so skip 5 instructions
MOVW YA,$9A ; YA = new Calculated Vibrato Value
MOV $F8C0+X,A ; Set Calculated Vibrato Value low
MOV A,Y ; Transfer Y to A
MOV $F8C1+X,A ; Set Calculated Vibrato Value high
OR ($DC),($8F) ; Enable Pitch Update in DSP for Current Voice
L07C8: MOV A,$F6C1+X ; A = Vibrato Cycle Counter
DEC A ; Decrement Vibrato Cycle Counter
BNE L07F9 ; If counting skip 18 instructions
MOV A,$F7C1+X ; A = Max Vibrato Change Rate high
MOV Y,A ; Transfer A to Y
MOV A,$F7C0+X ; A = Max Vibrato Change Rate low
MOVW $98,YA ; $98 = Max Vibrato Change Rate
MOV A,$C1 ; A = Vibrato Change Rate high
MOV $C2,A ; $C2 = Vibrato Change Rate high
MOV A,$0190+X ; A = Tremolo/Vibrato Gain Counter
MOV $9B,A ; Set $9B
AND $9B,#$70 ; $9B = Tremolo Gain Counter
AND A,#$07 ; A = Vibrato Gain Counter
CALL VolPit_ChgRate ; Process Change Rate with Mode and Gain Counter
MOV $F7E0+X,A ; Set Vibrato Change Rate low
MOV A,Y ; Transfer Y to A
MOV $F7E1+X,A ; Set Vibrato Change Rate high
MOV A,$9A ; A = Vibrato Gain Counter
OR A,$9B ; Join with Tremolo Gain Counter
MOV $0190+X,A ; Set Tremolo/Vibrato Gain Counter
MOV A,$F6C0+X ; A = Vibrato Cycle Duration
L07F9: MOV $F6C1+X,A ; Set Vibrato Cycle Counter
ProcessTremolo:
MOV A,$0170+X ; A = Tremolo mode/amp
BEQ L085D ; If zero skip to updates and exit
MOV $A2,A ; $A2 = Tremolo mode/amp
MOV A,$0131+X ; A = Tremolo Predelay Counter
BNE L085D ; If predelay active skip to updates and exit
MOV A,$F821+X ; A = Tremolo Change Rate high
MOV Y,A ; Transfer A to Y
MOV A,$F820+X ; A = Tremolo Change Rate low
MOVW $C0,YA ; $C0 = Tremolo Change Rate
MOV $C2,Y ; $C2 = Tremolo Change Rate high
MOV A,$F861+X ; A = Tremolo Value high
MOV Y,A ; Transfer A to Y
MOV A,$F860+X ; A = Tremolo Value low)
ADDW YA,$C0 ; Add Tremolo Change Rate
MOV $F860+X,A ; Set Tremolo Value low
MOV A,Y ; Transfer Y to A
CMP A,$F861+X ; Detect if Tremolo Value changed
BEQ L082B ; If no change skip 2 instructions
MOV $F861+X,A ; Set Tremolo Value high
OR ($DB),($8F) ; Enable Volume Update in DSP for Current Voice
L082B: MOV A,$F6E1+X ; A = Tremolo Cycle Counter
DEC A ; Decrement A
BNE L085A ; If counting skip 18 instructions
MOV A,$F801+X ; A = Tremolo Max Change Rate high
MOV Y,A ; Transfer A to Y
MOV A,$F800+X ; A = Tremolo Max Change Rate low
MOVW $98,YA ; $98 = Tremolo Max Change Rate
MOV A,$0190+X ; A = Tremolo/Vibrato Gain Counter
MOV $9B,A ; Set $9B
AND $9B,#$07 ; $9B = Vibrato Gain Counter
XCN A ; Swap high 4 and low 4 bits
AND A,#$07 ; A = Tremolo Gain Counter
CALL VolPit_ChgRate ; Process Change Rate with Mode and Gain Counter
MOV $F820+X,A ; Set Tremolo Change Rate low
MOV A,Y ; Transfer Y to A
MOV $F821+X,A ; Set Tremolo Change Rate high
MOV A,$9A ; A = Tremolo Gain Counter
XCN A ; Swap high 4 and low 4 bits
OR A,$9B ; Join with Vibrato Gain Counter
MOV $0190+X,A ; Set Tremolo/Vibrato Gain Counter
MOV A,$F6E0+X ; A = Tremolo Cycle Duration
L085A: MOV $F6E1+X,A ; Set Tremolo Cycle Counter
L085D: MOVW YA,$DB ; YA = DSP Volume/Pitch Update bitmask
BNE L0862 ; Branch if not 0, DSP Volume/Pitch Updates
RET ; Return
; Branch DSP Volume/Pitch Updates
VolumePitchDSP:
L0862: MOV A,X ; Transfer X to A
AND A,#$0F ; Mask #$0F
MOV $98,A ; ..
MOV A,$98 ; ..
XCN A ; Swap high 4 and low 4 bits
LSR A ; Halve A
MOV $99,A ; Set DSP Register x0
MOV A,$8F ; A = Current Voice
AND A,$DB ; And Enable Volume Update in DSP
BEQ L0879 ; if not skip 2 instructions
TCLR $00DB,A ; Test and clear Enable Volume Update in DSP
CALL VolumeUpdateDSP ; DSP Volume Update
L0879: MOV A,$8F ; A = Current Voice
AND A,$DC ; And Enable Pitch Update in DSP
BEQ L0887 ; If not exit
TCLR $00DC,A ; Test and clear Enable Pitch Update in DSP
SET1 $99 ; Set bit 1, DSP Register now x2
CALL PitchUpdateDSP ; DSP Pitch Update
L0887: RET ; Return
; DSP Volume Update
VolumeUpdateDSP:
MOV $9A,#$80 ; $9A = #$80
BBS0 $85,L08B1 ; If mono mode skip 15 instructions
BBS2 $85,L08B1 ; ??, this bit is never set
MOV A,$8F ; A = Current Voice
AND A,$84 ; And System SFX Voice
BNE L08B1 ; If voice is off then skip 12 instructions
MOV A,$F661+X ; A = Pan high
MOV Y,A ; Transfer A to Y
CMP X,#$10 ; X > #$10
BCC L08A7 ; If not SFX skip 4 instructions
MOV A,$B2 ; A = SFX Pansweep Value high
EOR A,#$80 ; Invert Signature
CALL PanWithPansweep ; Calculate Pan with Pansweep
MOV Y,A ; Y = Calculated SFX Pan
L08A7: MOV A,$F881+X ; A = Pansweep Value high
CALL PanWithPansweep ; Calculate Pan with Pansweep
EOR A,#$FF ; Invert A
MOV $9A,A ; $9A = Calculated Pan
L08B1: MOV A,$F621+X ; A = Voice Volume high
MOV Y,A ; Transfer A to Y
MOV $9B,A ; $9B = Voice Volume high
MOV A,$F861+X ; A = Tremolo Value high
BEQ L08C8 ; If zero skip 8 instructions
ASL A ; Double A
MUL YA ; Tremolo * Volume
BCS L08C8 ; If carry skip 5 instructions
MOV A,Y ; Transfer Y to A
ADC A,$9B ; Add Voice Volume
BPL L08C7 ; If less than #$80 skip next instruction
MOV A,#$7F ; A = #$7F
L08C7: MOV Y,A ; Transfer A to Y
L08C8: CMP X,#$10 ; X > #$10
BCS L08D9 ; If SFX skip 7 instructions
MOV A,$A6 ; A = Master Volume high
MUL YA ; Voice Volume wTremolo * Master Volume
MOV A,$8F ; A = Current Voice
AND A,$61 ; And Ignore Song Volume
BNE L08E6 ; If so skip 9 instructions)
MOV A,$51 ; A = Song Volume
BRA L08E5 ; Skip 6 instructions
L08D9: MOV A,$8F ; A = Current Voice
AND A,$84 ; And System SFX Voices
BEQ L08E3 ; If not System SFX voice skip 2 instructions
MOV A,#$FF ; A = #$FF
BRA L08E5 ; Skip next instruction
L08E3: MOV A,$A8 ; A = SFX Volume high
L08E5: MUL YA ; Calculated Volume * SFX Volume/Song Volume
L08E6: MOV $9B,Y ; $9B = Calculated Volume
MOV A,$9A ; A = Calculated Pan
L08EA: MOV Y,A ; Transfer A to Y
MOV A,$9B ; A = Calculated Volume
MUL YA ; Calculated * Calculated Pan
MOV A,$8F ; A = Current Voice
AND A,$A4 ; And Muted Voices
BEQ L08F6 ; If current voice is not muted skip next instruction
MOV Y,#$00 ; Zero Y
L08F6: MOV A,Y ; Transfer Y to A
MOV Y,$98 ; Y = Voice Pointer
MOV $00C9+Y,A ; Set Calculated Volume for Voice
MOV Y,A ; Transfer it to Y
MOV A,$99 ; A = DSP Register
MOV $F2,A ; Set register address
CMP Y,$F3 ; New and current value are the same
BEQ L0907 ; If so skip next instruction
MOV $F3,Y ; Write DSP Parameter
L0907: MOV A,$9A ; A = Calculated Pan
EOR A,#$FF ; Invert
NOT0 $0098 ; ,0 Increment/Decrement Voice Pointer
INC $99 ; Next register, x1
BBC1 $99,L08EA ; Loop for right channel
RET ; Return
; DSP Pitch Update
PitchUpdateDSP:
MOV A,$F8E1+X ; A = Calculated Frequency Value high
MOV Y,A ; Transfer A to Y
MOV A,$F8E0+X ; A = Calculated Frequency Value low
MOVW $9A,YA ; $9A = Calculated Frequency Value
MOV A,$F8C1+X ; A = Calculated Vibrato Value high
MOV Y,A ; Transfer A to Y
MOV A,$F8C0+X ; A = Calculated Vibrato Value low
ADDW YA,$9A ; Add Vibrato to Frequency
MOVW $9A,YA ; $9A = Final Frequency Value
CMP X,#$10 ; X < #$10
BCC L0930 ; If not SFX skip 2 instructions
MOVW YA,$9A ; YA = Final Frequency Value
BRA L094B ; Skip 20 instructions
L0930: MOV A,$BC ; A = Master Pitch Multiplier high
PUSH P ; Save Flags
PUSH A ; Save A
MUL YA ; Final Frequency Value high * Master Pitch Multiplier high
MOVW $9C,YA ; $9C = Shifted Final Frequency Value
POP A ; Restore A
MOV Y,$9A ; Y = Final Frequency Value low
MUL YA ; Final Frequency Value low * Master Pitch Multiplier high
MOV A,Y ; Transfer Y to A
MOV Y,#$00 ; Zero Y
ADDW YA,$9C ; Add Shifted Final Frequency Value
POP P ; Restore Flags
BMI L094B ; If negative skip 7 instructions
ASL A ; Double A
PUSH A ; Save A
MOV A,Y ; Transfer Y to A
ROL A ; Double A with carry
MOV Y,A ; Transfer A to Y
POP A ; Restore A
ADDW YA,$9A ; Add Final Frequency Value
L094B: PUSH X ; Save X
MOV X,$99 ; X = DSP Register
MOV $F2,X ; Set DSP Register
CMP A,$F3 ; Compare with current parameter
BEQ L0956 ; If no change skip next instruction
MOV $F3,A ; Write parameter
L0956: INC X ; Next register address
MOV $F2,X ; Set register address
CMP Y,$F3 ; Compare with current parameter
BEQ L095F ; If no change exit
MOV $F3,Y ; Write parameter
L095F: POP X ; Restore X
RET ; Return
; Process Change Rate with Mode and Gain Counter
; A = Primary Gain Counter (to use
; +$98 = Max Change Rate
; $9A = Primary Gain Counter (decremented by 1 to keep
; $9B = Secondary Gain Counter (to keep
; $A2 = mode/amplitude
; +$C0 = Change Rate
; $C2 = Change Rate (high byte, duplicate
VolPit_ChgRate:
MOV $9A,#$00 ; Zero $9A
CMP $A2,#$C0 ; Mode 3 Balanced)
BCS L09B4 ; If so skip to Mode 3
CMP $A2,#$80 ; Mode 2 Negative
BCS L098D ; If so skip to Mode 2)
; Mode 0/1 Positive
EOR $C0,#$FF ; Invert Change Rate low
EOR $C1,#$FF ; Invert Change Rate high
INCW $C0 ; Increment Change Rate
MOV Y,A ; Y = Gain Counter
BEQ L09DE ; If zero then exit with YA as Inverted Change Rate
DEC A ; Decrement A, Gain Counter
MOV $9A,A ; Store Primary Gain Counter
BBS7 $C1,L09DE ; If inversion is signed exit with YA as Inverted Change Rate
LSR $99 ; Halve Max Change Rate high
ROR $98 ; Halve Max Change Rate low with carry
LSR $99 ; Halve Max Change Rate high