-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayer.asm
833 lines (774 loc) · 28.1 KB
/
player.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
ORG $4000
Start
;JMP TestModValue
JMP StartMusic ; ORG + 0
JMP KillMusic ; ORG + 3
JMP TogglePause ; ORG + 6
JMP JukeBoxStart ; ORG + 9
JMP JukeBoxPlay ; ORG + 12
JBPatternIndex .byte $00 ; ORG + 15
PlayerPaused .byte $00
; TestModValue
; LDA #8
; STA ValueToMod
; LDA #$80
; JSR ModifyValue
; LDA ValueToMod
; STA $8000
; LDA #8
; STA ValueToMod
; LDA #$07
; JSR ModifyValue
; LDA ValueToMod
; STA $8001
; RTS
; Queue track and start IRQ
JukeBoxStart
JSR SetPause
StartMusic
JSR MBInit
LDA #0
STA patternListIndex
STA patternNum
STA patternPos
STA patternTrack
JSR LoadPattern ; load first pattern
JSR SetupIRQ
RTS
; Kill music stops the IRQ etc
KillMusic
LDX #0
JSR VIADisableT1IRQ
JSR MBInit
LDA OldIRQPtr+0
STA $3FE
LDA OldIRQPtr+1
STA $3FF
LDX #$00
JSR VIAReset
LDX #$80
JSR VIAReset
RTS
ResetTicks
.byte $00
NextNoteTicks
.byte $01
TicksNeeded
.byte $02
CurrentTicks
.byte $00
TickInterval
.word 63780
HandleIRQ
PHA ; save A
TXA ; copy X
PHA ; save X
TYA ; copy Y
PHA ; save Y
IRQMain
LDA PlayerPaused ; if paused just exit the irq (no work done)
BEQ CheckTickReset
JMP ExitIRQ
CheckTickReset
LDA ResetTicks
BEQ HandleTick
JSR SetupIRQ
LDA #0
STA ResetTicks
LDX #0
JSR VIAUpdateTimer1
; we only do the main thing every TicksNeeded IRQ calls because
; of limitations of the counter registers
HandleTick
INC CurrentTicks
LDA CurrentTicks
CMP TicksNeeded
BNE CheckPlayNote
; reset the counter
LDA #0
STA CurrentTicks
JSR ActionAdvance
JMP ExitIRQ
CheckPlayNote
CMP NextNoteTicks
BNE ExitIRQ
JSR ActionPlay
; clean up and return from irq
ExitIRQ
JSR VIAACKTimer1IRQ
PLA ; pull Y
TAY ; restore it
PLA ; pull X
TAX ; restore it
PLA ; restore A
RTI ; this was an interrupt service so exit properly
OldIRQPtr .word $0000
SetupIRQ
; save old irq
LDA $3FE
STA OldIRQPtr+0
LDA $3FF
STA OldIRQPtr+1
; set new irq
LDA #<HandleIRQ
STA $3FE
LDA #>HandleIRQ
STA $3FF
LDA TickInterval+0
STA Timer1Counter+0
LDA TickInterval+1
STA Timer1Counter+1
LDX #0
JSR VIASetTimer1
RTS
ActionPlay
JSR PlayRow
RTS
ActionAdvance
JSR NextRow
RTS
; Pause / Unpause routine
TogglePause
LDA PlayerPaused
BEQ SetPause
LDA #0
STA PlayerPaused
RTS
SetPause
LDA #1
STA PlayerPaused
RTS
; JukeboxPlay
JukeBoxPlay
LDA JBPatternIndex
STA patternListIndex
LDA #0
STA patternPos
JSR LoadNewPattern
LDA #0
STA CurrentTicks
STA PlayerPaused
RTS
; core playing code here, references mock.asm for lower level subroutines
; CheckNoise will check and apply a noise period
; if required
CheckNoise LDY #2
LDA (noteAddrLo),Y
CMP #$FF
BEQ DoneNoise
JSR SetNoisePeriod
DoneNoise RTS
; CheckEnvelope will check and apply env params
; if set
CheckEnvelope LDY #9
LDA (noteAddrLo),Y
CMP #$FF
BEQ DoneEnvelope ; dont interpret bits if $FF present
AND #4
BEQ DoneEnvelope
LDY #4
LDA (noteAddrLo),Y
SetEnvelope JSR SetEnvPeriodCoarse
LDY #5
LDA (noteAddrLo),Y
JSR SetEnvPeriodFine
LDY #6
LDA (noteAddrLo),Y
CMP #$FF
BEQ DoneEnvelope
JSR SetEnvShape
DoneEnvelope RTS
; SoundNote
; Play note data pointed to noteAddrLo/noteAddrHi
; Send to chip $00/$01
; Send to channel noteChannel
SoundNote
LDX noteChip
; --
LDY #0
CheckTone LDA (noteAddrLo),Y
CMP #$FE ; handle XXX inline
BNE CheckEmpty
JSR DisableNoise
JSR DisableTone
JMP CheckCommand
CheckEmpty CMP #$FF
BEQ DoneTone
ApplyTone JSR SetTonePeriodCoarse
LDY #1
LDA (noteAddrLo),Y
JSR SetTonePeriodFine
; --
DoneTone JSR CheckNoise
; --
CheckVolume LDX noteChip
LDY #3
LDA (noteAddrLo),Y
CMP #$FF
BEQ CheckEnv
JSR SetVolume
; --
CheckEnv JSR CheckEnvelope
; --
LDY #9
LDA (noteAddrLo),Y
CMP #$FF
BEQ CheckCommand
AND #1
CMP #1
BNE ToneOff
JSR EnableTone
JMP CheckNoiseOn
ToneOff JSR DisableTone
; --
CheckNoiseOn LDY #9
LDA (noteAddrLo),Y
AND #2
CMP #2
BNE NoiseOff
JSR EnableNoise
JMP CheckCommand
NoiseOff JSR DisableNoise
CheckCommand ; handle certain commands
; JMP DoneCommands
LDY #7
LDA (noteAddrLo),Y
CMP #$FF
BEQ DoneCommands
;
CMP #'j'
BNE NotJ
JMP CommandJ
;
NotJ CMP #'b'
BNE NotB
JMP CommandB
;
NotB CMP #'v'
BNE NotV
JMP CommandV
;
NotV CMP #'f'
BNE NotF
JMP CommandF
;
NotF CMP #'x'
BNE NotX
JMP CommandX
;
NotX CMP #'w'
BNE NotW
JMP CommandW
;
NotW CMP #'s'
BNE NotS
JMP CommandS
;
NotS CMP #'y'
BNE NotY
JMP CommandY
;
NotY CMP #'z'
BNE NotZ
JMP CommandZ
;
NotZ CMP #'t'
BNE NotT
JMP CommandT
;
NotT CMP #'u'
BNE NotU
JMP CommandU
;
NotU CMP #'q'
BNE NotQ
JMP CommandQ
;
NotQ CMP #'r'
BNE NotR
JMP CommandR
;
NotR CMP #'e'
BNE NotE
JMP CommandE
;
NotE CMP #'n'
BNE NotN
JMP CommandN
;
NotN CMP #'c'
BNE NotC
JMP CommandC
;
NotC CMP #'p'
BNE NotP
JMP CommandP
;
NotP
DoneCommands
RTS
; Pxx handler
CommandP
LDY #8 ; read param
LDA (noteAddrLo),Y
CMP #2 ; 2 == pause music
BEQ CommandPPause
;
JSR KillMusic ; anything else stop music
RTS
CommandPPause
LDA #1 ; pause player
STA PlayerPaused
RTS
; Cxx handler
CommandC
JSR GetVolume
PHA
AND #$10
STA $D9
PLA
AND #$15
STA ValueToMod
LDY #8 ; read param
LDA (noteAddrLo),Y
JSR ModifyValue
LDA ValueToMod
CMP #$10
BMI CVolOk
LDA #$0f
CVolOk
ORA $D9
JSR SetVolume
RTS
; Exx handler
CommandE
LDY #8 ; read param
LDA (noteAddrLo),Y
BEQ ESetOff
ESetOn
JSR GetVolume
ORA #$10
JSR SetVolume
RTS
ESetOff
JSR GetVolume
AND #$0f
JSR SetVolume
RTS
; Nxx handler
CommandN
LDY #8 ; read param
LDA (noteAddrLo),Y
AND #31
JSR SetNoisePeriod
RTS
; Vxx handler
CommandV
LDY #8 ; read param
LDA (noteAddrLo),Y
AND #15
JSR SetEnvShape
RTS
; Jxx handler
CommandJ
LDY #8 ; read param
LDA (noteAddrLo),Y
; we want to advance to next track, with pattern
STA DestinationPatternRow
LDY patternListIndex
INY
STY DestinationPatternIndex
INC TimeCircuitsActivated
RTS
; Bxx handler
CommandB
LDY #8 ; read param
LDA (noteAddrLo),Y
; we want to advance to pattern nn in song
STA DestinationPatternIndex
LDA #0
STA DestinationPatternRow
INC TimeCircuitsActivated
RTS
; Fxx handler
CommandF
LDY #8 ; read param
LDA (noteAddrLo),Y
; setting tempo
JSR SetTempo
RTS
RestartEnvelope
LDA #PSGEnvShape
JSR MBRegSelect
JSR MBRegRead
PHA
LDA #PSGEnvShape
JSR MBRegSelect
PLA
JSR MBRegWrite
RTS
; Xxx handler
CommandX
LDY #8 ; read param
LDA (noteAddrLo),Y
; setting env fine
JSR SetEnvPeriodFine
JSR RestartEnvelope
RTS
; Wxx handler
CommandW
LDY #8 ; read param
LDA (noteAddrLo),Y
; setting env fine
JSR SetEnvPeriodCoarse
JSR RestartEnvelope
RTS
; Yxx handler
CommandY
; read env period coarse -> ValueToMod
LDA #PSGEnvPeriodCourse
JSR MBRegSelect
JSR MBRegRead
STA ValueToMod
;
LDY #8 ; read param
LDA (noteAddrLo),Y
JSR ModifyValue
; setting env param
LDA ValueToMod
JSR SetEnvPeriodCoarse
;JSR RestartEnvelope
RTS
; Zxx handler
CommandZ
; read env period fine -> ValueToMod
LDA #PSGEnvPeriodFine
JSR MBRegSelect
JSR MBRegRead
STA ValueToMod
;
LDY #8 ; read param
LDA (noteAddrLo),Y
JSR ModifyValue
LDA ValueToMod
; setting env param
JSR SetEnvPeriodFine
;JSR RestartEnvelope
RTS
; Qxx handler
CommandQ
; read env period coarse -> ValueToMod
LDA #PSGEnvPeriodCourse
JSR MBRegSelect
JSR MBRegRead
STA ValueToMod
;
LDY #8 ; read param
LDA (noteAddrLo),Y
JSR ModifyValue
; setting env param
LDA ValueToMod
JSR SetEnvPeriodCoarse
; now set fine
LDY #8 ; read param
LDA (noteAddrLo),Y
CMP #$0f
BPL QZeroSet
QFFSet
LDA #$FF
JSR SetEnvPeriodFine
RTS
QZeroSet
LDA #$00
JSR SetEnvPeriodFine
RTS
; Rxx handler
CommandR
; read env period coarse -> ValueToMod
JSR GetTonePeriodCoarse
STA ValueToMod
;
LDY #8 ; read param
LDA (noteAddrLo),Y
JSR ModifyValue
; setting env param
LDA ValueToMod
JSR SetTonePeriodCoarse
; now set fine
LDY #8 ; read param
LDA (noteAddrLo),Y
CMP #$0f
BPL RZeroSet
RFFSet
LDA #$FF
JSR SetTonePeriodFine
RTS
RZeroSet
LDA #$00
JSR SetTonePeriodFine
RTS
; Txx handler
CommandT
; read env period coarse -> ValueToMod
JSR GetTonePeriodCoarse
STA ValueToMod
;
LDY #8 ; read param
LDA (noteAddrLo),Y
JSR ModifyValue
; setting env param
LDA ValueToMod
JSR SetTonePeriodCoarse
RTS
; Uxx handler
CommandU
; read env period coarse -> ValueToMod
JSR GetTonePeriodFine
STA ValueToMod
;
LDY #8 ; read param
LDA (noteAddrLo),Y
JSR ModifyValue
LDA ValueToMod
; setting env param
JSR SetTonePeriodFine
RTS
CommandS
LDY #8 ; read param
LDA (noteAddrLo),Y
CMP #$00
BEQ SBothOff
CMP #$10
BEQ SOnlyTone
CMP #$01
BEQ SOnlyNoise
CMP #$11
BEQ SBothOn
RTS
SBothOff JSR DisableNoise
JSR DisableTone
RTS
SBothOn JSR EnableNoise
JSR EnableTone
RTS
SOnlyTone JSR DisableNoise
JSR EnableTone
RTS
SOnlyNoise JSR DisableTone
JSR EnableNoise
RTS
ValueToMod .byte $00
TempModParam .byte $00
ModifyValue ; A reg contains diff field
STA TempModParam
CheckModUp
LSR
LSR
LSR
LSR
AND #$0f
BEQ CheckModDown
; A contains modify up count
TAY
ModUpLoop
LDA ValueToMod
CMP #$FF
BEQ ModUpOk
INC ValueToMod
DEY
BNE ModUpLoop
ModUpOk
RTS
;;
CheckModDown LDA TempModParam
AND #$0f
BEQ ModDownOk
; mod down
TAY
ModDownLoop
LDA ValueToMod
CMP #$00
BEQ ModDownOk
DEC ValueToMod
DEY
BNE ModDownLoop
ModDownOk
RTS
; note player routine
noteAddrLo .equ $D0
noteAddrHi .equ $D1
noteChip .byte $0
noteChannel .byte $0
; tracker state
patternListIndex .byte $0 ; postion in pattern list
patternNum .byte $0
patternPos .byte $0
patternTrack .byte $0
trackNote .byte $0
; pointer to current pattern
patternPtr .equ $D2
patternPtrHi .equ $D3
trackPtr .equ $D2
trackPtrHi .equ $D3
noteTablePtr .equ $D4
noteTablePtrHi .equ $D5
; pointer to start of each track
trackPtr0 .word $0000
trackPtr1 .word $0000
trackPtr2 .word $0000
trackPtr3 .word $0000
trackPtr4 .word $0000
trackPtr5 .word $0000
noteTablePtr0 .word $0000
noteTablePtr1 .word $0000
noteTablePtr2 .word $0000
noteTablePtr3 .word $0000
noteTablePtr4 .word $0000
noteTablePtr5 .word $0000
LoadPattern LDX patternListIndex
LDA SONGADDR,X
STA patternNum ; current pattern id
CLC
ROL
TAX
LDA PATTADDR,X ; set patternPtr to address of pattern
STA patternPtr
LDA PATTADDR+1,X
STA patternPtr+1
LDY #0
CopyTPLoop LDA (patternPtr),Y
STA trackPtr0,Y
INY
CPY #12
BNE CopyTPLoop
LDA #0 ; reset pattern pos
STA patternPos
RTS
; this routine will lookup and trigger a note in track
tempNotenum .byte $00
emptyCount .byte $00
PlayNote
; set trackPtr to current track,
LDA patternTrack
CLC
ROL
TAX
LDA trackPtr0,X
STA trackPtr
LDA trackPtr0+1,X
STA trackPtr+1
;
LDY patternPos ; index to note value
INY
INY ; add 2 to it because note table is trackPtr + 2
LDA (trackPtr),Y
CMP #$FF
BNE PlayNoteValid
INC emptyCount
RTS
PlayNoteValid ; current note is a valid note index
; we need to get the pointer to the note data
; and put it in noteAddrLo,noteAddrHi
PHA ; store note number for later
LDY #0
LDA (trackPtr),Y
STA noteTablePtr
INY
LDA (trackPtr),Y
STA noteTablePtrHi
PLA
CLC
ROL
TAY
LDA (noteTablePtr),Y
STA noteAddrLo
INY
LDA (noteTablePtr),Y
STA noteAddrHi
JSR SoundNote
RTS
PlayRow LDA #0
STA emptyCount
LDX #0 ; play current row
STX noteChip
LDA #0
STA noteChannel
STA patternTrack
JSR PlayNote
;
INC noteChannel
INC patternTrack
JSR PlayNote
;
INC noteChannel
INC patternTrack
JSR PlayNote
;
LDX #$80
STX noteChip
LDA #0
STA noteChannel
LDA #3
STA patternTrack
JSR PlayNote
;
INC patternTrack
INC noteChannel
JSR PlayNote
;
INC patternTrack
INC noteChannel
JSR PlayNote
;
RTS
skipAdvance .byte $00
;
TimeCircuitsActivated .byte $00
DestinationPatternIndex .byte $00
DestinationPatternRow .byte $00
NextRow
LDA TimeCircuitsActivated
BEQ NextRowOk
; something has requested a time jump in the song, so
; do it and exit...
LDA #0
STA TimeCircuitsActivated
LDA DestinationPatternIndex
STA patternListIndex
JSR LoadNewPattern
LDA DestinationPatternRow
STA patternPos
RTS
NextRowOk
INC patternPos
LDA patternPos
CMP #$40
BEQ NextPattern
RTS
NextPattern LDA #0
STA patternPos
INC patternListIndex
LoadNewPattern LDX patternListIndex
LDA SONGADDR,X
CMP #$FF
BEQ LoopPatternList
JSR LoadPattern
RTS
LoopPatternList LDX #0
STX patternListIndex
JSR LoadPattern
; JSR MBInit
RTS
StartSong JSR MBInit
LDA #0
STA patternListIndex
STA patternNum
STA patternPos
STA patternTrack
JSR LoadPattern ; load first pattern
RTS
; rest of the includes
.include via
.include mock
.include tempo
.include song