-
Notifications
You must be signed in to change notification settings - Fork 0
/
akg_player.s
2440 lines (2079 loc) · 95 KB
/
akg_player.s
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
/*******************************************************************************
* + + *
* Arkos Tracker 2 player "generic" player. *
* By Targhan/Arkos. *
* Psg optimization trick on CPC by Madram/Overlanders. *
* Conversion for Elektronika MS-0511 by aberrant_hacker *
* + + *
*******************************************************************************/
# 1 to have the "stop sounds" code. Set it to 0 if you never plan on
# stopping your music.
.equiv STOP_SOUNDS, 1
# 0 to skip some init code/values, saving memory.
# Possible if you don't plan on restarting your song.
.equiv FULL_INIT_CODE, 1
.global PLY_AKG_Init
.global PLY_AKG_Play
.if STOP_SOUNDS
.global PLY_AKG_Stop
.endif
# Agglomerates some flags, because they are treated the same way by this player.
#----------------------------------------------------------------------------{{{
# Special Track Used?
.ifdef PLY_CFG_UseSpeedTracks
UseSpecialTracks = 1
.endif
.ifdef PLY_CFG_UseEventTracks
UseSpecialTracks = 1
.endif
# SoftwareOnly and HardOnly share some code.
.ifdef PLY_CFG_SoftOnly
UseSoftOnlyOrHardOnly = 1
.endif
.ifdef PLY_CFG_HardOnly
UseSoftOnlyOrHardOnly = 1
.endif
# The same for their noise.
.ifdef PLY_CFG_SoftOnly_Noise
UseSoftOnlyOrHardOnly_Noise = 1
.endif
.ifdef PLY_CFG_HardOnly_Noise
UseSoftOnlyOrHardOnly_Noise = 1
.endif
# Agglomerates the Forced periods (soft/hard).
.ifdef PLY_CFG_SoftOnly_ForcedSoftwarePeriod
UseInstrumentForcedPeriods = 1
.endif
.ifdef PLY_CFG_HardOnly_ForcedHardwarePeriod
UseInstrumentForcedPeriods = 1
.endif
.ifdef PLY_CFG_SoftToHard_ForcedSoftwarePeriod
UseInstrumentForcedPeriods = 1
.endif
.ifdef PLY_CFG_HardToSoft_ForcedHardwarePeriod
UseInstrumentForcedPeriods = 1
.endif
.ifdef PLY_CFG_SoftAndHard_ForcedSoftwarePeriod
UseInstrumentForcedPeriods = 1
.endif
# Agglomerates the Instrument Arpeggios (soft/hard).
.ifdef PLY_CFG_SoftOnly_SoftwareArpeggio
UseInstrumentArpeggios = 1
.endif
.ifdef PLY_CFG_SoftToHard_SoftwareArpeggio
UseInstrumentArpeggios = 1
.endif
.ifdef PLY_CFG_HardOnly_HardwareArpeggio
UseInstrumentArpeggios = 1
.endif
.ifdef PLY_CFG_HardToSoft_HardwareArpeggio
UseInstrumentArpeggios = 1
.endif
.ifdef PLY_CFG_SoftAndHard_SoftwareArpeggio
UseInstrumentArpeggios = 1
.endif
.ifdef PLY_CFG_SoftAndHard_HardwareArpeggio
UseInstrumentArpeggios = 1
.endif
# Agglomerates the Instrument Pitchs (soft/hard).
.ifdef PLY_CFG_SoftOnly_SoftwarePitch
UseInstrumentPitchs = 1
.endif
.ifdef PLY_CFG_SoftToHard_SoftwarePitch
UseInstrumentPitchs = 1
.endif
.ifdef PLY_CFG_HardOnly_HardwarePitch
UseInstrumentPitchs = 1
.endif
.ifdef PLY_CFG_HardToSoft_HardwarePitch
UseInstrumentPitchs = 1
.endif
.ifdef PLY_CFG_SoftAndHard_SoftwarePitch
UseInstrumentPitchs = 1
.endif
.ifdef PLY_CFG_SoftAndHard_HardwarePitch
UseInstrumentPitchs = 1
.endif
# Agglomerates the Instrument Forced Periods, Arpeggios and Pitchs (soft/hard).
.ifdef UseInstrumentForcedPeriods
UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1
.endif
.ifdef UseInstrumentArpeggios
UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1
.endif
.ifdef UseInstrumentPitchs
UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1
.endif
# Agglomerates the Retrig flags for SoftToHard, HardToSoft, SoftAndHard.
.ifdef PLY_CFG_SoftToHard_Retrig
UseRetrig_StoH_HtoS_SandH = 1
.endif
.ifdef PLY_CFG_HardToSoft_Retrig
UseRetrig_StoH_HtoS_SandH = 1
.endif
.ifdef PLY_CFG_SoftAndHard_Retrig
UseRetrig_StoH_HtoS_SandH = 1
.endif
# Agglomerates the noise flags for SoftToHard, HardToSoft, SoftAndHard.
.ifdef PLY_CFG_SoftToHard_Noise
UseNoise_StoH_HtoS_SandH = 1
.endif
.ifdef PLY_CFG_HardToSoft_Noise
UseNoise_StoH_HtoS_SandH = 1
.endif
.ifdef PLY_CFG_SoftAndHard_Noise
UseNoise_StoH_HtoS_SandH = 1
.endif
# Agglomerates the noise flags to know if the code about R6 must be compiled.
.ifdef PLY_CFG_NoSoftNoHard_Noise
Use_NoiseRegister = 1
.endif
.ifdef PLY_CFG_SoftOnly_Noise
Use_NoiseRegister = 1
.endif
.ifdef PLY_CFG_HardOnly_Noise
Use_NoiseRegister = 1
.endif
.ifdef PLY_CFG_SoftToHard_Noise
Use_NoiseRegister = 1
.endif
.ifdef PLY_CFG_HardToSoft_Noise
Use_NoiseRegister = 1
.endif
.ifdef PLY_CFG_SoftAndHard_Noise
Use_NoiseRegister = 1
.endif
# Agglomerates the effect volume in/out.
.ifdef PLY_CFG_UseEffect_VolumeIn
UseEffect_VolumeSlide = 1
.endif
.ifdef PLY_CFG_UseEffect_VolumeOut
UseEffect_VolumeSlide = 1
.endif
# Agglomerates the Arpeggios Table effects.
.ifdef PLY_CFG_UseEffect_Arpeggio3Notes
PLY_AKS_UseEffect_Arpeggio = 1
.endif
.ifdef PLY_CFG_UseEffect_Arpeggio4Notes
PLY_AKS_UseEffect_Arpeggio = 1
.endif
.ifdef PLY_CFG_UseEffect_ArpeggioTable
PLY_AKS_UseEffect_Arpeggio = 1
.endif
# Agglomerates the PitchUp/Down effects.
.ifdef PLY_CFG_UseEffect_PitchUp
PLY_AKS_UseEffect_PitchUpOrDown = 1
.endif
.ifdef PLY_CFG_UseEffect_PitchDown
PLY_AKS_UseEffect_PitchUpOrDown = 1
.endif
# Agglomerates the PitchUp/Down/Glide effects.
# IMPORTANT TO NOTE that if there is Glide, there WILL be pitch up/down,
# because the Glide is embedded in the pitch up/down code.
.ifdef PLY_AKS_UseEffect_PitchUpOrDown
PLY_AKS_UseEffect_PitchUpOrDownOrGlide = 1
.endif
.ifdef PLY_CFG_UseEffect_PitchGlide
PLY_AKS_UseEffect_PitchUpOrDownOrGlide = 1
.endif
# Agglomerates a special flag combining ArpeggiosTable and PitchTable.
.ifdef PLY_AKS_UseEffect_Arpeggio
PLY_AKS_UseEffect_ArpeggioTableOrPitchTable = 1
.endif
.ifdef PLY_CFG_UseEffect_PitchTable
PLY_AKS_UseEffect_ArpeggioTableOrPitchTable = 1
.endif
#----------------------------------------------------------------------------}}}
.equiv OPCODE_CLC, 0000241 # Opcode for CLC
.equiv OPCODE_SEC, 0000261 # Opcode for SEC
.equiv OPCODE_ADD_IMMEDIATE_R5, 0062705 # Opcode ADD (PC)+, R5
.equiv OPCODE_SUB_IMMEDIATE_R5, 0162705 # Opcode SUB (PC)+, R5
.equiv OPCODE_ADD_IMMEDIATE_IMMEDIATE, 0062727 # Opcode ADD (PC)+, (PC)+
.equiv OPCODE_SUB_IMMEDIATE_IMMEDIATE, 0162727 # Opcode SUB (PC)+, (PC)+
.equiv OPCODE_ADC_R5, 005505 # Opcode ADC R5
.equiv OPCODE_SBC_R5, 005605 # Opcode SBC R5
# Includes the sound effects player, if wanted.
# Important to do it as soon as possible, so that
# its code can react to the Player Configuration and possibly alter it.
.ifdef PLY_AKG_MANAGE_SOUND_EFFECTS
.include "akg_sound_effects.s"
.endif # PLY_AKG_MANAGE_SOUND_EFFECTS
# [[INSERT_SOUND_EFFECT_SOURCE]] # A tag for test units.
# Don't touch or you're dead.
# Initializes the player.
# IN: R5 = music address.
# R0 = subsong index (>=0).
PLY_AKG_Init: #--------------------------------------------------------------{{{
.ifdef PLY_CFG_UseEffects # CONFIG SPECIFIC
ADD $4, R5 # Skip the tag
.ifdef PLY_AKS_UseEffect_Arpeggio # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:432
MOV (R5)+, ArpeggiosTable
.else
INC R5
INC R5
.endif # PLY_AKS_UseEffect_Arpeggio
.ifdef PLY_CFG_UseEffect_PitchTable # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:440
MOV (R5)+, PitchesTable
.else
INC R5
INC R5
.endif # PLY_CFG_UseEffect_PitchTable
.else # No effects.
ADD $4+2+2, R5 # Skips the tag and the arp/pitch table.
.endif # PLY_CFG_UseEffects
MOV (R5), InstrumentsTable1
MOV (R5), InstrumentsTable2
MOV (R5)+, InstrumentsTable3
.ifdef PLY_CFG_UseEffects # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:456
MOV (R5), Channel_ReadEffects_EffectBlocks1
MOV (R5)+, Channel_ReadEffects_EffectBlocks2
.else # No effects.
INC R5
INC R5 # Skips the effect block table.
.endif # PLY_CFG_UseEffects
# We have reached the Subsong addresses. Which one to use?
ASL R0
ADD R0, R5
MOV (R5), R5 # R5 points on the Subsong metadata.
ADD $5, R5 # Skips the replay frequency, digichannel, psg count, loop start index, end index.
MOVB (R5)+, CurrentSpeed
MOVB (R5)+, BaseNoteIndex
INC R5 # align on word boundary, kind of
MOV R5, ReadLinker_PtLinker
# Initializes values. You can remove this part if you don't stop/restart your song.
.if FULL_INIT_CODE # playerAkg/sources/PlayerAkg.asm:492
MOV $InitTable0, R5
.set words_count, (InitTable0_End - InitTable0) >> 1
MOV $words_count + 1, R1
CLR R0
CALL Init_ReadWordsAndFill
MOV $InitTable1, R5
.set words_count, (InitTable1_End - InitTable1) >> 1
MOV $words_count + 1, R1
INC R0
CALL Init_ReadWordsAndFill
MOV $InitTableOrA, R5
.set words_count, (InitTableOrA_End - InitTableOrA) >> 1
MOV $words_count + 1, R1
MOV $OPCODE_CLC, R0 # CLC opcode
CALL Init_ReadWordsAndFill
.ifdef PLY_CFG_UseRetrig # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:511
MOV $0xFF, PSGReg13_OldValue
.endif
.endif # FULL_INIT_CODE
# Stores the address to the empty instrument *data* (header skipped).
MOV InstrumentsTable1, R5
MOV (R5), R5
INC R5 # Skip the header
MOV R5, EmptyInstrumentDataPt
# Sets all the instrument to "empty".
MOV R5, Channel1_PtInstrument
MOV R5, Channel2_PtInstrument
MOV R5, Channel3_PtInstrument
# If sound effects, clears the SFX state.
.ifdef PLY_AKG_MANAGE_SOUND_EFFECTS # playerAkg/sources/PlayerAkg.asm:550
CLR Channel1_SoundEffectData
CLR Channel2_SoundEffectData
CLR Channel3_SoundEffectData
.endif # PLY_AKG_MANAGE_SOUND_EFFECTS
RETURN # Init #--------------------------------------------------------------}}}
.if FULL_INIT_CODE # playerAkg/sources/PlayerAkg.asm:559 #-----------------{{{
# Fills all the read addresses with a byte.
# IN: R5 = table where the addresses are.
# R1 = how many items in the table + 1.
# R0 = byte to fill.
Init_ReadWordsAndFill_Loop:
MOV R0, @(R5)+
Init_ReadWordsAndFill:
SOB R1, Init_ReadWordsAndFill_Loop
RETURN
# Table initializing some data with 0.
InitTable0: # playerAkg/sources/PlayerAkg.asm:576
.word Channel1_InvertedVolumeIntegerAndDecimal
.word Channel2_InvertedVolumeIntegerAndDecimal
.word Channel3_InvertedVolumeIntegerAndDecimal
.ifdef PLY_AKS_UseEffect_PitchUpOrDown # CONFIG SPECIFIC
.word Channel1_Pitch
.word Channel2_Pitch
.word Channel3_Pitch
.endif #PLY_AKS_UseEffect_PitchUpOrDown
.ifdef PLY_CFG_UseRetrig # CONFIG SPECIFIC
.word Retrig
.endif #PLY_CFG_UseRetrig
InitTable0_End:
InitTable1: # playerAkg/sources/PlayerAkg.asm:598
.word PatternDecreasingHeight
.word TickDecreasingCounter
InitTable1_End:
InitTableOrA: # playerAkg/sources/PlayerAkg.asm:605 #------------------------{{{
.ifdef UseEffect_VolumeSlide # CONFIG SPECIFIC
.word Channel1_IsVolumeSlide
.word Channel2_IsVolumeSlide
.word Channel3_IsVolumeSlide
.endif # UseEffect_VolumeSlide
.ifdef PLY_AKS_UseEffect_Arpeggio # CONFIG SPECIFIC
.word Channel1_IsArpeggioTable
.word Channel2_IsArpeggioTable
.word Channel3_IsArpeggioTable
.endif # PLY_AKS_UseEffect_Arpeggio
.ifdef PLY_CFG_UseEffect_PitchTable # CONFIG SPECIFIC
.word Channel1_IsPitchTable
.word Channel2_IsPitchTable
.word Channel3_IsPitchTable
.endif # PLY_CFG_UseEffect_PitchTable
.ifdef PLY_AKS_UseEffect_PitchUpOrDown # CONFIG SPECIFIC
.word Channel1_IsPitch
.word Channel2_IsPitch
.word Channel3_IsPitch
.endif # PLY_AKS_UseEffect_PitchUpOrDown
InitTableOrA_End: #----------------------------------------------------------}}}
.endif # FULL_INIT_CODE # playerAkg/sources/PlayerAkg.asm:629 #------------}}}
.if STOP_SOUNDS # playerAkg/sources/PlayerAkg.asm:654 #--------------------{{{
# Stops the music.
# This code can be removed if you don't intend to stop it!
PLY_AKG_Stop:
# All the volumes to 0, all sound/noise channels stopped.
CLRB PSGReg8
CLR PSGReg9_10_Instr
MOV $0b00111111, PSGReg7
JMP SendPSGRegisters
.endif # STOP_SOUNDS #-----------------------------------------------------}}}
################################################################################
# Plays one frame of the subsong. #
################################################################################
PLY_AKG_Play: # playerAkg/sources/PlayerAkg.asm:676 #
.ifdef PLY_CFG_UseEventTracks # CONFIG SPECIFIC
CLR Event
.endif # PLY_CFG_UseEventTracks
# Decreases the tick counter. If 0 is reached, a new line must be read.
.equiv TickDecreasingCounter, .+2
MOV $1, R0
DEC R0
BZE ReadNewLine
# Jumps if there is no new line: continues playing the sound stream.
JMP SetSpeedBeforePlayStreams
ReadNewLine:
# New line! Is the Pattern ended?
# Not as long as there are lines to read.
.equiv PatternDecreasingHeight, .+2
MOV $1, R0
DEC R0
BZE ReadLinker # pattern ended
# Jumps if the pattern isn't ended.
JMP SetCurrentLineBeforeReadLine
# New pattern!
# Reads the Linker. This is called at the start of the song,
# or at the end of every position.
ReadLinker: # playerAkg/sources/PlayerAkg.asm:704
.equiv ReadLinker_PtLinker, .+2
MOV $0, R4
# Reads the address of each Track.
MOV (R4)+, R5
BNZ ReadLinker_NoLoop
# End of the song.
MOV (R4)+, R4 # read loop address
MOV (R4)+, R5 # Reads once again the address of Track 1, in the pattern looped to.
ReadLinker_NoLoop: # playerAkg/sources/PlayerAkg.asm:720
MOV R5, Channel1_PtTrack
MOV (R4)+, Channel2_PtTrack
MOV (R4)+, Channel3_PtTrack
# Reads the address of the LinkerBlock.
MOV (R4)+, R5
MOV R4, ReadLinker_PtLinker
MOV R5, R4
# Reads the LinkerBlock. R4 = LinkerBlock.
# Reads the height and transposition1.
MOV (R4)+, R5
CLR R2
BISB R5, R2 # Height
.ifdef PLY_CFG_UseTranspositions # CONFIG SPECIFIC
SWAB R5
MOVB R5, R0 # can be negative, sign extension is required
MOV R0, Channel1_Transposition
.endif # PLY_CFG_UseTranspositions
# Reads the transposition2 and 3.
.ifdef UseSpecialTracks # CONFIG SPECIFIC #--------------------------------{{{
.ifndef PLY_CFG_UseTranspositions # CONFIG SPECIFIC
# Transpositions not used? We could stop here.
# BUT the SpecialTracks, if present, must access their data after.
# So in this case, the transpositions must be skipped.
INC R4
INC R4
.endif # PLY_CFG_UseTranspositions
.endif # UseSpecialTracks #------------------------------------------------}}}
.ifdef PLY_CFG_UseTranspositions # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:747
MOVB (R4)+, R5 # can be negative, sign extension is required
MOV R5, Channel2_Transposition
MOVB (R4)+, R5 # can be negative, sign extension is required
MOV R5, Channel3_Transposition
.endif # PLY_CFG_UseTranspositions
.ifdef UseSpecialTracks # CONFIG SPECIFIC #--------------------------------{{{
# Reads the special Tracks addresses.
# Must be performed even SpeedTracks not used, because EventTracks might
# be present, the word must be skipped.
MOV (R4)+, R5
# Reads the special Tracks addresses.
.ifdef PLY_CFG_UseSpeedTracks # CONFIG SPECIFIC
MOV R5, SpeedTrack_PtTrack
.endif # PLY_CFG_UseSpeedTracks
.ifdef PLY_CFG_UseEventTracks # CONFIG SPECIFIC
MOV (R4)+, R5
MOV R5, EventTrack_PtTrack
.endif # PLY_CFG_UseEventTracks
.endif # UseSpecialTracks #------------------------------------------------}}}
# Forces the reading of every Track and Special Track.
.ifdef PLY_CFG_UseSpeedTracks # CONFIG SPECIFIC #--------------------------{{{
CLR SpeedTrack_WaitCounter
.endif # PLY_CFG_UseSpeedTracks #------------------------------------------}}}
.ifdef PLY_CFG_UseEventTracks # CONFIG SPECIFIC #--------------------------{{{
CLR EventTrack_WaitCounter
.endif # PLY_CFG_UseEventTracks #------------------------------------------}}}
CLR Channel1_WaitCounter
CLR Channel2_WaitCounter
CLR Channel3_WaitCounter
MOV R2, R0
SetCurrentLineBeforeReadLine: # playerAkg/sources/PlayerAkg.asm:779
MOV R0, PatternDecreasingHeight
# Reads the new line (notes, effects, Special Tracks, etc.).
ReadLine: # playerAkg/sources/PlayerAkg.asm:784
# Reads the Speed Track.
.ifdef PLY_CFG_UseSpeedTracks # CONFIG SPECIFIC
# playerAkg/sources/PlayerAkg.asm:786 #------------------------------{{{
.equiv SpeedTrack_WaitCounter, .+2
MOV $0, R0 # Lines to wait?
SUB $1, R0
BCC SpeedTrack_MustWait # Jump if there are still lines to wait.
# No more lines to wait. Reads a new data.
# It may be an event value or a wait value.
.equiv SpeedTrack_PtTrack, .+2
MOV $0, R5
CLR R0
BISB (R5)+, R0
ASR R0 # Bit 0: wait?
# Jump if wait: R0 is the wait value.
BCS SpeedTrack_StorePointerAndWaitCounter
# Value found. If 0, escape value (rare).
BNZ SpeedTrack_NormalValue
# Escape code. Reads the right value.
MOVB (R5)+, R0
SpeedTrack_NormalValue:
MOVB R0, CurrentSpeed
CLR R0 # Next time, a new value is read.
SpeedTrack_StorePointerAndWaitCounter:
MOV R5, SpeedTrack_PtTrack
SpeedTrack_MustWait:
MOV R0, SpeedTrack_WaitCounter
SpeedTrack_End:
.endif # PLY_CFG_UseSpeedTracks #------------------------------------------}}}
# Reads the Event Track.
#--------------------------------------------------------------------{{{
.ifdef PLY_CFG_UseEventTracks # CONFIG SPECIFIC
.error "528" # playerAkg/sources/PlayerAkg.asm:828
.equiv EventTrack_WaitCounter, .+2
MOV $0, R0 # Lines to wait?
SUB $1, R0
BHIS EventTrack_MustWait # Jump if there are still lines to wait.
# No more lines to wait. Reads a new data.
# It may be an event value or a wait value.
.equiv EventTrack_PtTrack, .+2
MOV $0, R5
CLR R0
BISB (R5)+, R0
ASR R0 # Bit 0: wait?
# Jump if wait: R0 is the wait value.
BCS EventTrack_StorePointerAndWaitCounter
# Value found. If 0, escape value (rare).
BNZ EventTrack_NormalValue
# Escape code. Reads the right value.
CLR R0
BISB (R5)+, R0
EventTrack_NormalValue:
MOV R0, Event
CLR R0 # Next time, a new value is read.
EventTrack_StorePointerAndWaitCounter:
MOV R5, EventTrack_PtTrack
EventTrack_MustWait:
MOV R0, EventTrack_WaitCounter
EventTrack_End:
.endif # PLY_CFG_UseEventTracks #------------------------------------------}}}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Reads the possible Cell of the Channel 1, 2 and 3. *
* Use a Macro for each channel, but the code is duplicated. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
.macro ReadTrack cN # playerAkg/sources/PlayerAkg.asm:873 #------------------{{{
# Lines to wait?
.equiv Channel\cN\()_WaitCounter, .+2
DECB $0
BMI Channel\cN\()_ReadTrack
# Still some lines to wait.
JMP Channel\cN\()_ReadCellEnd
Channel\cN\()_ReadTrack: # playerAkg/sources/PlayerAkg.asm:886
# Points on the Cell to read.
.equiv Channel\cN\()_PtTrack, .+2
MOV $0, R5
# Reads note data. It can be a note, a wait...
CLR R2
BISB (R5)+, R2 # R2 = data (b5-0) + effect? (b6) + new Instrument? (b7).
MOV R2, R0
BIC $0xFFC0, R0 # R0 = data
# 0-59: note.
# "CMP" is preferred to "SUB" so that the "note" branch (the slowest) is note-ready.
CMP R0, $60
BLO Channel\cN\()_Note
SUB $60, R0
.jmp EQ, Channel\cN\()_MaybeEffects # 60 = no note, but maybe effects.
DEC R0
BZE Channel\cN\()_Wait # 61 = wait, no effect.
DEC R0
BZE Channel\cN\()_SmallWait # 62 = small wait, no effect.
# 63 = escape code for note, maybe effects.
# Reads the note in the next byte
CLR R0
BISB (R5)+, R0
BR Channel\cN\()_AfterNoteKnown
# Small wait, no effect.
Channel\cN\()_SmallWait: # playerAkg/sources/PlayerAkg.asm:914
ASH $-6, R2 # Uses bit 6/7 to indicate how many lines to wait.
INC R2 # This wait start at 2 lines, to 5.
MOV R2, Channel\cN\()_WaitCounter
BR Channel\cN\()_BeforeEnd_StoreCellPointer
# Wait, no effect.
Channel\cN\()_Wait: # playerAkg/sources/PlayerAkg.asm:924
# Reads the wait value on the next byte (HL has already been incremented).
MOVB (R5)+, Channel\cN\()_WaitCounter
BR Channel\cN\()_BeforeEnd_StoreCellPointer
# Little subcode put here, called just below. A bit dirty, but avoids long jump.
Channel\cN\()_SameInstrument: # playerAkg/sources/PlayerAkg.asm:931
# No new instrument. The instrument pointer must be reset.
.equiv Channel\cN\()_PtBaseInstrument, .+2
MOV $0, Channel\cN\()_PtInstrument
BR Channel\cN\()_AfterInstrument
# A note has been found, plus maybe an Instrument and effects.
# R0 = note. R2 = still has the New Instrument/Effects flags.
Channel\cN\()_Note: # playerAkg/sources/PlayerAkg.asm:943
# Declares this only for the first channel, else refers to it.
.if \cN == 1
# The encoded note is only from a 4 octave range, but the first note
# depends on the best window, determined by the song generator.
.equiv BaseNoteIndex, .+2
ADD $0, R0
.else
ADD BaseNoteIndex, R0
.endif
Channel\cN\()_AfterNoteKnown: # playerAkg/sources/PlayerAkg.asm:957
.ifdef PLY_CFG_UseTranspositions # CONFIG SPECIFIC
# Adds the Track transposition.
.equiv Channel\cN\()_Transposition, .+2
ADD $0, R0
.endif # PLY_CFG_UseTranspositions
MOV R0, Channel\cN\()_TrackNote
# R5 = next data. R2 = data byte.
ROLB R2 # New Instrument?
BCC Channel\cN\()_SameInstrument
# Gets the new Instrument.
MOVB (R5)+, R4 # NOTE: 127 instruments supported only
ASL R4
# Points on the Instruments table of the music (set on song initialization).
.equiv InstrumentsTable\cN, .+2
MOV 0(R4), R4
# No need to store an "original speed" if "force instrument speed"
# effect is not used.
.ifdef PLY_CFG_UseEffect_ForceInstrumentSpeed # CONFIG SPECIFIC
MOVB (R4)+, Channel\cN\()_InstrumentOriginalSpeed
.else
MOVB (R4)+, Channel\cN\()_InstrumentSpeed
.endif # PLY_CFG_UseEffect_ForceInstrumentSpeed
MOV R4, Channel\cN\()_PtInstrument
# Useful when playing another note with the same instrument.
MOV R4, Channel\cN\()_PtBaseInstrument
Channel\cN\()_AfterInstrument: # playerAkg/sources/PlayerAkg.asm:1008
# There is a new note. The instrument pointer has already been reset.
# -------------------------------------------------------------------
# Instrument number is set.
# Arpeggio and Pitch Table are reset.
# The track pitch and glide, instrument step are reset.
.ifdef PLY_AKS_UseEffect_PitchUpOrDownOrGlide # CONFIG SPECIFIC #----------{{{
CLR Channel\cN\()_Pitch
MOV $OPCODE_CLC, Channel\cN\()_IsPitch
.endif # PLY_AKS_UseEffect_PitchUpOrDownOrGlide #--------------------------}}}
# Resets the speed of the Arpeggio and the Pitch.
.ifdef PLY_AKS_UseEffect_Arpeggio # CONFIG SPECIFIC #----------------------{{{
CLR Channel\cN\()_ArpeggioTableCurrentStep
# Resets the speed of the Arpeggio
MOV Channel\cN\()_ArpeggioBaseSpeed, Channel\cN\()_ArpeggioTableSpeed
# Points to the first value of the Arpeggio.
MOV Channel\cN\()_ArpeggioTableBase, Channel\cN\()_ArpeggioTable
.endif # PLY_AKS_UseEffect_Arpeggio #--------------------------------------}}}
.ifdef PLY_CFG_UseEffect_PitchTable # CONFIG SPECIFIC #--------------------{{{
CLR Channel\cN\()_PitchTableCurrentStep
# Resets the speed of the Pitch.
MOV Channel\cN\()_PitchBaseSpeed, Channel\cN\()_PitchTableSpeed
# Points to the first value of the Pitch.
MOV Channel\cN\()_PitchTableBase, Channel\cN\()_PitchTable
.endif # PLY_CFG_UseEffect_PitchTable #------------------------------------}}}
CLR Channel\cN\()_InstrumentStep
# If the "force instrument speed" effect is used,
# the instrument speed must be reset to its original value.
.ifdef PLY_CFG_UseEffect_ForceInstrumentSpeed # CONFIG SPECIFIC #----------{{{
.equiv Channel\cN\()_InstrumentOriginalSpeed, .+2
MOV $0, Channel\cN\()_InstrumentSpeed
.endif # PLY_CFG_UseEffect_ForceInstrumentSpeed #--------------------------}}}
.ifdef PLY_CFG_UseEffects # CONFIG SPECIFIC
# Effects?
ROLB R2
.jmp CS, Channel\cN\()_ReadEffects
.endif # PLY_CFG_UseEffects
# No effects. Nothing more to read for this cell.
Channel\cN\()_BeforeEnd_StoreCellPointer:
MOV R5, Channel\cN\()_PtTrack
Channel\cN\()_ReadCellEnd:
.endm # ReadTrack # playerAkg/sources/PlayerAkg.asm:1081 #-------------------}}}
# Generates the code for each channel, from the macro above.
ReadTrack 1
ReadTrack 2
ReadTrack 3
.equiv CurrentSpeed, .+2
MOV $0, R0
SetSpeedBeforePlayStreams: # playerAkg/sources/PlayerAkg.asm:1104
MOV R0, TickDecreasingCounter
/* * * * * * * * * * * * * * * * * * * * * * * * * * *
* Applies the trailing effects for channel 1, 2, 3. *
* Uses a macro instead of duplicating the code. *
* * * * * * * * * * * * * * * * * * * * * * * * * * */
.macro ApplyTrailingEffects cN #---------------------------------------------{{{
# Use Volume slide?
#----------------------------
.equiv Channel\cN\()_InvertedVolumeIntegerAndDecimal, .+2
MOV $0, R5
# playerAkg/sources/PlayerAkg.asm:1127
.equiv Channel\cN\()_InvertedVolumeInteger, Channel\cN\()_InvertedVolumeIntegerAndDecimal + 1
.ifdef UseEffect_VolumeSlide # CONFIG SPECIFIC #---------------------------{{{
# Is there a Volume Slide ?
# Automodified. SEC if yes, CLC if not.
Channel\cN\()_IsVolumeSlide:
CLC
BCC Channel\cN\()_VolumeSlide_End
.equiv Channel\cN\()_VolumeSlideValue, .+2
ADD $0, R5 # May be negative.
BVC Channel\cN\()_VolumeNotOverflow
# Went below 0
CLR R5
BR Channel\cN\()_VolumeSetAgain
Channel\cN\()_VolumeNotOverflow:
# Higher than 15?
SWAB R5
CMPB R5, $15
BLOS Channel\cN\()_VolumeSetAgain
CLRB R5
BISB $15, R5
Channel\cN\()_VolumeSetAgain:
SWAB R5
MOV R5, Channel\cN\()_InvertedVolumeIntegerAndDecimal
Channel\cN\()_VolumeSlide_End:
.endif # UseEffect_VolumeSlide #-------------------------------------------}}}
SWAB R5
MOVB R5, Channel\cN\()_GeneratedCurrentInvertedVolume
# Use Arpeggio table? OUT: R2 = value.
#----------------------------------------
.ifdef PLY_AKS_UseEffect_Arpeggio # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:1169 {{{
CLR R2
Channel\cN\()_IsArpeggioTable:
CLC # Is there an arpeggio table? Automodified. SEC if yes, CLC if not.
BCC Channel\cN\()_ArpeggioTable_End
# We can read the Arpeggio table for a new value.
.equiv Channel\cN\()_ArpeggioTable, .+2
MOV $0, R5 # Points on the data, after the header.
MOVB (R5)+, R2 # Reads the value.
CMP R2, $-128 # Loop?
BNE Channel\cN\()_ArpeggioTable_AfterLoopTest
# Loop. Where to?
INC R5 # align at word boundary
MOV (R5), R5
MOVB (R5), R2 # Reads the value. Safe, we know there is no loop here.
# R5 = pointer on what is follows.
# R2 = value to use.
Channel\cN\()_ArpeggioTable_AfterLoopTest:
# Checks the speed.
# If reached, the pointer can be saved to read a new value next time.
.equiv Channel\cN\()_ArpeggioTableCurrentStep, .+2
MOV $0, R0
INC R0
CMP R0, Channel\cN\()_ArpeggioTableSpeed # From 1 to 256.
# The current step may be higher than the limit if
# Force Speed effect is used.
BLO Channel\cN\()_ArpeggioTable_BeforeEnd_SaveStep
# Stores the pointer to read a new value next time.
MOV R5, Channel\cN\()_ArpeggioTable
CLR R0
Channel\cN\()_ArpeggioTable_BeforeEnd_SaveStep:
MOV R0, Channel\cN\()_ArpeggioTableCurrentStep
Channel\cN\()_ArpeggioTable_End:
.endif # PLY_AKS_UseEffect_Arpeggio #--------------------------------------}}}
# Use Pitch table? OUT: R3 = pitch value.
# R2 must NOT be modified!
#-----------------------
CLR R3 # Default value.
.ifdef PLY_CFG_UseEffect_PitchTable # CONFIG SPECIFIC #--------------------{{{
# playerAkg/sources/PlayerAkg.asm:1232
Channel\cN\()_IsPitchTable:
CLC # Is there an pitch table? Automodified. SEC if yes, CLC if not.
BCC Channel\cN\()_PitchTable_End
.equiv Channel\cN\()_PitchTable, .+2
MOV $0, R5 # Read the Pitch table for a value.
MOV (R5)+, R3 # Reads the value.
MOV (R5), R5 # Reads the pointer to the next value. Manages the loop automatically!
# Checks the speed.
# If reached, the pointer can be saved (advance in the Pitch).
.equiv Channel\cN\()_PitchTableCurrentStep, .+2
MOV $0, R0
INC R0
CMPB R0, Channel\cN\()_PitchTableSpeed # From 1 to 256.
# The current step may be higher than the limit if Force Speed effect is used.
BLO Channel\cN\()_PitchTable_BeforeEnd_SaveStep
# Advances in the Pitch.
MOV R5, Channel\cN\()_PitchTable
CLR R0
Channel\cN\()_PitchTable_BeforeEnd_SaveStep:
MOV R0, Channel\cN\()_PitchTableCurrentStep
Channel\cN\()_PitchTable_End:
.endif # PLY_CFG_UseEffect_PitchTable #------------------------------------}}}
# Pitch management. The Glide is embedded, but relies on the Pitch
# (Pitch can exist without Glide, but Glide can not without Pitch).
# Do NOT modify R2 or R3.
#-----------------------------------------------------------------------
.ifndef PLY_AKS_UseEffect_PitchUpOrDownOrGlide # CONFIG SPECIFIC
# playerAkg/sources/PlayerAkg.asm:1276
CLR R5 # No pitch.
# Some dirty duplication in case there is no pitch up/down/glide.
# The "real" vars are a bit below.
# Put here, no need for better place (see the real label below, with the same name).
Channel\cN\()_SoundStream_RelativeModifierAddress:
.ifdef PLY_AKS_UseEffect_ArpeggioTableOrPitchTable # CONFIG SPECIFIC #---{{{
BR Channel\cN\()_AfterArpeggioPitchVariables
.ifdef PLY_AKS_UseEffect_Arpeggio # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:1284 {{{
Channel\cN\()_ArpeggioTableSpeed: .word 0
Channel\cN\()_ArpeggioBaseSpeed: .word 0
Channel\cN\()_ArpeggioTableBase: .word 0
.endif # PLY_AKS_UseEffect_Arpeggio #----------------------------------}}}
.ifdef PLY_CFG_UseEffect_PitchTable # CONFIG SPECIFIC # playerAkg/sources/PlayerAkg.asm:1291 {{{
Channel\cN\()_PitchTableSpeed: .word 0
Channel\cN\()_PitchBaseSpeed: .word 0
Channel\cN\()_PitchTableBase: .word 0
.endif # PLY_CFG_UseEffect_PitchTable #--------------------------------}}}
Channel\cN\()_AfterArpeggioPitchVariables:
.endif # PLY_AKS_UseEffect_ArpeggioTableOrPitchTable #-------------------}}}
.else # PLY_AKS_UseEffect_PitchUpOrDownOrGlide # playerAkg/sources/PlayerAkg.asm:1301
.equiv Channel\cN\()_Pitch, .+2
MOV $0, R5 # #----------------------{{{
Channel\cN\()_IsPitch:
CLC # Is there a Pitch? Automodified. SEC if yes, CLC if not.
BCC Channel\cN\()_Pitch_End
# R2 must NOT be modified, stores it.
.ifdef PLY_AKS_UseEffect_Arpeggio # CONFIG SPECIFIC
# NOTE: в оригинале, содержимое C сохраняется в
# Channel\cN\()_GeneratedCurrentArpNote в самом конце
# ApplyTrailingEffects, и больше нигде не используется
# почему нельзя было сохранить прям отсюда???
#ld ixl, c
# R2 ниже нигде не изменятся и тоже сохраняется в
# самом конце макроса
.endif # PLY_AKS_UseEffect_Arpeggio
Channel\cN\()_PitchTrackAddOrSub:
# Value from the user. ALWAYS POSITIVE. Does not evolve. MSB is always 0.
.equiv Channel\cN\()_PitchTrack, .+2
ADD $0, R5 # WILL BE AUTOMODIFIED to ADD or SUB
# Makes the decimal part evolves.
# Value from the user.
Channel\cN\()_PitchTrackDecimalInstr:
.equiv Channel\cN\()_PitchTrackDecimalValue, .+2
# the label is present in original code
# but here it acts as a commentary
# the only result we use down below is the carry flag
.equiv Channel\cN\()_PitchTrackDecimalCounter, .+4
ADD $0, $0 # WILL BE AUTOMODIFIED to ADD or SUB.
Channel\cN\()_PitchTrackIntegerAdcOrSbc:
ADC R5 # WILL BE AUTOMODIFIED to ADC or SBC
MOV R5, Channel\cN\()_Pitch
# This must be placed at the any location to allow reaching
# the variables via R2/R3
Channel\cN\()_SoundStream_RelativeModifierAddress:
.ifdef PLY_CFG_UseEffect_PitchGlide # CONFIG SPECIFIC #------------------{{{
# playerAkg/sources/PlayerAkg.asm:1360
# Glide?
# 0 = no glide. 1 = glide/pitch up. 2 = glide/pitch down.
.equiv Channel\cN\()_GlideDirection, .+2
MOV $0, R0
# Is there a Glide?
BZE Channel\cN\()_Glide_End
MOV R5, R4
MOV Channel\cN\()_TrackNote, R1
ASLB R1
ADD PeriodTable(R1), R4
# R4 is now the current period (track pitch + note period).
# Period to reach (note given by the user, converted to period).
.equiv Channel\cN\()_GlideToReach, .+2
MOV $0, R1
# Have we reached the glide destination?
# Depends on the direction.
RORB R0
BCC Channel\cN\()_GlideDownCheck
# Glide up. Check.
# The glide period should be lower than the current pitch.
SUB R1, R4
# If not reached yet, continues the pitch.
BCC Channel\cN\()_Glide_End
BR Channel\cN\()_GlideOver
Channel\cN\()_GlideDownCheck:
# The glide period should be higher than the current pitch.
SUB R1, R4
# If not reached yet, continues the pitch.
BCS Channel\cN\()_Glide_End
Channel\cN\()_GlideOver:
# The glide is over. However, it may be over, so we can't simply use
# the current pitch period. We have to set the exact needed value.
MOV R1, R5
MOV Channel\cN\()_TrackNote, R1
ASLB R1
SUB PeriodTable(R1), R5
MOV R5, Channel\cN\()_Pitch
MOV $OPCODE_CLC, Channel\cN\()_IsPitch
BR Channel\cN\()_Glide_End
#--------------------------------------------------------------------}}}
.else # PLY_CFG_UseEffect_PitchGlide not defined
# Skips the variables below, if there are present.
.ifdef PLY_AKS_UseEffect_ArpeggioTableOrPitchTable # CONFIG SPECIFIC
BR Channel\cN\()_AfterArpeggioPitchVariables
.endif # PLY_AKS_UseEffect_ArpeggioTableOrPitchTable
.endif # PLY_CFG_UseEffect_PitchGlide # playerAkg/sources/PlayerAkg.asm:1429
# A small place to stash some vars which have to be within relative
# range. Dirty, but no choice.
# Note that the vars just below are duplicated due to the conditional
# assembling (they are a bit above).
.ifdef PLY_AKS_UseEffect_Arpeggio # CONFIG SPECIFIC #--------------------{{{
Channel\cN\()_ArpeggioTableSpeed: .word 0
Channel\cN\()_ArpeggioBaseSpeed: .word 0
Channel\cN\()_ArpeggioTableBase: .word 0
.endif # PLY_AKS_UseEffect_Arpeggio #------------------------------------}}}
.ifdef PLY_CFG_UseEffect_PitchTable # CONFIG SPECIFIC #------------------{{{
Channel\cN\()_PitchTableSpeed: .word 0
Channel\cN\()_PitchBaseSpeed: .word 0
Channel\cN\()_PitchTableBase: .word 0
.endif # PLY_CFG_UseEffect_PitchTable #----------------------------------}}}
Channel\cN\()_AfterArpeggioPitchVariables:
.ifdef PLY_CFG_UseEffect_PitchGlide # CONFIG SPECIFIC #------------------{{{
Channel\cN\()_Glide_End:
.endif # PLY_CFG_UseEffect_PitchGlide #----------------------------------}}}
Channel\cN\()_Pitch_End:
#--------------------------------------------------------------------}}}