-
Notifications
You must be signed in to change notification settings - Fork 4
/
pentlymusic.s
1549 lines (1407 loc) · 34.6 KB
/
pentlymusic.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
;
; Pently audio engine
; Music interpreter and instrument renderer
;
; Copyright 2009-2018 Damian Yerrick
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Permission is granted to anyone to use this software for any purpose,
; including commercial applications, and to alter it and redistribute it
; freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must not
; claim that you wrote the original software. If you use this software
; in a product, an acknowledgment in the product documentation would be
; appreciated but is not required.
; 2. Altered source versions must be plainly marked as such, and must not be
; misrepresented as being the original software.
; 3. This notice may not be removed or altered from any source distribution.
;
.include "pentlyseq.inc"
.include "pently.inc"
.include "../obj/nes/pentlybss.inc"
.importzp pently_zp_state
.import pentlyBSS
.import periodTableLo, periodTableHi
.export pentlyi_update_music, pentlyi_update_music_ch
.if PENTLY_USE_ATTACK_PHASE && PENTLY_USE_NOISE_POOLING
.export pentlyi_attackPitch, pentlyi_attackLen
.exportzp pentlyi_noteAttackPos
.endif
.if PENTLY_USE_PAL_ADJUST
.importzp tvSystem
.endif
.if PENTLY_USE_ROW_CALLBACK
.import pently_row_callback, pently_dalsegno_callback
.endif
PENTLY_NUM_CHANNELS = 4
PENTLY_DRUM_TRACK = 12
PENTLY_ATTACK_TRACK = 16
PENTLY_MAX_CHANNEL_VOLUME = 4
PENTLY_INITIAL_TEMPO = 300
PENTLY_INITIAL_ROW_LENGTH = 4
PENTLY_MAX_TEMPO_SCALE = 8
.if PENTLY_USE_ATTACK_TRACK
PENTLY_LAST_TRACK = PENTLY_ATTACK_TRACK
.else
PENTLY_LAST_TRACK = PENTLY_DRUM_TRACK
.endif
PENTLY_USE_TEMPO_ROUNDING = PENTLY_USE_TEMPO_ROUNDING_SEGNO || (PENTLY_USE_TEMPO_ROUNDING_PLAY_CH >= 0) || PENTLY_USE_TEMPO_ROUNDING_BEAT
; pently_zp_state (PENTLY_USE_ATTACK_PHASE = 1)
; +0 +1 +2 +3
; 0 | Sq1 sound effect data ptr Sq1 music pattern data ptr
; 4 | Sq2 sound effect data ptr Sq2 music pattern data ptr
; 8 | Tri sound effect data ptr Tri music pattern data ptr
; 12 | Noise sound effect data ptr Noise music pattern data ptr
; 16 | Sq1 envelope data ptr Attack music pattern data ptr
; 20 | Sq2 envelope data ptr Conductor track position
; 24 | Tri envelope data ptr Tempo counter
; 28 | Noise envelope data ptr Play/Pause Attack channel
;
; pently_zp_state (PENTLY_USE_ATTACK_PHASE = 0)
; +0 +1 +2 +3
; 0 | Sq1 sound effect data ptr Sq1 music pattern data ptr
; 4 | Sq2 sound effect data ptr Sq2 music pattern data ptr
; 8 | Tri sound effect data ptr Tri music pattern data ptr
; 12 | Noise sound effect data ptr Noise music pattern data ptr
; 16 | Conductor track position Tempo counter
; 20 | Play/Pause
.zeropage
; ASM6 translation quirk:
; The delay_labels mechanism to reduce forward references to
; pently_zp_state and pentlymusicbase does not work correctly
; if the references are in a .if block. So move them to
; .zeropage so that delay_labels will not touch them.
pentlyi_chnPatternPos = pently_zp_state + 2
.if PENTLY_USE_ATTACK_PHASE
pentlyi_noteAttackPos = pently_zp_state + 16
pentlyi_conductorPos = pently_zp_state + 22
pently_tempoCounterLo = pently_zp_state + 26
pently_tempoCounterHi = pently_zp_state + 27
pently_music_playing = pently_zp_state + 30
pentlyi_attackChn = pently_zp_state + 31
.else
pentlyi_conductorPos = pently_zp_state + 16
pently_tempoCounterLo = pently_zp_state + 18
pently_tempoCounterHi = pently_zp_state + 19
pently_music_playing = pently_zp_state + 20
.endif
.bss
; Statically allocated so as not to be cleared by the clear loop
pentlyi_conductorSegnoLo = pentlyBSS + 16
pentlyi_conductorSegnoHi = pentlyBSS + 17
; The rest is allocated by pentlybss.py
; Noise envelope is NOT unused. Conductor track cymbals use it.
pentlymusicbase: .res pentlymusicbase_size
; Visualize particular notes within a playing score
.if PENTLY_USE_ARPEGGIO || PENTLY_USE_ATTACK_TRACK
pently_vis_arpphase = pentlyi_arpPhase
.endif
.if PENTLY_USE_PORTAMENTO
pently_vis_note = pentlyi_notePitch
.else
pently_vis_note = pentlyi_chPitchHi
.endif
.segment PENTLY_RODATA
pentlymusic_rodata_start = *
FRAMES_PER_MINUTE_PAL = 3000
FRAMES_PER_MINUTE_NTSC = 3606
FRAMES_PER_MINUTE_GB = 3584 ; not used in NES port
FRAMES_PER_MINUTE_SGB = 3670 ; not used in NES port
pently_fpmLo:
.byt <FRAMES_PER_MINUTE_NTSC, <FRAMES_PER_MINUTE_PAL, <FRAMES_PER_MINUTE_PAL
pently_fpmHi:
.byt >FRAMES_PER_MINUTE_NTSC, >FRAMES_PER_MINUTE_PAL, >FRAMES_PER_MINUTE_PAL
pentlyi_silent_pattern: ; a pattern consisting of a single whole rest
.byt 26*8+7, 255
pentlyi_durations:
.byt 1, 2, 3, 4, 6, 8, 12, 16
.if PENTLY_USE_VIBRATO
PENTLY_PREVIBRATO_PERIOD = 11
PENTLY_VIBRATO_PERIOD = 12
; bit 7: negate; bits 6-0: amplitude in units of 1/128 semitone
pentlyi_vibrato_pattern:
.byt $88,$8B,$8C,$8B,$88,$00,$08,$0B,$0C,$0B,$08
.endif
.if PENTLY_USE_PORTAMENTO
pentlyi_porta1x_rates_lo:
.byte 4, 8, 12, 16, 24, 32, 48, 64, 96, 128, 128
pentlyi_porta1x_rates_hi:
.byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
.endif
.segment PENTLY_CODE
pentlymusic_code_start = *
.proc pently_start_music
; Fetch initial conductor track position (limit 128 songs)
asl a
tax
lda pently_songs,x
sta pentlyi_conductorPos
sta pentlyi_conductorSegnoLo
lda pently_songs+1,x
sta pentlyi_conductorPos+1
sta pentlyi_conductorSegnoHi
; Clear all music state except that shared with sound effects
ldy #pentlymusicbase_size - 1
lda #0
.if ::PENTLY_USE_ATTACK_TRACK
sta pentlyi_attackChn
.endif
:
sta pentlymusicbase,y
dey
bpl :-
; Init each track's volume and play silent pattern
ldx #PENTLY_LAST_TRACK
channelLoop:
lda #<pentlyi_silent_pattern
sta pentlyi_chnPatternPos,x
lda #>pentlyi_silent_pattern
sta pentlyi_chnPatternPos+1,x
tya ; Y is $FF from the clear everything loop
sta pentlyi_musicPattern,x
.if ::PENTLY_USE_CHANNEL_VOLUME
.if ::PENTLY_USE_ATTACK_TRACK
cpx #PENTLY_ATTACK_TRACK
bcs :+
.endif
lda #PENTLY_MAX_CHANNEL_VOLUME
sta pentlyi_chVolScale,x
:
.endif
dex
dex
dex
dex
bpl channelLoop
; Set tempo upcounter and beat part to wrap around
; the first time they are incremented
lda #$FF
sta pently_tempoCounterLo
sta pently_tempoCounterHi
.if ::PENTLY_USE_BPMMATH
sta pently_row_beat_part
lda #PENTLY_INITIAL_ROW_LENGTH
sta pently_rows_per_beat
.endif
lda #<PENTLY_INITIAL_TEMPO
sta pentlyi_tempoLo
lda #>PENTLY_INITIAL_TEMPO
sta pentlyi_tempoHi
; Fall through
.endproc
.proc pently_resume_music
lda #1
; Fall through
.endproc
.proc pently_set_music_playing
sta pently_music_playing
rts
.endproc
.proc pently_stop_music
lda #0
beq pently_set_music_playing
.endproc
.proc pentlyi_update_music
lda pently_music_playing
beq music_not_playing
; This applies Bresenham's algorithm to tick generation: add
; rows per minute every frame, then subtract frames per minute
; when it overflows. But
.if ::PENTLY_USE_REHEARSAL
scaled_tempoHi = pently_zptemp + 0
lda pentlyi_tempoHi
sta scaled_tempoHi
lda pentlyi_tempoLo
ldx pently_tempo_scale
clc ; allow have_scaled_tempoLo to round
beq have_scaled_tempoLo
cpx #PENTLY_MAX_TEMPO_SCALE
bcs music_not_playing
shiftLoop:
lsr scaled_tempoHi
ror a
dex
bne shiftLoop
have_scaled_tempoLo:
adc pently_tempoCounterLo
sta pently_tempoCounterLo
lda scaled_tempoHi
.else
lda pentlyi_tempoLo
clc
adc pently_tempoCounterLo
sta pently_tempoCounterLo
lda pentlyi_tempoHi
.endif
adc pently_tempoCounterHi
sta pently_tempoCounterHi
bcs pentlyi_next_row
music_not_playing:
rts
.endproc
; Conductor reading ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.proc pentlyi_next_row
; Subtract tempo
.if ::PENTLY_USE_PAL_ADJUST
ldy tvSystem
.else
ldy #0
.endif
; sec ; carry was set by bcs in pentlyi_update_music
lda pently_tempoCounterLo
sbc pently_fpmLo,y
sta pently_tempoCounterLo
lda pently_tempoCounterHi
sbc pently_fpmHi,y
sta pently_tempoCounterHi
.if ::PENTLY_USE_REHEARSAL
inc pently_rowslo
bne :+
inc pently_rowshi
:
.endif
.if ::PENTLY_USE_BPMMATH
; Update row
ldy pently_row_beat_part
iny
cpy pently_rows_per_beat
bcc :+
.if ::PENTLY_USE_TEMPO_ROUNDING_BEAT
jsr pentlyi_round_to_beat
.endif
ldy #0
:
sty pently_row_beat_part
.endif
.if ::PENTLY_USE_ROW_CALLBACK
jsr pently_row_callback
.endif
; If in middle of waitRows, don't process conductor
lda pentlyi_songWaitRows
beq doConductor
dec pentlyi_songWaitRows
jmp pentlyi_read_patterns
doConductor:
ldy #0
lda (pentlyi_conductorPos),y
inc pentlyi_conductorPos
bne :+
inc pentlyi_conductorPos+1
:
cmp #PENTLY_CON_WAITROWS
beq is_waitrows
bcs not_playpat
; 00-07 pp tt ii: Play pattern pp on track A & $07,
; transposed up tt semitones, with instrument ii
and #$07
asl a
asl a
tax
.if ::PENTLY_USE_TEMPO_ROUNDING_PLAY_CH >= 0
cpx #PENTLY_USE_TEMPO_ROUNDING_PLAY_CH
bne not_round_ch
jsr pentlyi_round_to_beat
ldx #PENTLY_USE_TEMPO_ROUNDING_PLAY_CH
ldy #0
not_round_ch:
.endif
lda #0
cpx #PENTLY_ATTACK_TRACK
; If attack track is enabled, don't enable legato on attack
; track. Otherwise, don't start patterns on attack track at all
; because another variable overlaps it.
.if ::PENTLY_USE_ATTACK_TRACK
bcs skipClearLegato
.else
bcs skip3conductor
.endif
sta pentlyi_noteLegato,x ; start all patterns with legato off
skipClearLegato:
lda (pentlyi_conductorPos),y
sta pentlyi_musicPattern,x
iny
lda (pentlyi_conductorPos),y
sta pentlyi_chBaseNote,x
iny
lda (pentlyi_conductorPos),y
sta pentlyi_instrument,x
jsr pentlyi_start_pattern
skip3conductor:
lda #3
clc
adc pentlyi_conductorPos
sta pentlyi_conductorPos
bcc :+
inc pentlyi_conductorPos+1
:
jmp doConductor
is_waitrows:
; 20 ww: Wait ww+1 rows
lda (pentlyi_conductorPos),y
sta pentlyi_songWaitRows
inc pentlyi_conductorPos
bne :+
inc pentlyi_conductorPos+1
:
jmp pentlyi_read_patterns
not_playpat:
; Loop control block 21-23
cmp #PENTLY_CON_DALSEGNO
beq is_dalsegno
bcs not_loopcontrol
cmp #PENTLY_CON_SEGNO
bcs is_segno
; 21: Fine (end playback; set playing and tempo to 0)
lda #0
sta pently_music_playing
sta pentlyi_tempoHi
sta pentlyi_tempoLo
.if ::PENTLY_USE_ROW_CALLBACK
clc
jmp pently_dalsegno_callback
.else
rts
.endif
is_segno:
lda pentlyi_conductorPos
sta pentlyi_conductorSegnoLo
lda pentlyi_conductorPos+1
sta pentlyi_conductorSegnoHi
jmp segno_round
is_dalsegno:
lda pentlyi_conductorSegnoLo
sta pentlyi_conductorPos
lda pentlyi_conductorSegnoHi
sta pentlyi_conductorPos+1
.if ::PENTLY_USE_ROW_CALLBACK
sec
jsr pently_dalsegno_callback
.endif
.if ::PENTLY_USE_TEMPO_ROUNDING_SEGNO
segno_round:
jsr pentlyi_round_to_beat
.else
segno_round = doConductor
.endif
jmp doConductor
not_loopcontrol:
cmp #PENTLY_CON_NOTEON
bcs not_setattack
; 24-26: Play attack track on channel A & $07
.if ::PENTLY_USE_ATTACK_TRACK
and #%00000011
asl a
asl a
sta pentlyi_attackChn
.endif
jmp doConductor
not_setattack:
cmp #PENTLY_CON_SETTEMPO
bcs not_noteon
; 28-2F nn ii: Play note nn with instrument ii on track A & $07
and #%00000011
asl a
asl a
tax
lda (pentlyi_conductorPos),y
inc pentlyi_conductorPos
bne :+
inc pentlyi_conductorPos+1
:
pha
lda (pentlyi_conductorPos),y
tay
pla
jsr pently_play_note
jmp skipConductorByte
not_noteon:
cmp #PENTLY_CON_SETBEAT
bcs not_tempo_change
; 30-37 tt: Set tempo to (A & $07) * 256 + tt
and #%00000111
sta pentlyi_tempoHi
lda (pentlyi_conductorPos),y
sta pentlyi_tempoLo
skipConductorByte:
inc pentlyi_conductorPos
bne :+
inc pentlyi_conductorPos+1
:
jmp doConductor
not_tempo_change:
cmp #PENTLY_CON_SETBEAT+8
bcs not_set_beat
.if ::PENTLY_USE_BPMMATH
and #%00000111
tay
lda pentlyi_durations,y
sta pently_rows_per_beat
ldy #0
sty pently_row_beat_part
.endif
not_set_beat:
jmp doConductor
.endproc
; Pattern reading ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.proc pentlyi_read_patterns
ldx #4 * (PENTLY_NUM_CHANNELS - 1)
channelLoop:
jsr pentlyi_read_pattern
dex
dex
dex
dex
bpl channelLoop
; Process attack track last so it can override a just played attack
.if ::PENTLY_USE_ATTACK_TRACK
ldx #PENTLY_ATTACK_TRACK
;jmp pentlyi_read_pattern
; (that's a fallthrough)
.else
rts
.endif
.endproc
.proc pentlyi_read_pattern
lda pentlyi_noteRowsLeft,x
beq anotherPatternByte
skipNote:
dec pentlyi_noteRowsLeft,x
rts
anotherPatternByte:
; Read one pattern byte. If it's a loop, restart the pattern.
lda (pentlyi_chnPatternPos,x)
cmp #PENTLY_PATEND
bne notStartPatternOver
jsr pentlyi_start_pattern
lda (pentlyi_chnPatternPos,x)
notStartPatternOver:
inc pentlyi_chnPatternPos,x
bne patternNotNewPage
inc pentlyi_chnPatternPos+1,x
patternNotNewPage:
cmp #PENTLY_INSTRUMENT
bcc isNoteCmd
sbc #PENTLY_INSTRUMENT
cmp #num_patcmdhandlers ; ignore invalid pattern commands
bcs anotherPatternByte
asl a
tay
lda patcmdhandlers+1,y
pha
lda patcmdhandlers,y
pha
rts
isNoteCmd:
; set the note's duration
pha
and #$07
tay
lda pentlyi_durations,y
sta pentlyi_noteRowsLeft,x
pla
.if ::PENTLY_USE_VARMIX
ldy pently_mute_track,x
bmi isKeyOff
.endif
lsr a
lsr a
lsr a
cmp #25
bcc isTransposedNote
beq notKeyOff
isKeyOff:
lda #0
.if ::PENTLY_USE_ATTACK_TRACK
cpx #PENTLY_ATTACK_TRACK
bcs notKeyOff
.endif
.if ::PENTLY_USE_ATTACK_PHASE
sta pentlyi_attackLen,x
.endif
sta pentlyi_sustainVol,x
notKeyOff:
jmp skipNote
isTransposedNote:
cpx #PENTLY_DRUM_TRACK
beq isDrumNote
clc
adc pentlyi_chBaseNote,x
ldy pentlyi_instrument,x
jsr pently_play_note
jmp skipNote
isDrumNote:
.if ::PENTLY_USE_VARMIX
ldy pently_mute_track+PENTLY_DRUM_TRACK
bmi skipNote
.endif
asl a
pha
tax
lda pently_drums,x
jsr pently_start_sound
pla
tax
lda pently_drums+1,x
bmi noSecondDrum
jsr pently_start_sound
noSecondDrum:
ldx #PENTLY_DRUM_TRACK
jmp skipNote
; Effect handlers
; The 6502 adds 2 to PC in JSR and 1 in RTS, so push minus 1.
; Each effect is called with carry clear and the effect number
; times 2 in Y.
patcmdhandlers:
.addr set_fx_instrument-1
.addr set_fx_arpeggio-1
.addr set_fx_legato-1
.addr set_fx_legato-1
.addr set_fx_transpose-1
.addr set_fx_grace-1
.addr set_fx_vibrato-1
.addr set_fx_ch_volume-1
.addr set_fx_portamento-1
.addr set_fx_portamento-1 ; Reserved for future use
.addr set_fx_fastarp-1
.addr set_fx_slowarp-1
num_patcmdhandlers = (* - patcmdhandlers) / 2
set_fx_instrument:
lda (pentlyi_chnPatternPos,x)
sta pentlyi_instrument,x
nextPatternByte:
inc pentlyi_chnPatternPos,x
bne :+
inc pentlyi_chnPatternPos+1,x
:
jmp anotherPatternByte
.if ::PENTLY_USE_ARPEGGIO
set_fx_arpeggio:
cpx #PENTLY_DRUM_TRACK
bcs :+
lda (pentlyi_chnPatternPos,x)
lsr a
lsr a
lsr a
lsr a
sta pentlyi_arpInterval1,x
lda (pentlyi_chnPatternPos,x)
and #$0F
sta pentlyi_arpInterval2,x
:
jmp nextPatternByte
set_fx_fastarp:
cpx #PENTLY_DRUM_TRACK
bcs :+
lda #%10111111
and pentlyi_arpPhase,x
sta pentlyi_arpPhase,x
:
jmp anotherPatternByte
set_fx_slowarp:
cpx #PENTLY_DRUM_TRACK
bcs :+
lda #%01000000
ora pentlyi_arpPhase,x
sta pentlyi_arpPhase,x
:
jmp anotherPatternByte
.else
set_fx_arpeggio = nextPatternByte
set_fx_fastarp = anotherPatternByte
set_fx_slowarp = anotherPatternByte
.endif
set_fx_legato:
cpx #PENTLY_DRUM_TRACK
bcs :+
tya
and #$02
sta pentlyi_noteLegato,x
:
jmp anotherPatternByte
set_fx_grace:
lda (pentlyi_chnPatternPos,x)
; Because grace note processing decrements before comparing to
; zero, 1 is treated the same as 0.
; 0: this row's pattern already read
; 1: will read this row's pattern this frame
; 2: will read this row's pattern next frame
; 3: will read this row's pattern 2 frames from now
clc
adc #1
sta pentlyi_graceTime,x
jmp nextPatternByte
set_fx_transpose:
lda pentlyi_chBaseNote,x
adc (pentlyi_chnPatternPos,x)
sta pentlyi_chBaseNote,x
jmp nextPatternByte
.if ::PENTLY_USE_VIBRATO
set_fx_vibrato:
cpx #PENTLY_DRUM_TRACK
bcs :+
lda (pentlyi_chnPatternPos,x)
and #$07
sta pentlyi_vibratoDepth,x
:
jmp nextPatternByte
.else
set_fx_vibrato = nextPatternByte
.endif
.if ::PENTLY_USE_PORTAMENTO
set_fx_portamento:
cpx #PENTLY_DRUM_TRACK
bcs :+
lda (pentlyi_chnPatternPos,x)
sta pentlyi_chPortamento,x
:
jmp nextPatternByte
.else
set_fx_portamento = nextPatternByte
.endif
.if ::PENTLY_USE_CHANNEL_VOLUME
set_fx_ch_volume:
.if ::PENTLY_USE_ATTACK_TRACK
; Channel volume has no effect on attack track, yet it should be
; explicitly ignored so that if attack and pulse share a pattern,
; it doesn't clobber some other variable.
cpx #PENTLY_ATTACK_TRACK
bcs :+
.endif
lda (pentlyi_chnPatternPos,x)
sta pentlyi_chVolScale,x
:
jmp nextPatternByte
.else
set_fx_ch_volume = nextPatternByte
.endif
.endproc
.proc pentlyi_start_pattern
lda #0
sta pentlyi_graceTime,x
sta pentlyi_noteRowsLeft,x
lda pentlyi_musicPattern,x
cmp #255
bcc @notSilentPattern
lda #<pentlyi_silent_pattern
sta pentlyi_chnPatternPos,x
lda #>pentlyi_silent_pattern
sta pentlyi_chnPatternPos+1,x
rts
@notSilentPattern:
asl a
tay
bcc @isLoPattern
lda pently_patterns+256,y
sta pentlyi_chnPatternPos,x
lda pently_patterns+257,y
sta pentlyi_chnPatternPos+1,x
rts
@isLoPattern:
lda pently_patterns,y
sta pentlyi_chnPatternPos,x
lda pently_patterns+1,y
sta pentlyi_chnPatternPos+1,x
rts
.endproc
.if PENTLY_USE_REHEARSAL
; Known bug: Tracks with GRACE effects may fall behind
.proc pentlyi_skip_to_row_nz
pha
txa
pha
; Fake out grace processing
ldx #PENTLY_LAST_TRACK
graceloop:
lda pentlyi_graceTime,x
beq noGraceThisCh
lda #0
sta pentlyi_graceTime,x
jsr pentlyi_read_pattern
jmp graceloop
noGraceThisCh:
dex
dex
dex
dex
bpl graceloop
lda #0
sta pently_tempoCounterHi
sta pently_tempoCounterLo
jsr pentlyi_next_row
pla
tax
pla
; fall through to version that handles zero arguments correctly
.endproc
.proc pently_skip_to_row
cpx pently_rowshi
bne pentlyi_skip_to_row_nz
cmp pently_rowslo
bne pentlyi_skip_to_row_nz
; When seeking, kill cymbals because they tend to leave an
; envelope hanging
lda #0
sta pentlyi_sustainVol+PENTLY_DRUM_TRACK
rts
.endproc
.endif
; Playing notes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
; Plays note A on channel X (0, 4, 8, 12) with instrument Y.
; Trashes ZP temp 0-1 and preserves X.
.proc pently_play_note
notenum = pently_zptemp + 0
instrument_id = pently_zptemp + 1
sta notenum
sty instrument_id
; 5 bytes per instrument
tya
asl a
asl a
adc instrument_id
tay
; at this point:
; x = channel #
; y = offset in instrument table
.if ::PENTLY_USE_ATTACK_TRACK
cpx #PENTLY_ATTACK_TRACK
bcs skipSustainPart
lda pentlyi_arpPhase,x ; bit 7 set if attack is injected
bmi dont_legato_injected_attack
lda notenum
sta pentlyi_attackPitch,x
.endif
dont_legato_injected_attack:
lda notenum
.if ::PENTLY_USE_PORTAMENTO
cpx #PENTLY_DRUM_TRACK
bcs bypass_notePitch
sta pentlyi_notePitch,x
bcc pitch_is_stored
bypass_notePitch:
.endif
sta pentlyi_chPitchHi,x
pitch_is_stored:
lda pentlyi_noteLegato,x
bne skipAttackPart
lda instrument_id
sta pentlyi_instrument,x
lda pently_instruments,y
asl a
asl a
asl a
asl a
ora #$0C
sta pentlyi_sustainVol,x
cpx #PENTLY_DRUM_TRACK
bcs skipSustainPart
.if ::PENTLY_USE_ARPEGGIO
lda #%01000000
and pentlyi_arpPhase,x
sta pentlyi_arpPhase,x ; keep only bit 6: is arp fast
.elseif ::PENTLY_USE_ATTACK_TRACK
lda #0
sta pentlyi_arpPhase,x
.endif
.if ::PENTLY_USE_VIBRATO
lda #PENTLY_VIBRATO_PERIOD + PENTLY_PREVIBRATO_PERIOD
sta pentlyi_vibratoPhase,x
.endif
skipSustainPart:
.if ::PENTLY_USE_ATTACK_PHASE
lda pently_instruments+4,y
beq skipAttackPart
txa
pha
.if ::PENTLY_USE_ATTACK_TRACK
cpx #PENTLY_ATTACK_TRACK
bcc notAttackTrack
ldx pentlyi_attackChn
lda #$80 ; Disable arpeggio, vibrato, and legato until sustain
ora pentlyi_arpPhase,x
sta pentlyi_arpPhase,x
notAttackTrack:
lda notenum
sta pentlyi_attackPitch,x
.endif
lda pently_instruments+4,y
sta pentlyi_noteAttackPos+1,x
lda pently_instruments+3,y
sta pentlyi_noteAttackPos,x
lda pently_instruments+2,y
and #$7F
sta pentlyi_attackLen,x
pla
tax
.endif
skipAttackPart:
; Fall through
.endproc
.proc pentlyi_play_note_rts
rts
.endproc
; The mixer (in pentlysound.s) expects output to be stored in
; zero page temporaries 2, 3, and 4.
xsave = pently_zptemp + 0
out_volume = pently_zptemp + 2
out_pitch = pently_zptemp + 3
out_pitchadd = pently_zptemp + 4
;;
; Calculates the pitch, detune amount, and volume for channel X.
; @return out_volume: value for $4000/$4004/$4008/$400C
; out_pitch: semitone number
; out_pitchadd: amount to add to semitone
; X: preserved
.proc pentlyi_update_music_ch
lda pently_music_playing
bne :+
jmp pentlyi_set_ch_silent
:
lda pentlyi_graceTime,x
beq nograce
dec pentlyi_graceTime,x
bne nograce
jsr pentlyi_read_pattern
nograce:
.if ::PENTLY_USE_ATTACK_TRACK
cpx #PENTLY_ATTACK_TRACK
bcs pentlyi_play_note_rts
.endif
.if ::PENTLY_USE_VIS
lda #0
sta pently_vis_pitchlo,x
.endif
.if ::PENTLY_USE_PORTAMENTO
cpx #PENTLY_DRUM_TRACK
bcs no_pitch_no_porta
jsr pentlyi_calc_portamento
no_pitch_no_porta:
.endif
.if ::PENTLY_USE_ATTACK_PHASE
; Handle attack phase of envelope
lda pentlyi_attackLen,x
beq pentlyi_calc_sustain
dec pentlyi_attackLen,x
lda (pentlyi_noteAttackPos,x)
inc pentlyi_noteAttackPos,x
bne :+
inc pentlyi_noteAttackPos+1,x
:
.if ::PENTLY_USE_CHANNEL_VOLUME
jsr pentlyi_scale_volume
.else
sta out_volume
.endif
ldy pentlyi_chPitchHi,x
.if ::PENTLY_USE_PORTAMENTO
; Use portamento pitch if not injected
cpx #PENTLY_DRUM_TRACK
bcs attack_not_pitched_ch
.if ::PENTLY_USE_ATTACK_TRACK
lda pentlyi_arpPhase,x
bpl porta_not_injected
.endif
attack_not_pitched_ch:
.endif
.if ::PENTLY_USE_ATTACK_TRACK
ldy pentlyi_attackPitch,x
.endif
porta_not_injected:
; X=instrument, Y=pitch (before adding arp env)
; Read saved duty/ctrl/volume byte from attack envelope
lda out_volume