-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcdremote.s
2761 lines (2412 loc) · 157 KB
/
cdremote.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
.setcpu "65c02"
.org $4000
ZP1 := $19
ZP2 := $1B
ZP3 := $1D
PlayedListPtr := $1F
COUT := $FDED
MLIEntry := $BF00
Kbd := $C000
KbdStrobe := $C010
SetGraphics := $C050
SetFullScreen := $C052
SetPage1 := $C054
SetHiRes := $C057
OpenApple := $C061
CardROMByte := $C5FF
; Save the ZP values for the locations we're going to use
MAIN: lda ZP1
pha
lda ZP1 + 1
pha
lda ZP2
pha
lda ZP2 + 1
pha
lda ZP3
pha
lda ZP3 + 1
pha
; "Null" out T/M/S values
lda #$aa
sta BCDRelTrack
sta BCDRelMinutes
sta BCDRelSeconds
; Locate an Apple SCSI card and CDSC/CDSC+ drive
jsr FindHardware
bcs EXIT
; Application setup
jsr InitializeScreen
jsr InitDriveAndDisc
jsr InitPlayedList
; Do all the things!
jsr MainLoop
; Restore the saved ZP values and call MLI QUIT
EXIT: pla
sta ZP1
pla
sta ZP1 + 1
pla
sta ZP2
pla
sta ZP2 + 1
pla
sta ZP3
pla
sta ZP3 + 1
jsr MLIQUIT
rts
; Is drive online?
InitDriveAndDisc: jsr StatusDrive
bcs ExitInitDriveDisc
; Read the TOC for Track numbers - TOC VALUES ARE BCD!!
jsr C27ReadTOC
sed
clc
lda BCDLastTrackTOC
sbc BCDFirstTrackTOC
adc #$01
sta BCDTrackCount0Base
cld
jsr C24AudioStatus
lda SPBuffer
; Audio Status = $00 (currently playing)
beq IDDPlaying
dec a
; Audio status = $01 (currently paused)
beq IDDPaused
; Audio status = anything else - stop operation explicitly
jsr DoStopAction
bra ExitInitDriveDisc
; Set Pause button to active
IDDPaused: dec PauseButtonState
jsr ToggleUIPauseButton
; Read the current disc position and update the Track and Time display
jsr C28ReadQSubcode
jsr DrawTrack
jsr DrawTime
; Set Play button to active
IDDPlaying: dec PlayButtonState
; Set drive playing flag to true
dec DrivePlayingFlag
jsr ToggleUIPlayButton
bra ExitInitDriveDisc
ExitInitDriveDisc: rts
; Set Full-Screen HGR Page 1
InitializeScreen: lda SetFullScreen
lda SetGraphics
lda SetHiRes
lda SetPage1
; Clear screen to all white, initialize the GUI elements
jsr ClearHGR1toWhite
jsr PaintCDRemoteUI
jsr PaintCDRemoteMenu
jsr DrawTrack
jsr DrawTime
rts
; This is the start of where all the real action takes place
MainLoop: lda TOCInvalidFlag
; Have we read in a valid, usable TOC from an audio CD?
beq TOCisValid
; We don't have a valid TOC - poll the drive to see if it's online so we can try to read a TOC
jsr StatusDrive
; No - drive is offline, go check for user input instead
bcs NoFurtherBGAction
; Yes - make another attempt at reading a TOC
jsr ReReadTOC
; Re-check the status of the drive
TOCisValid: jsr StatusDrive
lda DrivePlayingFlag
; Drive is not currently playing audio, go check for user input
beq NoFurtherBGAction
; Drive is playing audio, watch for an AudioStatus return code of $03 = "Play operation complete"
jsr C24AudioStatus
lda SPBuffer
cmp #$03
; Audio playback operation is not complete
bne StillPlaying
; Deal with reaching the end of a playback operation. It's complicated. :)
jsr PlayBackComplete
; Go read the QSubcode channel and update the Track & Time display
StillPlaying: jsr C28ReadQSubcode
bcs NoFurtherBGAction
jsr DrawTrack
jsr DrawTime
; Read and process any Keyboard inputs from the user
NoFurtherBGAction: jsr GetKeypress
bcs MainLoop
; $51 = Q (Quit)
cmp #$51
bne NotQ
jmp DoQuitAction
; $1B = ESC (Quit)
NotQ: cmp #$1b
bne NotESC
jmp DoQuitAction
; $4C = L (Continuous Play)
NotESC: cmp #$4c
bne NotL
jsr ToggleLoopMode
jmp MainLoop
; $52 = R (Random Play)
NotL: cmp #$52
bne NotR
jsr ToggleRandomMode
jmp MainLoop
; All other key operations (Play/Stop/Pause/Next/Prev/Eject) require an online drive/disc, so check one more time
NotR: jsr StatusDrive
bcs MainLoop
; Loop falls through to here only if the drive is online
pha
lda TOCInvalidFlag
; Valid TOC has been read, we can skip the re-read
beq SkipTOCRead
jsr ReReadTOC
SkipTOCRead: pla
; $50 = P (Play)
cmp #$50
bne NotP
jsr DoPlayAction
jmp MainLoop
; $53 = S (Stop)
NotP: cmp #$53
bne NotS
jsr DoStopAction
jmp MainLoop
; $20 = Space (Pause)
NotS: cmp #$20
bne NotSpace
jsr DoPauseAction
jmp MainLoop
; $08 = ^H, LA (Previous Track, Scan Backward)
NotSpace: cmp #$08
bne NotCtrlH
lda OpenApple
bpl JustLeftArrow
OA_LeftArrow: jsr DoScanBackAction
jmp MainLoop
JustLeftArrow: jsr DoPrevTrackAction
jmp MainLoop
; $15 = ^U, RA (Next Track/Scan Forward)
NotCtrlH: cmp #$15
bne NotCtrlU
lda OpenApple
bpl JustRightArrow
OA_RightArrow: jsr DoScanFwdAction
jmp MainLoop
JustRightArrow: jsr DoNextTrackAction
jmp MainLoop
; $45 = E (Eject)
NotCtrlU: cmp #$45
bne UnsupportedKeypress
jsr C26Eject
UnsupportedKeypress:jmp MainLoop
DoQuitAction: rts
PlayBackComplete: lda RandomButtonState
; Random button is inactive - the entire Disc has been played to the end
beq PBCRandomIsInactive
; Random button is active - handle rollover to next random track
lda PlayButtonState
; Play button is inactive - bail out, there's nothing to do
beq ExitPBCHandler
; Increment the count of how many tracks have been played
inc HexPlayedCount0Base
lda HexPlayedCount0Base
cmp HexTrackCount0Base
; Haven't played all the tracks on the disc, so pick another one
bne PBCPlayARandomTrack
; All tracks have been played randomly - clear the Play button STATE to inactive (with no UI change) ...
lda #$00
sta PlayButtonState
; re-randomize from scratch ...
jsr RandomModeInit
; then reset the Play button STATE back to active (again with no UI change)
lda #$ff
sta PlayButtonState
lda LoopButtonState
; Loop button is active, so play the whole disc over again - start by picking a new track
bne PBCPlayARandomTrack
; Loop button is inactive - reset the first/last/current values to the TOC values and stop
lda BCDFirstTrackTOC
sta BCDFirstTrackNow
sta BCDRelTrack
lda BCDLastTrackTOC
sta BCDLastTrackNow
jsr DoStopAction
bra ExitPBCHandler
PBCPlayARandomTrack:jsr PickARandomTrack
lda BCDRandomPickedTrk
; We're in random mode, "playing" just one track now, so Current/First/Last are all the same
sta BCDRelTrack
sta BCDFirstTrackNow
sta BCDLastTrackNow
; and we're "stopping" at the end of the current track
jsr SetStopToEoBCDLTN
; Set flag to Track mode, because we're in random mode
lda #$ff
sta TrackOrMSFFlag
; Call AudioPlay function and exit
jsr C21AudioPlay
bra ExitPBCHandler
; Entire disc has been played to the end, do we need to loop?
PBCRandomIsInactive:lda LoopButtonState
; Loop Button is inactive - so we just stop
beq PBCLoopIsInactive
; Loop button is active - reset stop point to EoT LastTrack
jsr C23AudioStop
lda PlayButtonState
; Play button is inactive - bail out, there's nothing to do
beq ExitPBCHandler
; Make sure we start fresh from the first track on the disc
lda BCDFirstTrackTOC
sta BCDFirstTrackNow
; Set flag to MSF mode, because we're not in random mode
dec TrackOrMSFFlag
; Call AudioPlay function and exit
jsr C21AudioPlay
bra ExitPBCHandler
; Stop, because Loop is inactive
PBCLoopIsInactive: lda PlayButtonState
; Play button is inactive (already) - bail out, there's nothing to do
beq ExitPBCHandler
; Use the existing StopAction to stop everything
jsr DoStopAction
ExitPBCHandler: rts
StatusDrive: pha
; Try three times for a good status return
lda #$03
sta RetryCount
; $00 = Status
RetryLoop: lda #$00
sta SPCommandType
; $00 = Code
lda #$00
sta SPCode
jsr SPCallVector
bcc GotStatus
dec RetryCount
bne RetryLoop
sec
; Failed Status call three times - exit with carry set
bra ExitStatusDrive
; First byte is general status byte
GotStatus: lda SPBuffer
; $B4 means Block Device, Read-Only, and Online (CD-ROM)
cmp #$b4
beq StatusDriveSuccess
lda TOCInvalidFlag
; TOC is currently flagged invalid - that's expected, so just return the Status call failure
bne StatusDriveFail
; EXCEPTION - Call a HardShutdown if we encounter a bad Status call with an existing valid TOC because something's gone very wrong
jsr HardShutdown
; Hard-flag the TOC as now invalid
lda #$ff
sta TOCInvalidFlag
; Exit with carry set
StatusDriveFail: sec
bra ExitStatusDrive
; Exit with carry clear
StatusDriveSuccess: clc
ExitStatusDrive: pla
rts
; EXCEPTION - Explicitly stop the CD drive, forcibly clear the Play/Pause buttons, forcibly set the Stop button, and wipe the Track/Time display clean
HardShutdown: jsr DoStopAction
lda PauseButtonState
; Pause button is inactive (already) - nothing to do
beq NoPauseButtonChange
; Clear Pause button to inactive
lda #$00
sta PauseButtonState
jsr ToggleUIPauseButton
NoPauseButtonChange:lda PlayButtonState
; Play button is inactive (already) - nothing to do
beq NoPlayButtonChange
; Clear Play button to inactive
lda #$00
sta PlayButtonState
jsr ToggleUIPlayButton
NoPlayButtonChange: lda StopButtonState
; Stop button is active (already) - nothing to do
bne ClearTrackAndTime
; Set Stop button to active
lda #$ff
sta StopButtonState
jsr ToggleUIStopButton
; "Null" out T/M/S values and blank Track & Time display
ClearTrackAndTime: lda #$aa
sta BCDRelTrack
sta BCDRelMinutes
sta BCDRelSeconds
jsr DrawTrack
jsr DrawTime
lda BlankGlyphAddr
sta ZP1
lda BlankGlyphAddr + 1
sta ZP1 + 1
ldx #$20
jsr DrawDigitAtX
rts
; Called in the background if the drive reports a valid/online Status and we still have an invalid TOC.
ReReadTOC: lda #$00
sta TOCInvalidFlag
jsr C27ReadTOC
sed
clc
lda BCDLastTrackTOC
sbc BCDFirstTrackTOC
adc #$01
; Calculate the number of tracks on the disc, minus 1, and stash it
sta BCDTrackCount0Base
cld
; Explicitly stop playback and set stop point to EoT LastTrack
jsr C23AudioStop
; Set flag to Track mode
lda #$ff
sta TrackOrMSFFlag
; Update Track and Time display
jsr C28ReadQSubcode
jsr DrawTrack
jsr DrawTime
lda RandomButtonState
; Random button is inactive, so nothing else to do
beq ExitReReadTOC
; Random button is active, so set up random mode
jsr RandomModeInit
ExitReReadTOC: rts
; Read a keypress if there is one
GetKeypress: inc RandomSeed
lda Kbd
bpl GKNoKeyPressed
bit KbdStrobe
; Strip the high bit
and #$7f
cmp #$61
bmi GKNotLowerCase
cmp #$7a
bpl GKNotLowerCase
; Force it to upper-case
and #$5f
; Set carry flag to indicate keypress or not, and exit
GKNotLowerCase: clc
bra ExitGetKeypress
GKNoKeyPressed: sec
ExitGetKeypress: rts
; Function to wait until an existing keypress is released
KeyReleaseWait: lda KbdStrobe
bmi KeyReleaseWait
rts
DoPlayAction: lda PauseButtonState
; Pause button is inactive - nothing to do yet
beq DPAPauseIsInactive
; Pause button is active - forcibly clear it to inactive and then...
lda #$00
sta PauseButtonState
jsr ToggleUIPauseButton
; call AudioPause to release Pause (resume playing) and exit
jsr C22AudioPause
bra ExitPlayAction
DPAPauseIsInactive: lda PlayButtonState
; Play button is active (already) - bail out, there's nothing to do
bne ExitPlayAction
; Play button is inactive, we're starting from scratch - before activating, check the random mode
lda RandomButtonState
; Random button is inactive - just update the UI buttons and start playback
beq DPARandomIsInactive
; Random button is active - initialize random mode, pick a Track, and start it
jsr RandomModeInit
jsr PickARandomTrack
lda BCDRandomPickedTrk
; In random mode, we're "playing" just one track, so Current/First/Last are all the same
sta BCDRelTrack
sta BCDFirstTrackNow
sta BCDLastTrackNow
; and we "stop" playing at the end of that track
jsr SetStopToEoBCDLTN
jsr C20AudioSearch
; Set Play button to active
DPARandomIsInactive:dec PlayButtonState
jsr ToggleUIPlayButton
lda StopButtonState
; Stop button is inactive (already) - just start the playback and exit
beq DPAStopIsInactive
; Set Stop button to inactive, then start the playback and exit
lda #$00
sta StopButtonState
jsr ToggleUIStopButton
DPAStopIsInactive: jsr C21AudioPlay
ExitPlayAction: rts
DoStopAction: lda StopButtonState
; Stop button is active (already) - bail out, there's nothing to do
bne ExitStopAction
; Reset First/Last to TOC values
lda BCDFirstTrackTOC
sta BCDFirstTrackNow
lda BCDLastTrackTOC
sta BCDLastTrackNow
lda PlayButtonState
; Play button is inactive (already) - nothing to do
beq DSAPlayIsInactive
; Clear Play button to inactive
lda #$00
sta PlayButtonState
jsr ToggleUIPlayButton
DSAPlayIsInactive: lda PauseButtonState
; Pause button is inactive (already) - nothing to do
beq DSAPauseIsInactive
; Clear Pause button to inactive
lda #$00
sta PauseButtonState
jsr ToggleUIPauseButton
; Set Stop button to active
DSAPauseIsInactive: lda #$ff
sta StopButtonState
; Switch Stop Flag to EoTrack mode
dec TrackOrMSFFlag
jsr ToggleUIStopButton
; Force drive to seek to first track
lda BCDFirstTrackNow
sta BCDRelTrack
jsr C20AudioSearch
; Explicitly stop playback and set stop point to EoT last Track
jsr C23AudioStop
; Update Track and Time display
jsr C28ReadQSubcode
jsr DrawTrack
jsr DrawTime
ExitStopAction: rts
DoPauseAction: lda StopButtonState
; Stop button is active - bail out, there's nothing to do
bne ExitPauseAction
; Toggle Pause button
lda #$ff
eor PauseButtonState
sta PauseButtonState
jsr ToggleUIPauseButton
; Execute pause action (pause or resume) based on new button state
jsr C22AudioPause
; Wait for key to be released and exit
jsr KeyReleaseWait
ExitPauseAction: rts
ToggleLoopMode: lda #$ff
eor LoopButtonState
sta LoopButtonState
jsr ToggleUILoopButton
jsr KeyReleaseWait
rts
ToggleRandomMode: lda #$ff
eor RandomButtonState
sta RandomButtonState
beq TRMRandomIsInactive
; Random button is now active - re-initialize random mode and exit
jsr RandomModeInit
bra TRMUpdateButton
; Random button is now inactive - reset First/Last to TOC values and update stop point to EoT last Track
TRMRandomIsInactive:lda BCDLastTrackTOC
sta BCDLastTrackNow
lda BCDFirstTrackTOC
sta BCDFirstTrackNow
jsr SetStopToEoBCDLTN
; Update UI, wait for key release, and exit
TRMUpdateButton: jsr ToggleUIRandButton
jsr KeyReleaseWait
rts
; Zero the Played Track Counter
RandomModeInit: lda #$00
sta HexPlayedCount0Base
; Clear the list of what Tracks have been played
jsr ClearPlayedList
; Convert the BCD count-of-tracks-minus-1 into a Hex count-of-tracks-minus-1 and store it in HexTrackCount0Base
lda BCDTrackCount0Base
and #$0f
sta HexTrackCount0Base
lda BCDTrackCount0Base
and #$f0
lsr a
sta RandFuncTempStorage
lsr a
lsr a
clc
adc RandFuncTempStorage
clc
adc HexTrackCount0Base
; This value is used to compare against HexPlayedCount0Base so we can determine when we've random-played the whole disc
sta HexTrackCount0Base
lda PlayButtonState
; Play button is inactive - don't do anything else
beq ExitRandomInit
; Play button is active - set the current Track as First/Last/Picked, and set the proper random Stop mode
lda BCDRelTrack
sta BCDRandomPickedTrk
sta BCDFirstTrackNow
sta BCDLastTrackNow
jsr SetStopToEoBCDLTN
; Mark the current Track's element of the Played List as $FF ("played")
phy
ldy BCDRelTrack
lda #$ff
sta (PlayedListPtr),y
ply
ExitRandomInit: rts
DoScanBackAction: lda PlayButtonState
; Play button is inactive - bail out, there's nothing to do
beq ExitScanBackAction
lda PauseButtonState
; Pause button is active - bail out, there's nothing to do
bne ExitScanBackAction
; Highlight the Scan Back button and engage "scan" mode backwards
jsr ToggleUIScanBackButton
jsr C25AudioScanBack
; Keep updating the time display as long as you get good QSub reads
DSBALoop: jsr C28ReadQSubcode
bcs DSBAQSubReadErr
jsr DrawTrack
jsr DrawTime
; Keep scanning as long as the key is held down
DSBAQSubReadErr: lda KbdStrobe
bmi DSBALoop
; Key released - dim the Scan Back button, and resume playing from where we are
jsr ToggleUIScanBackButton
jsr C22AudioPause
ExitScanBackAction: rts
DoScanFwdAction: lda PlayButtonState
; Play button is inactive - bail out, there's nothing to do
beq ExitScanFwdAction
lda PauseButtonState
; Pause button is active - bail out, there's nothing to do
bne ExitScanFwdAction
; Highlight the Scan Forward button and engage "scan" mode forward
jsr ToggleUIScanFwdButton
jsr C25AudioScanFwd
; Keep updating the time display as long as you get good QSub reads
DSFALoop: jsr C28ReadQSubcode
bcs DSFAQSubReadErr
jsr DrawTrack
jsr DrawTime
; Keep scanning as long as the key is held down
DSFAQSubReadErr: lda KbdStrobe
bmi DSFALoop
; Key released - dim the Scan Forward button, and resume playing from where we are
jsr ToggleUIScanFwdButton
jsr C22AudioPause
ExitScanFwdAction: rts
DoPrevTrackAction: jsr ToggleUIPrevButton
; Reset to start of track
lda #$00
sta BCDRelMinutes
sta BCDRelSeconds
jsr DrawTime
DPTAWrapCheck: lda BCDRelTrack
cmp BCDFirstTrackTOC
; If we're not at the "first" track, just decrement track #
bne DPTAJustPrev
; Otherwise, wrap to the "last" track instead
lda BCDLastTrackTOC
sta BCDRelTrack
bra DPTACheckRandom
; BCD decrement
DPTAJustPrev: sed
lda BCDRelTrack
sbc #$01
sta BCDRelTrack
cld
DPTACheckRandom: lda RandomButtonState
; Random button is inactive - just execute playback
beq DPTARandomInactive
; Random button is active - set the current Track as First/Last/Picked, and set the proper random Stop mode
lda BCDRelTrack
sta BCDFirstTrackNow
sta BCDLastTrackNow
sta BCDRandomPickedTrk
jsr SetStopToEoBCDLTN
; TODO: Remove - This check/branch does ABSOLUTELY NOTHING. Pointless code, remove these two lines
lda PlayButtonState
beq DPTARandomInactive
; Seek to new Track and update Track display
DPTARandomInactive: jsr C20AudioSearch
jsr DrawTrack
; Wait a comparatively long time for key release
ldx #$ff
DPTAHeldKeyCheck: lda KbdStrobe
bpl DPTAKeyReleased
dex
bne DPTAHeldKeyCheck
DPTAKeyWasHeld: lda Kbd
; If key is held long enough, loop all the way back to DPTAWrapCheck and go back another track
bpl DPTAWrapCheck
bit KbdStrobe
bra DPTAKeyWasHeld
DPTAKeyReleased: jsr ToggleUIPrevButton
rts
DoNextTrackAction: jsr ToggleUINextButton
; Reset to start of track
lda #$00
sta BCDRelMinutes
sta BCDRelSeconds
jsr DrawTime
DNTAWrapCheck: lda BCDRelTrack
cmp BCDLastTrackTOC
; If we're not at the "last" track, just increment track #
bne DNTAJustNext
; Otherwise, wrap to the "first" track instead
lda BCDFirstTrackTOC
sta BCDRelTrack
bra DNTACheckRandom
; BCD increment
DNTAJustNext: sed
lda BCDRelTrack
adc #$01
sta BCDRelTrack
cld
DNTACheckRandom: lda RandomButtonState
; Random button is inactive - just execute playback
beq DNTARandomInactive
; Random button is active - set the current Track as First/Last/Picked, and set the proper random Stop mode
lda BCDRelTrack
sta BCDFirstTrackNow
sta BCDLastTrackNow
sta BCDRandomPickedTrk
jsr SetStopToEoBCDLTN
; TODO: Remove - This check/branch does ABSOLUTELY NOTHING. Pointless code, remove these two lines
lda PlayButtonState
beq DNTARandomInactive
; Seek to new Track and update Track display
DNTARandomInactive: jsr C20AudioSearch
jsr DrawTrack
; Wait a comparatively long time for key release
ldx #$ff
DNTAHeldKeyCheck: lda KbdStrobe
bpl DNTAKeyReleased
dex
bne DNTAHeldKeyCheck
DNTAKeyWasHeld: lda Kbd
; If key is held long enough, loop all the way back to DPTAWrapCheck and go forward another track
bpl DNTAWrapCheck
bit KbdStrobe
bra DNTAKeyWasHeld
DNTAKeyReleased: jsr ToggleUINextButton
rts
C26Eject: jsr ToggleUIEjectButton
; $26 = Eject
lda #$26
sta SPCode
; $04 = Control
lda #$04
sta SPCommandType
jsr SPCallVector
jsr KeyReleaseWait
jsr ToggleUIEjectButton
lda StopButtonState
; Stop button is active - just go wipe the Track & Time display and clear the TOC
bne ClearTrackTime_TOC
lda PauseButtonState
; Pause button is inactive (already) - nothing to do
beq EjPauseIsInactive
; Clear Pause button to inactive
lda #$00
sta PauseButtonState
jsr ToggleUIPauseButton
EjPauseIsInactive: lda PlayButtonState
; Play button is inactive (already) - nothing to do
beq EjPlayIsInactive
; Clear Play button to inactive
lda #$00
sta PlayButtonState
jsr ToggleUIPlayButton
; Clear Drive Playing state
EjPlayIsInactive: lda #$00
sta DrivePlayingFlag
; Set Stop button to active
dec a
sta StopButtonState
jsr ToggleUIStopButton
; "Null" out T/M/S, blank Track & Time display, and invalidate the TOC
ClearTrackTime_TOC: lda #$aa
sta BCDRelTrack
sta BCDRelMinutes
sta BCDRelSeconds
jsr DrawTrack
jsr DrawTime
lda BlankGlyphAddr
sta ZP1
lda BlankGlyphAddr + 1
sta ZP1 + 1
ldx #$20
jsr DrawDigitAtX
lda #$ff
sta TOCInvalidFlag
rts
C21AudioPlay: lda #$ff
sta DrivePlayingFlag
; $04 = Control
lda #$04
sta SPCommandType
; $21 = AudioPlay
lda #$21
sta SPCode
jsr ZeroOutSPBuffer
; Stop flag = $00 (stop address in 2-5) LA I think this is wrong, and it should be start address?
lda #$00
sta SPBuffer
; Play mode = $09 (Standard stereo)
lda #$09
sta SPBuffer + 1
lda TrackOrMSFFlag
; Use M/S/F to stop playback at the end of the disc for sequential mode
beq APStopAtMSF
; Use end of currently-selected Track as "stop" point for random mode
APStopAtTrack: lda BCDFirstTrackNow
sta SPBuffer + 2
; Address Type = $02 (Track)
lda #$02
sta SPBuffer + 6
lda #$00
sta TrackOrMSFFlag
bra CallAudioPlay
APStopAtMSF: lda BCDAbsMinutes
sta SPBuffer + 4
lda BCDAbsSeconds
sta SPBuffer + 3
lda BCDAbsFrame
sta SPBuffer + 2
; Address Type = $01 (MSF)
lda #$01
sta SPBuffer + 6
CallAudioPlay: jsr SPCallVector
rts
; $04 = Control
C20AudioSearch: lda #$04
sta SPCommandType
lda #$20
sta SPCode
jsr ZeroOutSPBuffer
lda PlayButtonState
; Play button is inactive - seek and hold
beq ASHoldAfterSearch
; Play button is active - seek and play
ASPlayAfterSearch: lda #$ff
; Set the Drive Playing flag to active
sta DrivePlayingFlag
; $01 = Play after search
lda #$01
bra ASExecuteSearch
; $00 = Hold after search
ASHoldAfterSearch: lda #$00
ASExecuteSearch: sta SPBuffer
; $09 = Play mode (Standard stereo)
lda #$09
sta SPBuffer + 1
; Search address = Track
lda BCDRelTrack
sta SPBuffer + 2
; Address Type = $02 (Track)
lda #$02
sta SPBuffer + 6
jsr SPCallVector
; TODO: Remove - This whole block of code is dead/unbranched - remove all of it up through DeadCodeExit
bra ASSkipDeadCode
DeadCode: phx
ldx #$03
DeadCodeLoop: jsr C24AudioStatus
lda SPBuffer
cmp #$03
beq DeadCodeExit
dex
bne DeadCodeLoop
.byte $00, $00
DeadCodeExit: plx
ASSkipDeadCode: lda PauseButtonState
; Pause button is inactive (already) - nothing to do
beq ASPauseIsInactive