-
Notifications
You must be signed in to change notification settings - Fork 0
/
PlayerAkg.asm
3683 lines (3222 loc) · 157 KB
/
PlayerAkg.asm
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.
;
; This compiles with RASM. Check the compatibility page on the Arkos Tracker 2 website, it contains a source converter to any Z80 assembler!
;
; The player uses the stack for optimizations. Make sure the interruptions are disabled before it is called.
; The stack pointer is saved at the beginning and restored at the end.
;
; Target hardware:
; ---------------
; This code can target Amstrad CPC, MSX, Spectrum and Pentagon. By default, it targets Amstrad CPC.
; Simply use one of the follow line (BEFORE this player):
; PLY_AKG_HARDWARE_CPC = 1
; PLY_AKG_HARDWARE_MSX = 1
; PLY_AKG_HARDWARE_SPECTRUM = 1
; PLY_AKG_HARDWARE_PENTAGON = 1
; Note that the PRESENCE of this variable is tested, NOT its value.
;
; ROM
; ----------------------
; To use a ROM player (no automodification, use of a small buffer to put in RAM):
; PLY_AKG_ROM = 1
; PLY_AKG_ROM_Buffer = #4000 (or wherever).
; This makes the player a bit slower and slightly bigger.
; The buffer is PLY_AKG_ROM_BufferSize long (=250 max). You can hardcode this value, because it is calculated, so it won't be accessible before this player is assembled.
; This value decreases when you use player configuration, but increases if you use sound effects.
;
; Optimizations:
; --------------
; - Use the Player Configuration of Arkos Tracker 2 to generate a configuration file to be included at the beginning of this player.
; It will disable useless features according to your songs, saving for memory and CPU! Check the manual for more details, or more simply the testers.
; - Set PLY_AKG_USE_HOOKS to 0 to remove the three hooks just below to save 9 bytes (yay!).
; - Set PLY_AKG_STOP_SOUNDS to 0 if you don't intent to stop the music via the PLY_AKG_Stop method.
; - If you play your song "one shot" (i.e. without restarting it again), you can set the PLY_AKG_FULL_INIT_CODE to 0, some
; initialization code will not be assembled.
; Sound effects:
; --------------
; Sound effects are disabled by default. Declare PLY_AKG_MANAGE_SOUND_EFFECTS to enable it:
; PLY_AKG_MANAGE_SOUND_EFFECTS = 1
; Check the sound effect tester to see how it enables it.
; Note that the PRESENCE of this variable is tested, NOT its value.
; Additional note:
; - There can be a slightly difference when using volume in/out effects compared to the PC side, because the volume management is
; different. This means there can be a difference of 1 at certain frames. As it shouldn't be a bother, I let it this way.
; This allows the Z80 code to be faster and simpler.
;
; -------------------------------------------------------
PLY_AKG_Start:
;Checks the hardware. Only one must be selected.
PLY_AKG_HardwareCounter = 0
IFDEF PLY_AKG_HARDWARE_CPC
PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1
ENDIF
IFDEF PLY_AKG_HARDWARE_MSX
PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1
PLY_AKG_HARDWARE_SPECTRUM_OR_MSX = 1
ENDIF
IFDEF PLY_AKG_HARDWARE_SPECTRUM
PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1
PLY_AKG_HARDWARE_SPECTRUM_OR_PENTAGON = 1
PLY_AKG_HARDWARE_SPECTRUM_OR_MSX = 1
ENDIF
IFDEF PLY_AKG_HARDWARE_PENTAGON
PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1
PLY_AKG_HARDWARE_SPECTRUM_OR_PENTAGON = 1
ENDIF
IF PLY_AKG_HardwareCounter > 1
FAIL 'Only one hardware must be selected!'
ENDIF
;By default, selects the Amstrad CPC.
IF PLY_AKG_HardwareCounter == 0
PLY_AKG_HARDWARE_CPC = 1
ENDIF
PLY_AKG_USE_HOOKS: equ 1 ;Use hooks for external calls? 0 if the Init/Play/Stop methods are directly called. Will save a few bytes.
PLY_AKG_STOP_SOUNDS: equ 1 ;1 to have the "stop sounds" code. Set it to 0 if you never plan on stopping your music.
PLY_AKG_FULL_INIT_CODE: equ 1 ;0 to skip some init code/values, saving memory. Possible if you don't plan on restarting your song.
;Is there a loaded Player Configuration source? If no, use a default configuration.
IFNDEF PLY_CFG_ConfigurationIsPresent
PLY_CFG_UseSpeedTracks = 1
PLY_CFG_UseEventTracks = 1
PLY_CFG_UseTranspositions = 1
PLY_CFG_UseHardwareSounds = 1
PLY_CFG_UseEffects = 1
PLY_CFG_UseRetrig = 1
;PLY_CFG_UseInstrumentRetrig = 1 ;Not tested, the UseRetrig is used instead.
PLY_CFG_UseInstrumentLoopTo = 1
;PLY_CFG_NoSoftNoHard = 1 ;Not tested, because always present.
PLY_CFG_NoSoftNoHard_Noise = 1
PLY_CFG_SoftOnly = 1
PLY_CFG_SoftOnly_Noise = 1
PLY_CFG_SoftOnly_ForcedSoftwarePeriod = 1
PLY_CFG_SoftOnly_SoftwareArpeggio = 1
PLY_CFG_SoftOnly_SoftwarePitch = 1
PLY_CFG_SoftToHard = 1
PLY_CFG_SoftToHard_Noise = 1
PLY_CFG_SoftToHard_ForcedSoftwarePeriod = 1
PLY_CFG_SoftToHard_SoftwareArpeggio = 1
PLY_CFG_SoftToHard_SoftwarePitch = 1
PLY_CFG_SoftToHard_HardwarePitch = 1
PLY_CFG_SoftToHard_Retrig = 1
PLY_CFG_HardOnly = 1
PLY_CFG_HardOnly_Noise = 1
PLY_CFG_HardOnly_ForcedHardwarePeriod = 1
PLY_CFG_HardOnly_HardwareArpeggio = 1
PLY_CFG_HardOnly_HardwarePitch = 1
PLY_CFG_HardOnly_Retrig = 1
PLY_CFG_HardToSoft = 1
PLY_CFG_HardToSoft_Noise = 1
PLY_CFG_HardToSoft_ForcedHardwarePeriod = 1
PLY_CFG_HardToSoft_HardwareArpeggio = 1
PLY_CFG_HardToSoft_HardwarePitch = 1
PLY_CFG_HardToSoft_SoftwarePitch = 1
PLY_CFG_HardToSoft_Retrig = 1
PLY_CFG_SoftAndHard = 1
PLY_CFG_SoftAndHard_Noise = 1
PLY_CFG_SoftAndHard_ForcedSoftwarePeriod = 1
PLY_CFG_SoftAndHard_SoftwareArpeggio = 1
PLY_CFG_SoftAndHard_SoftwarePitch = 1
PLY_CFG_SoftAndHard_ForcedHardwarePeriod = 1
PLY_CFG_SoftAndHard_HardwareArpeggio = 1
PLY_CFG_SoftAndHard_HardwarePitch = 1
PLY_CFG_SoftAndHard_Retrig = 1
PLY_CFG_UseEffect_Legato = 1
PLY_CFG_UseEffect_Reset = 1
PLY_CFG_UseEffect_ForcePitchTableSpeed = 1
PLY_CFG_UseEffect_ForceArpeggioSpeed = 1
PLY_CFG_UseEffect_ForceInstrumentSpeed = 1
PLY_CFG_UseEffect_PitchGlide = 1
PLY_CFG_UseEffect_PitchUp = 1
PLY_CFG_UseEffect_PitchDown = 1
PLY_CFG_UseEffect_PitchTable = 1
PLY_CFG_UseEffect_Arpeggio3Notes = 1
PLY_CFG_UseEffect_Arpeggio4Notes = 1
PLY_CFG_UseEffect_ArpeggioTable = 1
PLY_CFG_UseEffect_SetVolume = 1
PLY_CFG_UseEffect_VolumeIn = 1
PLY_CFG_UseEffect_VolumeOut = 1
ENDIF
;Agglomerates some flags, because they are treated the same way by this player.
;--------------------------------------------------
;Special Track Used?
IFDEF PLY_CFG_UseSpeedTracks
PLY_AKG_UseSpecialTracks = 1
ENDIF
IFDEF PLY_CFG_UseEventTracks
PLY_AKG_UseSpecialTracks = 1
ENDIF
;SoftwareOnly and HardOnly share some code.
IFDEF PLY_CFG_SoftOnly
PLY_AKG_UseSoftOnlyOrHardOnly = 1
ENDIF
IFDEF PLY_CFG_HardOnly
PLY_AKG_UseSoftOnlyOrHardOnly = 1
ENDIF
;The same for their noise.
IFDEF PLY_CFG_SoftOnly_Noise
PLY_AKG_UseSoftOnlyOrHardOnly_Noise = 1
ENDIF
IFDEF PLY_CFG_HardOnly_Noise
PLY_AKG_UseSoftOnlyOrHardOnly_Noise = 1
ENDIF
;Agglomerates the Forced periods (soft/hard).
IFDEF PLY_CFG_SoftOnly_ForcedSoftwarePeriod
PLY_AKG_UseInstrumentForcedPeriods = 1
ENDIF
IFDEF PLY_CFG_HardOnly_ForcedHardwarePeriod
PLY_AKG_UseInstrumentForcedPeriods = 1
ENDIF
IFDEF PLY_CFG_SoftToHard_ForcedSoftwarePeriod
PLY_AKG_UseInstrumentForcedPeriods = 1
ENDIF
IFDEF PLY_CFG_HardToSoft_ForcedHardwarePeriod
PLY_AKG_UseInstrumentForcedPeriods = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_ForcedSoftwarePeriod
PLY_AKG_UseInstrumentForcedPeriods = 1
ENDIF
;Agglomerates the Instrument Arpeggios (soft/hard).
IFDEF PLY_CFG_SoftOnly_SoftwareArpeggio
PLY_AKG_UseInstrumentArpeggios = 1
ENDIF
IFDEF PLY_CFG_SoftToHard_SoftwareArpeggio
PLY_AKG_UseInstrumentArpeggios = 1
ENDIF
IFDEF PLY_CFG_HardOnly_HardwareArpeggio
PLY_AKG_UseInstrumentArpeggios = 1
ENDIF
IFDEF PLY_CFG_HardToSoft_HardwareArpeggio
PLY_AKG_UseInstrumentArpeggios = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_SoftwareArpeggio
PLY_AKG_UseInstrumentArpeggios = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_HardwareArpeggio
PLY_AKG_UseInstrumentArpeggios = 1
ENDIF
;Agglomerates the Instrument Pitchs (soft/hard).
IFDEF PLY_CFG_SoftOnly_SoftwarePitch
PLY_AKG_UseInstrumentPitchs = 1
ENDIF
IFDEF PLY_CFG_SoftToHard_SoftwarePitch
PLY_AKG_UseInstrumentPitchs = 1
ENDIF
IFDEF PLY_CFG_HardOnly_HardwarePitch
PLY_AKG_UseInstrumentPitchs = 1
ENDIF
IFDEF PLY_CFG_HardToSoft_HardwarePitch
PLY_AKG_UseInstrumentPitchs = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_SoftwarePitch
PLY_AKG_UseInstrumentPitchs = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_HardwarePitch
PLY_AKG_UseInstrumentPitchs = 1
ENDIF
;Agglomerates the Instrument Forced Periods, Arpeggios and Pitchs (soft/hard).
IFDEF PLY_AKG_UseInstrumentForcedPeriods
PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1
ENDIF
IFDEF PLY_AKG_UseInstrumentArpeggios
PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1
ENDIF
IFDEF PLY_AKG_UseInstrumentPitchs
PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1
ENDIF
;Agglomerates the Retrig flags for SoftToHard, HardToSoft, SoftAndHard.
IFDEF PLY_CFG_SoftToHard_Retrig
PLY_AKG_UseRetrig_StoH_HtoS_SandH = 1
ENDIF
IFDEF PLY_CFG_HardToSoft_Retrig
PLY_AKG_UseRetrig_StoH_HtoS_SandH = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_Retrig
PLY_AKG_UseRetrig_StoH_HtoS_SandH = 1
ENDIF
;Agglomerates the noise flags for SoftToHard, HardToSoft, SoftAndHard.
IFDEF PLY_CFG_SoftToHard_Noise
PLY_AKG_UseNoise_StoH_HtoS_SandH = 1
ENDIF
IFDEF PLY_CFG_HardToSoft_Noise
PLY_AKG_UseNoise_StoH_HtoS_SandH = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_Noise
PLY_AKG_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
PLY_AKG_Use_NoiseRegister = 1
ENDIF
IFDEF PLY_CFG_SoftOnly_Noise
PLY_AKG_Use_NoiseRegister = 1
ENDIF
IFDEF PLY_CFG_HardOnly_Noise
PLY_AKG_Use_NoiseRegister = 1
ENDIF
IFDEF PLY_CFG_SoftToHard_Noise
PLY_AKG_Use_NoiseRegister = 1
ENDIF
IFDEF PLY_CFG_HardToSoft_Noise
PLY_AKG_Use_NoiseRegister = 1
ENDIF
IFDEF PLY_CFG_SoftAndHard_Noise
PLY_AKG_Use_NoiseRegister = 1
ENDIF
;Agglomerates the effect volume in/out.
IFDEF PLY_CFG_UseEffect_VolumeIn
PLY_AKG_UseEffect_VolumeSlide = 1
ENDIF
IFDEF PLY_CFG_UseEffect_VolumeOut
PLY_AKG_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 ArpeggioTable and PitchTable.
IFDEF PLY_AKS_UseEffect_Arpeggio
PLY_AKS_UseEffect_ArpeggioTableOrPitchTable = 1
ENDIF
IFDEF PLY_CFG_UseEffect_PitchTable
PLY_AKS_UseEffect_ArpeggioTableOrPitchTable = 1
ENDIF
;A nice trick to manage the offset using the same instructions, according to the player (ROM or not).
IFDEF PLY_AKG_Rom
PLY_AKG_Offset1b: equ 0
PLY_AKG_Offset2b: equ 0 ;Used for instructions such as ld iyh,xx
ELSE
PLY_AKG_Offset1b: equ 1
PLY_AKG_Offset2b: equ 2
ENDIF
IFNDEF PLY_AKG_Rom
PLY_AKG_OPCODE_OR_A: equ #b7 ;Opcode for "or a".
PLY_AKG_OPCODE_SCF: equ #37 ;Opcode for "scf".
ELSE
;Another trick for the ROM player. The original opcodes are converted to number, which will be multiplied by 2, provoking a carry or not.
PLY_AKG_OPCODE_OR_A: equ 0 ;0 * 2 = 0, no carry.
PLY_AKG_OPCODE_SCF: equ #ff ;255 * 2 = carry.
PLY_AKG_OPCODE_JP: equ #c3
ENDIF
PLY_AKG_OPCODE_ADD_HL_BC_LSB: equ #09 ;Opcode for "add hl,bc", LSB.
PLY_AKG_OPCODE_ADD_HL_BC_MSB: equ #00 ;Opcode for "add hl,bc", MSB (fake, it is only 8 bits).
PLY_AKG_OPCODE_SBC_HL_BC_LSB: equ #42 ;Opcode for "sbc hl,bc", LSB.
PLY_AKG_OPCODE_SBC_HL_BC_MSB: equ #ed ;Opcode for "sbc hl,bc", MSB.
PLY_AKG_OPCODE_INC_HL: equ #23 ;Opcode for "inc hl".
PLY_AKG_OPCODE_DEC_HL: equ #2b ;Opcode for "dec hl".
PLY_AKG_OPCODE_ADD_A_IMMEDIATE: equ #c6 ;Opcode for "add a,x".
PLY_AKG_OPCODE_SUB_IMMEDIATE: equ #d6 ;Opcode for "sub x".
;Disark macro: Word region Start.
disarkCounter = 0
IFNDEF dkws
MACRO dkws
PLY_AKG_DisarkWordRegionStart_{disarkCounter}
ENDM
ENDIF
;Disark macro: Word region End.
IFNDEF dkwe
MACRO dkwe
PLY_AKG_DisarkWordRegionEnd_{disarkCounter}:
disarkCounter = disarkCounter + 1
ENDM
ENDIF
;Disark macro: Pointer region Start.
disarkCounter = 0
IFNDEF dkps
MACRO dkps
PLY_AKG_DisarkPointerRegionStart_{disarkCounter}
ENDM
ENDIF
;Disark macro: Pointer region End.
IFNDEF dkpe
MACRO dkpe
PLY_AKG_DisarkPointerRegionEnd_{disarkCounter}:
disarkCounter = disarkCounter + 1
ENDM
ENDIF
;Disark macro: Byte region Start.
disarkCounter = 0
IFNDEF dkbs
MACRO dkbs
PLY_AKG_DisarkByteRegionStart_{disarkCounter}
ENDM
ENDIF
;Disark macro: Byte region End.
IFNDEF dkbe
MACRO dkbe
PLY_AKG_DisarkByteRegionEnd_{disarkCounter}:
disarkCounter = disarkCounter + 1
ENDM
ENDIF
;Disark macro: Force "No Reference Area" for 3 bytes (ld hl,xxxx).
IFNDEF dknr3
MACRO dknr3
PLY_AKG_DisarkForceNonReferenceDuring3_{disarkCounter}:
disarkCounter = disarkCounter + 1
ENDM
ENDIF
;Hooks for external calls. Can be removed if not needed.
if PLY_AKG_USE_HOOKS
assert PLY_AKG_Start == $ ;Makes sure no extra byte were inserted before the hooks.
jp PLY_AKG_Init ;PLY_AKG_Start + 0.
jp PLY_AKG_Play ;PLY_AKG_Start + 3.
if PLY_AKG_STOP_SOUNDS
jp PLY_AKG_Stop ;PLY_AKG_Start + 6.
endif ;PLY_AKG_STOP_SOUNDS
endif ;PLY_AKG_USE_HOOKS
;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 "PlayerAkg_SoundEffects.asm"
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: HL = music address.
; A = subsong index (>=0).
PLY_AKG_InitDisarkGenerateExternalLabel:
PLY_AKG_Init:
IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
;Skips the tag.
dknr3 (void): ld de,4
add hl,de
IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC
ld de,PLY_AKG_ArpeggiosTable + PLY_AKG_Offset1b
ldi
ldi
ELSE
inc hl
inc hl
ENDIF
IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
ld de,PLY_AKG_PitchesTable + PLY_AKG_Offset1b
ldi
ldi
ELSE
inc hl
inc hl
ENDIF ;PLY_CFG_UseEffect_PitchTable
ELSE
;No effects. Skips the tag and the arp/pitch table.
dknr3 (void): ld de,4 + 2 + 2
add hl,de
ENDIF ;PLY_CFG_UseEffects
ld de,PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b
ldi
ldi
IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
ld c,(hl)
inc hl
ld b,(hl)
inc hl
ld (PLY_AKG_Channel_ReadEffects_EffectBlocks1 + PLY_AKG_Offset1b),bc
IFNDEF PLY_AKG_Rom
;Not used in ROM, the same value is used.
ld (PLY_AKG_Channel_ReadEffects_EffectBlocks2 + PLY_AKG_Offset1b),bc
ENDIF
ELSE
;No effects. Skips the effect block table.
inc hl
inc hl
ENDIF ;PLY_CFG_UseEffects
;We have reached the Subsong addresses. Which one to use?
add a,a
ld e,a
ld d,0
add hl,de
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
;HL points on the Subsong metadata.
dknr3 (void): ld de,5 ;Skips the replay frequency, digichannel, psg count, loop ^art index, end index.
add hl,de
ld de,PLY_AKG_CurrentSpeed + PLY_AKG_Offset1b ;Reads the initial speed (>0).
ldi
ld de,PLY_AKG_BaseNoteIndex + PLY_AKG_Offset1b ;Reads the base note of the note that is considered "optimized", contrary to "escaped".
ldi
ld (PLY_AKG_ReadLinker_PtLinker + PLY_AKG_Offset1b),hl
;Initializes values. You can remove this part if you don't stop/restart your song.
if PLY_AKG_FULL_INIT_CODE
ld hl,PLY_AKG_InitTable0
dknr3 (void): ld bc,((PLY_AKG_InitTable0_End - PLY_AKG_InitTable0) / 2 + 1) * 256 + 0
call PLY_AKG_Init_ReadWordsAndFill
inc c
ld hl,PLY_AKG_InitTable1
ld b,(PLY_AKG_InitTable1_End - PLY_AKG_InitTable1) / 2 + 1
call PLY_AKG_Init_ReadWordsAndFill
ld hl,PLY_AKG_InitTableOrA
dknr3 (void): ld bc,((PLY_AKG_InitTableOrA_End - PLY_AKG_InitTableOrA) / 2 + 1) * 256 + PLY_AKG_OPCODE_OR_A
call PLY_AKG_Init_ReadWordsAndFill
IFDEF PLY_AKG_Rom
;The ROM version requires a bit more of setup.
ld hl,PLY_AKG_InitTableJp
dknr3 (void): ld bc,((PLY_AKG_InitTableJp_End - PLY_AKG_InitTableJp) / 2 + 1) * 256 + PLY_AKG_OPCODE_JP
call PLY_AKG_Init_ReadWordsAndFill
ENDIF
IFDEF PLY_CFG_UseRetrig ;CONFIG SPECIFIC
ld a,255
ld (PLY_AKG_PSGReg13_OldValue + PLY_AKG_Offset1b),a
ENDIF ;PLY_CFG_UseRetrig
endif
;Stores the address to the empty instrument *data* (header skipped).
ld hl,(PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b)
ld e,(hl)
inc hl
ld d,(hl)
ex de,hl
inc hl ;Skips the header.
ld (PLY_AKG_EmptyInstrumentDataPt + PLY_AKG_Offset1b),hl
;Sets all the instrument to "empty".
ld (PLY_AKG_Channel1_PtInstrument + PLY_AKG_Offset1b),hl
ld (PLY_AKG_Channel2_PtInstrument + PLY_AKG_Offset1b),hl
ld (PLY_AKG_Channel3_PtInstrument + PLY_AKG_Offset1b),hl
;The ROM version requires a bit more of setup.
IFDEF PLY_AKG_Rom
IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC
xor a
REPEAT 3, channelNumber
;In the non-ROM code, the MSB is always 0, the LSB is updated. The MSB must be reset for ROM version.
ld (PLY_AKG_Channel{channelNumber}_PitchTrack + 1),a
ld hl,PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAddOrSubReturn
ld (PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress + 1),hl
ld hl,PLY_AKG_Channel{channelNumber}_PitchTrackAddOrSbc_16bitsReturn
ld (PLY_AKG_Channel{channelNumber}_PitchTrackAfterAddOrSbcJumpInstrAndAddress + 1),hl
ld hl,PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValueReturnAfterJp
ld (PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValueReturnJp + 1),hl
REND
ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide
ENDIF
;If sound effects, clears the SFX state.
IFDEF PLY_AKG_MANAGE_SOUND_EFFECTS
dknr3 (void): ld hl,0
REPEAT 3, channelNumber
ld (PLY_AKG_Channel{channelNumber}_SoundEffectData),hl
REND
ENDIF ;PLY_AKG_MANAGE_SOUND_EFFECTS
ret
if PLY_AKG_FULL_INIT_CODE
;Fills all the read addresses with a byte.
;IN: HL = table where the addresses are.
; B = how many items in the table + 1.
; C = byte to fill.
PLY_AKG_Init_ReadWordsAndFill_Loop:
ld e,(hl)
inc hl
ld d,(hl)
inc hl
ld a,c
ld (de),a
PLY_AKG_Init_ReadWordsAndFill:
djnz PLY_AKG_Init_ReadWordsAndFill_Loop
ret
;Table initializing some data with 0.
PLY_AKG_InitTable0:
dkps (void): ;Disark macro.
dw PLY_AKG_Channel1_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b
dw PLY_AKG_Channel1_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + 1 ;PLY_AKG_Offset2b must NOT be used here.
dw PLY_AKG_Channel2_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b
dw PLY_AKG_Channel2_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + 1
dw PLY_AKG_Channel3_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b
dw PLY_AKG_Channel3_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + 1
IFDEF PLY_AKS_UseEffect_PitchUpOrDown ;CONFIG SPECIFIC
dw PLY_AKG_Channel1_Pitch + PLY_AKG_Offset1b
dw PLY_AKG_Channel1_Pitch + PLY_AKG_Offset1b + 1
dw PLY_AKG_Channel2_Pitch + PLY_AKG_Offset1b
dw PLY_AKG_Channel2_Pitch + PLY_AKG_Offset1b + 1
dw PLY_AKG_Channel3_Pitch + PLY_AKG_Offset1b
dw PLY_AKG_Channel3_Pitch + PLY_AKG_Offset1b + 1
ENDIF ;PLY_AKS_UseEffect_PitchUpOrDown
IFDEF PLY_CFG_UseRetrig ;CONFIG SPECIFIC
dw PLY_AKG_Retrig + 1
ENDIF ;PLY_CFG_UseRetrig
dkpe (void): ;Disark macro.
PLY_AKG_InitTable0_End:
PLY_AKG_InitTable1:
dkps (void): ;Disark macro.
dw PLY_AKG_PatternDecreasingHeight + PLY_AKG_Offset1b
dw PLY_AKG_TickDecreasingCounter + PLY_AKG_Offset1b
dkpe (void): ;Disark macro.
PLY_AKG_InitTable1_End:
PLY_AKG_InitTableOrA:
dkps (void): ;Disark macro.
IFDEF PLY_AKG_UseEffect_VolumeSlide ;CONFIG SPECIFIC
dw PLY_AKG_Channel1_IsVolumeSlide
dw PLY_AKG_Channel2_IsVolumeSlide
dw PLY_AKG_Channel3_IsVolumeSlide
ENDIF ;PLY_AKG_UseEffect_VolumeSlide
IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC
dw PLY_AKG_Channel1_IsArpeggioTable
dw PLY_AKG_Channel2_IsArpeggioTable
dw PLY_AKG_Channel3_IsArpeggioTable
ENDIF ;PLY_AKS_UseEffect_Arpeggio
IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
dw PLY_AKG_Channel1_IsPitchTable
dw PLY_AKG_Channel2_IsPitchTable
dw PLY_AKG_Channel3_IsPitchTable
ENDIF ;PLY_CFG_UseEffect_PitchTable
IFDEF PLY_AKS_UseEffect_PitchUpOrDown ;CONFIG SPECIFIC
dw PLY_AKG_Channel1_IsPitch
dw PLY_AKG_Channel2_IsPitch
dw PLY_AKG_Channel3_IsPitch
ENDIF ;PLY_AKS_UseEffect_PitchUpOrDown
dkpe (void): ;Disark macro.
PLY_AKG_InitTableOrA_End:
endif ;PLY_AKG_FULL_INIT_CODE
IFDEF PLY_AKG_Rom
PLY_AKG_InitTableJp:
dkps (void): ;Disark macro.
IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide
dw PLY_AKG_Channel1_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress
dw PLY_AKG_Channel2_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress
dw PLY_AKG_Channel3_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress
dw PLY_AKG_Channel1_PitchTrackAfterAddOrSbcJumpInstrAndAddress
dw PLY_AKG_Channel2_PitchTrackAfterAddOrSbcJumpInstrAndAddress
dw PLY_AKG_Channel3_PitchTrackAfterAddOrSbcJumpInstrAndAddress
dw PLY_AKG_Channel1_PitchTrackDecimalInstrAndValueReturnJp
dw PLY_AKG_Channel2_PitchTrackDecimalInstrAndValueReturnJp
dw PLY_AKG_Channel3_PitchTrackDecimalInstrAndValueReturnJp
ENDIF
IFDEF PLY_CFG_UseEffects
dw PLY_AKG_Channel_ReadEffects_EndJumpInstrAndAddress
ENDIF
dw PLY_AKG_TempPlayInstrumentJumpInstrAndAddress
dkpe (void): ;Disark macro.
PLY_AKG_InitTableJp_End:
ENDIF
if PLY_AKG_STOP_SOUNDS
;Stops the music. This code can be removed if you don't intend to stop it!
PLY_AKG_StopDisarkGenerateExternalLabel:
PLY_AKG_Stop:
ld (PLY_AKG_SaveSP + PLY_AKG_Offset1b),sp ;Only useful because the PLY_AKG_SendPSGRegisters restores it at the end.
;All the volumes to 0, all sound/noise channels stopped.
xor a
ld l,a
ld h,a
ld (PLY_AKG_PSGReg8),a
ld (PLY_AKG_PSGReg9_10_Instr + PLY_AKG_Offset1b),hl
IFDEF PLY_AKG_HARDWARE_MSX
ld a,%10111111 ;On MSX, bit 7 must be 1, bit 6 0.
ELSE
ld a,%00111111 ;On CPC, bit 6 must be 0. Other platforms don't care.
ENDIF
jp PLY_AKG_SendPSGRegisters
endif ;PLY_AKG_STOP_SOUNDS
;Plays one frame of the subsong.
PLY_AKG_PlayDisarkGenerateExternalLabel:
PLY_AKG_Play:
ld (PLY_AKG_SaveSP + PLY_AKG_Offset1b),sp
IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC
xor a
ld (PLY_AKG_Event),a
ENDIF ;PLY_CFG_UseEventTracks
;Decreases the tick counter. If 0 is reached, a new line must be read.
IFNDEF PLY_AKG_Rom
PLY_AKG_TickDecreasingCounter: ld a,1
ELSE
ld a,(PLY_AKG_TickDecreasingCounter)
ENDIF
dec a
jp nz,PLY_AKG_SetSpeedBeforePlayStreams ;Jumps if there is no new line: continues playing the sound stream.
;New line! Is the Pattern ended? Not as long as there are lines to read.
IFNDEF PLY_AKG_Rom
PLY_AKG_PatternDecreasingHeight: ld a,1
ELSE
ld a,(PLY_AKG_PatternDecreasingHeight)
ENDIF
dec a
jr nz,PLY_AKG_SetCurrentLineBeforeReadLine ;Jumps if the pattern isn't ended.
;New pattern!
;Reads the Linker. This is called at the start of the song, or at the end of every position.
PLY_AKG_ReadLinker:
IFNDEF PLY_AKG_Rom
dknr3 (void):
PLY_AKG_ReadLinker_PtLinker: ld sp,0
ELSE
ld sp,(PLY_AKG_ReadLinker_PtLinker)
ENDIF
;Reads the address of each Track.
pop hl
ld a,l
or h
jr nz,PLY_AKG_ReadLinker_NoLoop ;Reached the end of the song?
;End of the song.
pop hl ;HL is the loop address.
ld sp,hl
pop hl ;Reads once again the address of Track 1, in the pattern looped to.
PLY_AKG_ReadLinker_NoLoop:
ld (PLY_AKG_Channel1_PtTrack + PLY_AKG_Offset1b),hl
pop hl
ld (PLY_AKG_Channel2_PtTrack + PLY_AKG_Offset1b),hl
pop hl
ld (PLY_AKG_Channel3_PtTrack + PLY_AKG_Offset1b),hl
;Reads the address of the LinkerBlock.
pop hl
ld (PLY_AKG_ReadLinker_PtLinker + PLY_AKG_Offset1b),sp
ld sp,hl
;Reads the LinkerBlock. SP = LinkerBlock.
;Reads the height and transposition1.
pop hl
ld c,l ;Stores the pattern height, used below.
IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
ld a,h
ld (PLY_AKG_Channel1_Transposition + PLY_AKG_Offset1b),a
ENDIF ;PLY_CFG_UseTranspositions
;Reads the transposition2 and 3.
IFDEF PLY_AKG_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.
pop hl
ENDIF ;PLY_CFG_UseTranspositions
ENDIF ;PLY_AKG_UseSpecialTracks
IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
pop hl
ld a,l
ld (PLY_AKG_Channel2_Transposition + PLY_AKG_Offset1b),a
ld a,h
ld (PLY_AKG_Channel3_Transposition + PLY_AKG_Offset1b),a
ENDIF ;PLY_CFG_UseTranspositions
IFDEF PLY_AKG_UseSpecialTracks ;CONFIG SPECIFIC
;Reads the special Tracks addresses.
pop hl ;Must be performed even SpeedTracks not used, because EventTracks might be present, the word must be skipped.
IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC
ld (PLY_AKG_SpeedTrack_PtTrack + PLY_AKG_Offset1b),hl
ENDIF ;PLY_CFG_UseSpeedTracks
IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC
pop hl
ld (PLY_AKG_EventTrack_PtTrack + PLY_AKG_Offset1b),hl
ENDIF ;PLY_CFG_UseEventTracks
ENDIF ;PLY_AKG_UseSpecialTracks
xor a
;Forces the reading of every Track and Special Track.
IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC
ld (PLY_AKG_SpeedTrack_WaitCounter + PLY_AKG_Offset1b),a
ENDIF ;PLY_CFG_UseSpeedTracks
IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC
ld (PLY_AKG_EventTrack_WaitCounter + PLY_AKG_Offset1b),a
ENDIF ;PLY_CFG_UseEventTracks
ld (PLY_AKG_Channel1_WaitCounter + PLY_AKG_Offset1b),a
ld (PLY_AKG_Channel2_WaitCounter + PLY_AKG_Offset1b),a
ld (PLY_AKG_Channel3_WaitCounter + PLY_AKG_Offset1b),a
ld a,c
PLY_AKG_SetCurrentLineBeforeReadLine:
ld (PLY_AKG_PatternDecreasingHeight + PLY_AKG_Offset1b),a
;Reads the new line (notes, effects, Special Tracks, etc.).
PLY_AKG_ReadLine:
;Reads the Speed Track.
IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC
;-------------------------------------------------------------------
IFNDEF PLY_AKG_Rom
PLY_AKG_SpeedTrack_WaitCounter: ld a,0 ;Lines to wait?
ELSE
ld a,(PLY_AKG_SpeedTrack_WaitCounter)
ENDIF
sub 1
jr nc,PLY_AKG_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.
IFNDEF PLY_AKG_Rom
dknr3 (void):
PLY_AKG_SpeedTrack_PtTrack: ld hl,0
ELSE
ld hl,(PLY_AKG_SpeedTrack_PtTrack)
ENDIF
ld a,(hl)
inc hl
srl a ;Bit 0: wait?
jr c,PLY_AKG_SpeedTrack_StorePointerAndWaitCounter ;Jump if wait: A is the wait value.
;Value found. If 0, escape value (rare).
jr nz,PLY_AKG_SpeedTrack_NormalValue
;Escape code. Reads the right value.
ld a,(hl)
inc hl
PLY_AKG_SpeedTrack_NormalValue:
ld (PLY_AKG_CurrentSpeed + PLY_AKG_Offset1b),a
xor a ;Next time, a new value is read.
PLY_AKG_SpeedTrack_StorePointerAndWaitCounter:
ld (PLY_AKG_SpeedTrack_PtTrack + PLY_AKG_Offset1b),hl
PLY_AKG_SpeedTrack_MustWait:
ld (PLY_AKG_SpeedTrack_WaitCounter + PLY_AKG_Offset1b),a
PLY_AKG_SpeedTrack_End:
ENDIF ;PLY_CFG_UseSpeedTracks
;Reads the Event Track.
;-------------------------------------------------------------------
IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC
IFNDEF PLY_AKG_Rom
PLY_AKG_EventTrack_WaitCounter: ld a,0 ;Lines to wait?
ELSE
ld a,(PLY_AKG_EventTrack_WaitCounter)
ENDIF
sub 1
jr nc,PLY_AKG_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.
IFNDEF PLY_AKG_Rom
dknr3 (void):
PLY_AKG_EventTrack_PtTrack: ld hl,0
ELSE
ld hl,(PLY_AKG_EventTrack_PtTrack)
ENDIF
ld a,(hl)
inc hl
srl a ;Bit 0: wait?
jr c,PLY_AKG_EventTrack_StorePointerAndWaitCounter ;Jump if wait: A is the wait value.
;Value found. If 0, escape value (rare).
jr nz,PLY_AKG_EventTrack_NormalValue
;Escape code. Reads the right value.
ld a,(hl)
inc hl
PLY_AKG_EventTrack_NormalValue:
ld (PLY_AKG_Event),a
xor a ;Next time, a new value is read.
PLY_AKG_EventTrack_StorePointerAndWaitCounter:
ld (PLY_AKG_EventTrack_PtTrack + PLY_AKG_Offset1b),hl
PLY_AKG_EventTrack_MustWait:
ld (PLY_AKG_EventTrack_WaitCounter + PLY_AKG_Offset1b),a
PLY_AKG_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 PLY_AKG_ReadTrack channelNumber
IFNDEF PLY_AKG_Rom
PLY_AKG_Channel{channelNumber}_WaitCounter: ld a,0 ;Lines to wait?
ELSE
ld a,(PLY_AKG_Channel{channelNumber}_WaitCounter)
ENDIF
sub 1
jr c,PLY_AKG_Channel{channelNumber}_ReadTrack
;Still some lines to wait.
ld (PLY_AKG_Channel{channelNumber}_WaitCounter + PLY_AKG_Offset1b),a
jp PLY_AKG_Channel{channelNumber}_ReadCellEnd
PLY_AKG_Channel{channelNumber}_ReadTrack:
IFNDEF PLY_AKG_Rom
dknr3 (void):
PLY_AKG_Channel{channelNumber}_PtTrack: ld hl,0 ;Points on the Cell to read.
ELSE
ld hl,(PLY_AKG_Channel{channelNumber}_PtTrack)
ENDIF
;Reads note data. It can be a note, a wait...
ld c,(hl) ;C = data (b5-0) + effect? (b6) + new Instrument? (b7).
inc hl
ld a,c
and %111111 ;A = data.
cp 60 ;0-59: note. "cp" is preferred to "sub" so that the "note" branch (the slowest) is note-ready.
jr c,PLY_AKG_Channel{channelNumber}_Note
sub 60
jp z,PLY_AKG_Channel{channelNumber}_MaybeEffects ;60 = no note, but maybe effects.
dec a
jr z,PLY_AKG_Channel{channelNumber}_Wait ;61 = wait, no effect.
dec a
jr z,PLY_AKG_Channel{channelNumber}_SmallWait ;62 = small wait, no effect.
;63 = escape code for note, maybe effects.
;Reads the note in the next byte (HL has already been incremented).
ld a,(hl)
inc hl
jr PLY_AKG_Channel{channelNumber}_AfterNoteKnown
;Small wait, no effect.
PLY_AKG_Channel{channelNumber}_SmallWait:
ld a,c ;Uses bit 6/7 to indicate how many lines to wait.
rlca
rlca
and %11
inc a ;This wait start at 2 lines, to 5.
ld (PLY_AKG_Channel{channelNumber}_WaitCounter + PLY_AKG_Offset1b),a
jr PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer
;Wait, no effect.
PLY_AKG_Channel{channelNumber}_Wait:
ld a,(hl) ;Reads the wait value on the next byte (HL has already been incremented).
ld (PLY_AKG_Channel{channelNumber}_WaitCounter + PLY_AKG_Offset1b),a
inc hl
jr PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer
;Little subcode put here, called just below. A bit dirty, but avoids long jump.
PLY_AKG_Channel{channelNumber}_SameInstrument:
;No new instrument. The instrument pointer must be reset.
IFNDEF PLY_AKG_Rom
dknr3 (void):
PLY_AKG_Channel{channelNumber}_PtBaseInstrument: ld de,0
ELSE
ld de,(PLY_AKG_Channel{channelNumber}_PtBaseInstrument)
ENDIF
ld (PLY_AKG_Channel{channelNumber}_PtInstrument + PLY_AKG_Offset1b),de
jr PLY_AKG_Channel{channelNumber}_AfterInstrument
;A note has been found, plus maybe an Instrument and effects. A = note. C = still has the New Instrument/Effects flags.
PLY_AKG_Channel{channelNumber}_Note:
if {channelNumber} == 1 ;Declares this only for the first channel, else refers to it.
IFNDEF PLY_AKG_Rom
PLY_AKG_BaseNoteIndex: add a,0 ;The encoded note is only from a 4 octave range, but the first note depends on he best window, determined by the song generator.
ELSE
ld b,a
ld a,(PLY_AKG_BaseNoteIndex + PLY_AKG_Offset1b)
add a,b
ENDIF
else
ld b,a
ld a,(PLY_AKG_BaseNoteIndex + PLY_AKG_Offset1b)
add a,b
endif
PLY_AKG_Channel{channelNumber}_AfterNoteKnown:
IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
IFNDEF PLY_AKG_Rom
PLY_AKG_Channel{channelNumber}_Transposition: add a,0 ;Adds the Track transposition.
ELSE
ld b,a
ld a,(PLY_AKG_Channel{channelNumber}_Transposition + PLY_AKG_Offset1b)
add a,b
ENDIF
ENDIF ;PLY_CFG_UseTranspositions
ld (PLY_AKG_Channel{channelNumber}_TrackNote + PLY_AKG_Offset1b),a
;HL = next data. C = data byte.
rl c ;New Instrument?
jr nc,PLY_AKG_Channel{channelNumber}_SameInstrument
;Gets the new Instrument.
ld a,(hl)
inc hl
exx
if {channelNumber} == 1 ;Declares this only for the first channel, else refers to it.
ld l,a
ld h,0
add hl,hl
IFNDEF PLY_AKG_Rom
dknr3 (void):
PLY_AKG_InstrumentsTable: ld de,0 ;Points on the Instruments table of the music (set on song initialization).
ELSE
ld de,(PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b)
ENDIF
add hl,de
else
ld e,a
ld d,0
ld hl,(PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b) ;Points on the Instruments table of the music (set on song initialization).
add hl,de
add hl,de
endif
ld sp,hl
pop hl
ld a,(hl) ;Gets the speed.
inc hl
;No need to store an "original speed" if "force instrument speed" effect is not used.
IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed ;CONFIG SPECIFIC