/
surl_stream_av.s
1739 lines (1539 loc) · 53.2 KB
/
surl_stream_av.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
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 3 of the License, or
; (at your option) any later version.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
.export _surl_stream_av
.export _SAMPLES_BASE
.importzp _zp6, _zp8, _zp9, _zp10, _zp12, _zp13, tmp1, tmp2, tmp3, ptr1, ptr2, ptr3, ptr4
.import _serial_putc_direct
.import _simple_serial_set_irq
.import _simple_serial_flush
.import _sleep, _init_text, _clrscr
.import _printer_slot, _data_slot
.import acia_status_reg_r, acia_data_reg_r
.include "apple2.inc"
; -----------------------------------------------------------------
MAX_LEVEL = 31
serial_status_reg = acia_status_reg_r
serial_data_reg = acia_data_reg_r
HAS_BYTE = $08
MAX_OFFSET = 126
N_BASES = (8192/MAX_OFFSET)+1
N_TEXT_BASES = 4
.ifdef DOUBLE_BUFFER
PAGE0_HB = $20
PAGE1_HB = $40
.else
PAGE0_HB = $20
PAGE1_HB = PAGE0_HB
.endif
SPKR := $C030
spkr_ptr = _zp6 ; word
last_offset = _zp8 ; byte
cur_mix = _zp9 ; byte
next = _zp10 ; word
page = _zp12 ; byte
has_byte_zp = _zp13 ; byte
store_dest = ptr1 ; word
page_ptr_low = ptr3 ; word
page_ptr_high = ptr4 ; word
zp_zero = tmp1 ; byte
zp_vflag = tmp2 ; byte
kbd_cmd = tmp3 ; byte
; Used to cross page
VD_PAGE_OFFSET = 254
; ---------------------------------------------------------
;
; Macros
.macro SEV_ABS ; We use V flag to track HGR page
bit abs_vflag ; dedicate a var because BIT #IMMEDIATE
.endmacro ; does NOT affect V flag
.macro SEV_ZP ; We use V flag to track HGR page
bit zp_vflag ; dedicate a var because BIT #IMMEDIATE
.endmacro ; does NOT affect V flag
.macro CLV_ZP ; We use V flag to track HGR page
bit zp_zero ; dedicate a var because BIT #IMMEDIATE
.endmacro ; does NOT affect V flag
; ease cycle counting
.macro WASTE_2 ; Cycles wasters
nop
.endmacro
.macro WASTE_3
stz zp_zero
.endmacro
.macro WASTE_4
nop
nop
.endmacro
.macro WASTE_5
WASTE_2
WASTE_3
.endmacro
.macro WASTE_6
nop
nop
nop
.endmacro
.macro WASTE_7
WASTE_2
WASTE_5
.endmacro
.macro WASTE_8
WASTE_4
WASTE_4
.endmacro
.macro WASTE_9
WASTE_3
WASTE_6
.endmacro
.macro WASTE_10
WASTE_4
WASTE_6
.endmacro
.macro WASTE_11
WASTE_5
WASTE_6
.endmacro
.macro WASTE_12
WASTE_6
WASTE_6
.endmacro
.macro WASTE_13
WASTE_7
WASTE_6
.endmacro
.macro WASTE_14
WASTE_8
WASTE_6
.endmacro
.macro WASTE_15
WASTE_12
WASTE_3
.endmacro
.macro WASTE_16
WASTE_12
WASTE_4
.endmacro
.macro WASTE_17
WASTE_12
WASTE_5
.endmacro
.macro WASTE_18
WASTE_12
WASTE_6
.endmacro
.macro WASTE_19
WASTE_12
WASTE_7
.endmacro
.macro WASTE_20
WASTE_12
WASTE_8
.endmacro
.macro WASTE_21
WASTE_12
WASTE_9
.endmacro
.macro WASTE_22
WASTE_12
WASTE_10
.endmacro
.macro WASTE_23
WASTE_12
WASTE_11
.endmacro
.macro WASTE_24
WASTE_12
WASTE_12
.endmacro
.macro WASTE_25
WASTE_12
WASTE_10
WASTE_3
.endmacro
.macro WASTE_26
WASTE_12
WASTE_12
WASTE_2
.endmacro
.macro WASTE_27
WASTE_12
WASTE_12
WASTE_3
.endmacro
.macro WASTE_28
WASTE_12
WASTE_12
WASTE_4
.endmacro
.macro WASTE_29
WASTE_12
WASTE_12
WASTE_5
.endmacro
.macro WASTE_30
WASTE_12
WASTE_12
WASTE_6
.endmacro
.macro WASTE_31
WASTE_12
WASTE_12
WASTE_7
.endmacro
.macro WASTE_32
WASTE_12
WASTE_12
WASTE_8
.endmacro
.macro WASTE_33
WASTE_12
WASTE_12
WASTE_9
.endmacro
.macro WASTE_34
WASTE_12
WASTE_12
WASTE_10
.endmacro
.macro WASTE_35
WASTE_12
WASTE_12
WASTE_11
.endmacro
.macro WASTE_36
WASTE_12
WASTE_12
WASTE_12
.endmacro
.macro WASTE_37
WASTE_12
WASTE_12
WASTE_6
WASTE_7
.endmacro
.macro WASTE_38
WASTE_12
WASTE_12
WASTE_7
WASTE_7
.endmacro
.macro WASTE_40
WASTE_12
WASTE_12
WASTE_12
WASTE_4
.endmacro
.macro WASTE_39
WASTE_12
WASTE_12
WASTE_12
WASTE_3
.endmacro
.macro WASTE_41
WASTE_12
WASTE_12
WASTE_12
WASTE_5
.endmacro
.macro WASTE_42
WASTE_12
WASTE_12
WASTE_12
WASTE_6
.endmacro
.macro WASTE_43
WASTE_12
WASTE_12
WASTE_12
WASTE_7
.endmacro
.macro WASTE_44
WASTE_12
WASTE_12
WASTE_12
WASTE_8
.endmacro
.macro WASTE_45 ; 18 nop + sta zp + 3 nop = 21+2 = 23 bytes
WASTE_12
WASTE_12
WASTE_12
WASTE_9
.endmacro
; Hack to waste 1 cycle. Use absolute stx with $00nn
.macro ABS_STX zpvar
.byte $8E ; stx absolute
.byte zpvar
.byte $00
.endmacro
; Hack to waste 1 cycle. Use absolute stz with $00nn
.macro ABS_STZ zpvar
.byte $9C ; stz absolute
.byte zpvar
.byte $00
.endmacro
; Hack to waste 1 cycle. Use absolute stz with $00nn
.macro ABS_STA zpvar
.byte $8D ; sta absolute
.byte zpvar
.byte $00
.endmacro
.macro ____SPKR_DUTY____4 ; Toggle speaker
sta SPKR ; 4
.endmacro
.ifdef __APPLE2ENH__
.macro ____SPKR_DUTY____5 ; Toggle speaker slower (but without phantom-read)
sta (spkr_ptr) ; 5
.endmacro
.endif
.code
; General principles. we expect audio to be pushed on one serial port, video on
; the other. Execution flow is controlled via the audio stream, with each
; received byte being the next duty cycle's address high byte.
;
; This means that duty cycle functions have to be page-aligned so their low byte
; is always 00.
; Functions that don't need to be aligned are stuffed between duty_cycle functions
; when possible, to waste less space.
;
; There are 32 different duty cycles (0-31), each moving the speaker by toggling
; it on and off at a certain cycles interval (from 8 to 39).
; The tasks of each duty cycle handler is:
; - Toggle the speaker on, wait a certain amount of cycles, toggle it off
; - Fetch the next byte of audio
; - Fetch the next byte of video
; - If there is a video byte, handle it
;
; It is possible to lose audio bytes without horrible effects (the sound quality
; would just be badder), but every video byte has to be fetched and handled,
; otherwise the video handler, being a state machine, will get messed up.
; Reading the same audio byte twice has no big drawback either, but reading the
; same video byte would, for the same reasons.
; In accordance to that, we spare ourselves the cycles required to verify the
; audio serial port's status register and unconditionnaly read the audio serial
; port's data register.
;
; Each duty cycle takes 73 cycles to complete (just below the 86µs interval at
; which bytes arrive on a serial port at 115200 bps), but as precisely timing
; the speaker toggling moves the serial fetching code around, it is possible to
; lose bytes of video if we only check once per duty cycle.
;
; Hence, each duty cycle does the following:
; - Load whatever is in the audio data register into jump destination
; - If there is a video byte,
; - Load it,
; - Finish driving speaker,
; - Waste the required amount of cycles
; - Jump to video handler
; - Otherwise,
; - Waste the required amount of cycles (less than in the first case),
; - Check video byte again,
; - If there is one, jump to video handler,
; - Otherwise,
; - Load audio byte again,
; - Waste (a lot) of cycles,
; - Jump to the next duty cycle.
;
; Keyboard handling is cycle-expensive and can't be macroized properly, so
; reading the keyboard in cycle 15. This cycle, being in the middle, is,
; hopefully, called multiple hundreds of time per second.
;
; The video handler is responsible for jumping directly to the next duty
; cycle once the video byte is handled.
;
; As a rule of thumb, no bytes are dropped if we check for video byte around
; cycles 12-20 and 24-31.
;
; Almost every reference to serial registers is direct, so every duty cycle
; is patched in multiple places. The patched instructions are labelled like
; ad0 (audio data cycle 0), and they need to be in *_patches arrays to be
; patched at start.
; Hardcoded placeholders have $xxFF where $xx is the register's address low
; byte for my setup, for reference.
; vs = video status = $99 (printer port in slot 1)
; vd = video data = $98 (printer port in slot 1)
; as = audio status = $A9 (modem port in slot 2)
; ad = audio data = $A8 (modem port in slot 2)
; To emulate 22kHz, we'd have to fit 29 duty cycles in 80 cycles,
; with jmp direct, from:
; toggle4-toggle4-other29-other3-toggle4-toggle4-other29-jump3
; to:
; toggle4-other29-toggle4-other3-toggle4-other29-toggle4-jump3
; This seems difficult to achieve (8 cycles needed for the second toggling,
; going from 73 cycles without to 80 with means no wasting at all)
;
; Warning about the alignment of the 32 duty cycles: as we read the next
; sample without verifying the ACIA's status register, we may read an
; incomplete byte, while it is being landed in the data register.
;
; So, we have to align the duty cycles in a way that even when this happens,
; we do not jump to a place where we have no duty cycle. This is why, here,
; we have them from $6000 to $7F00:
; $60 = 01100000
; $7F = 01111111
;
; At worst, we'll play a wrong sample from time to time. Tests with duty
; cycles from $6400 to $8300 crashed into the monitor quite fast:
; $64 = 01100100
; $83 = 10000011
; Reading an incomplete byte there could result in reading 11111111, for
; example, but not only. We don't want that.
.align $100
_SAMPLES_BASE = *
.assert * = $6000, error
duty_cycle0: ; end spkr at 8
____SPKR_DUTY____4 ; 4 toggle speaker on
____SPKR_DUTY____4 ; 8 toggle speaker off
vs0: lda $99FF ; 12 load video status register
ad0: ldx $A8FF ; 16 load audio data register
and #HAS_BYTE ; 18 check if video has byte
beq no_vid0 ; 20/21 branch accordingly
vd0: ldy $98FF ; 24 load video data
stx next+1 ; 27 store next duty cycle destination
WASTE_12 ; 39 waste extra cycles
jmp video_direct ; 42=>73
no_vid0: ; we had no video byte
ad0b: ldx $A8FF ; 25 load audio data register again
stx next+1 ; 28 store next duty cycle destination
WASTE_39 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; -----------------------------------------------------------------
_surl_stream_av: ; Entry point
php
sei ; Disable all interrupts
pha
lda #$00 ; Disable serial interrupts
jsr _simple_serial_set_irq
pla
; Setup pointers
jsr setup
; Clear pages
bit $C082
lda #$40
sta $E6
jsr $F3F2
lda #$20
sta $E6
jsr $F3F2
bit $C080
lda #$2F ; Surl client ready
jsr _serial_putc_direct
clv ; clear offset-received flag
jmp duty_start ; And start!
; -----------------------------------------------------------------
.align $100
.assert * = _SAMPLES_BASE + $100, error
duty_cycle1: ; end spkr at 9
____SPKR_DUTY____4 ; 4
____SPKR_DUTY____5 ; 9
ad1: ldx $A8FF ; 13
vs1: lda $99FF ; 17
and #HAS_BYTE ; 19
beq no_vid1 ; 21/22
vd1: ldy $98FF ; 25
stx next+1 ; 28
WASTE_11 ; 39
jmp video_direct ; 42=>73 (takes 31 cycles, jumps to next)
no_vid1:
ad1b: ldx $A8FF ; 26
stx next+1 ; 29
WASTE_38 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; -----------------------------------------------------------------
patch_addresses: ; Patch all registers in ptr1 array with A
ldy #$00 ; Start at beginning
sta tmp1 ; Save value
next_addr:
clc
lda (ptr1),y
adc #1 ; Add one to patch after label
sta ptr2
iny
lda (ptr1),y
beq done ; If high byte is 0, we're done
adc #0
sta ptr2+1
iny
lda (ptr2) ; Debug to be sure
cmp #$FF
beq :+
brk
:
lda tmp1 ; Patch low byte
sta (ptr2)
inc ptr2 ; Patch high byte with base (in X)
bne :+
inc ptr2+1
: txa
sta (ptr2)
bra next_addr
done:
rts
; -----------------------------------------------------------------
.align $100
.assert * = _SAMPLES_BASE + $200, error
duty_cycle2: ; end spkr at 10
____SPKR_DUTY____4 ; 4
WASTE_2 ; 6
____SPKR_DUTY____4 ; 10
ad2: ldx $A8FF ; 14
vs2: lda $99FF ; 18
and #HAS_BYTE ; 20
beq no_vid2 ; 22/23
vd2: ldy $98FF ; 26
stx next+1 ; 29
WASTE_10 ; 39
jmp video_direct ; 42=>73 (takes 31 cycles, jumps to next)
no_vid2:
ad2b: ldx $A8FF ; 27
stx next+1 ; 30
WASTE_37 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; -----------------------------------------------------------------
patch_serial_registers:
.ifdef IIGS
brk ; Todo
.else
clc
ldx #$C0 ; Serial registers' high byte
lda #<(video_status_patches)
sta ptr1
lda #>(video_status_patches)
sta ptr1+1
lda _printer_slot
asl
asl
asl
asl
adc #$89 ; ACIA STATUS (in slot 0)
jsr patch_addresses
lda #<(video_data_patches)
sta ptr1
lda #>(video_data_patches)
sta ptr1+1
lda _printer_slot
asl
asl
asl
asl
adc #$88 ; ACIA DATA (in slot 0)
pha
jsr patch_addresses
pla
clc
adc #2
sta vcmd+1
stx vcmd+2
sta vcmd2+1
stx vcmd2+2
adc #1
sta vctrl+1
stx vctrl+2
lda #<(audio_status_patches)
sta ptr1
lda #>(audio_status_patches)
sta ptr1+1
lda _data_slot
asl
asl
asl
asl
adc #$89
jsr patch_addresses
lda #<(audio_data_patches)
sta ptr1
lda #>(audio_data_patches)
sta ptr1+1
lda _data_slot
asl
asl
asl
asl
adc #$88
pha
jsr patch_addresses
pla
clc
adc #2
sta acmd+1
stx acmd+2
adc #1
sta actrl+1
stx actrl+2
rts
.endif
; -----------------------------------------------------------------
.align $100
.assert * = _SAMPLES_BASE + $300, error
duty_cycle3: ; end spkr at 11
____SPKR_DUTY____4 ; 4
WASTE_3 ; 7
____SPKR_DUTY____4 ; 11
ad3: ldx $A8FF ; 15
vs3: lda $99FF ; 19
and #HAS_BYTE ; 21
beq no_vid3 ; 23/24
vd3: ldy $98FF ; 27
stx next+1 ; 30
WASTE_9 ; 39
jmp video_direct ; 42=>73 (takes 31 cycles, jumps to next)
no_vid3:
ad3b: ldx $A8FF ; 28
stx next+1 ; 31
WASTE_36 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; -----------------------------------------------------------------
video_status_patches:
.word vss
.word vs0
.word vs1
.word vs2
.word vs3
.word vs4
.word vs5
.word vs6
.word vs7
.word vs8
.word vs9
.word vs10
.word vs11
.word vs12
.word vs13
.word vs14
.word vs15
.word vs16
.word vs17
.word vs18
.word vs19
.word vs20
.word vs21
.word vs22
.word vs23
.word vs24
.word vs25
.word vs26
.word vs27
.word vs28
.word vs29
.word vs30
.word vs31
.word $0000
; -----------------------------------------------------------------
.align $100
.assert * = _SAMPLES_BASE + $400, error
duty_cycle4: ; end spkr at 12
____SPKR_DUTY____4 ; 4
ad4: ldx $A8FF ; 8
____SPKR_DUTY____4 ; 12
vs4: lda $99FF ; 16
and #HAS_BYTE ; 18
beq no_vid4 ; 20/21
vd4: ldy $98FF ; 24
stx next+1 ; 27
WASTE_12 ; 39
jmp video_direct ; 42=>73 (takes 31 cycles, jumps to next)
no_vid4:
ad4b: ldx $A8FF ; 25
stx next+1 ; 28
WASTE_39 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; -----------------------------------------------------------------
video_data_patches:
.word vds
.word vd0
.word vd1
.word vd2
.word vd3
.word vd4
.word vd5
.word vd6
.word vd7
.word vd8
.word vd9
.word vd10
.word vd11
.word vd12
.word vd13
.word vd14
.word vd15
.word vd16
.word vd17
.word vd18
.word vd19
.word vd20
.word vd21
.word vd22
.word vd23
.word vd24
.word vd25
.word vd26
.word vd27
.word vd28
.word vd29
.word vd30
.word vd31
.word $0000
; -----------------------------------------------------------------
.align $100
.assert * = _SAMPLES_BASE + $500, error
duty_cycle5: ; end spkr at 13
____SPKR_DUTY____4 ; 4
ad5: ldx $A8FF ; 8
____SPKR_DUTY____5 ; 13
vs5: lda $99FF ; 17
and #HAS_BYTE ; 19
beq no_vid5 ; 21/22
vd5: ldy $98FF ; 25
stx next+1 ; 28
WASTE_11 ; 39
jmp video_direct ; 42=>73 (takes 31 cycles, jumps to next)
no_vid5:
ad5b: ldx $A8FF ; 26
stx next+1 ; 29
WASTE_38 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; -----------------------------------------------------------------
audio_status_patches:
.word ass
.word asp
.word $0000
audio_data_patches:
.word ads
.word adp
.word ad0
.word ad0b
.word ad1
.word ad1b
.word ad2
.word ad2b
.word ad3
.word ad3b
.word ad4
.word ad4b
.word ad5
.word ad5b
.word ad6
.word ad6b
.word ad7
.word ad7b
.word ad8
.word ad8b
.word ad9
.word ad9b
.word ad10
.word ad10b
.word ad11
.word ad11b
.word ad12
.word ad12b
.word ad13
.word ad13b
.word ad14
.word ad14b
.word ad15
.word ad15b
.word ad16
.word ad16b
.word ad17
.word ad17b
.word ad18
.word ad18b
.word ad19
.word ad19b
.word ad20
.word ad20b
.word ad21
.word ad21b
.word ad22
.word ad22b
.word ad23
.word ad23b
.word ad24
.word ad24b
.word ad25
.word ad25b
.word ad26
.word ad26b
.word ad27
.word ad27b
.word ad28
.word ad28b
.word ad29
.word ad29b
.word ad30
.word ad30b
.word ad31
.word ad31b
.word $0000
; -----------------------------------------------------------------
.align $100
.assert * = _SAMPLES_BASE + $600, error
duty_cycle6: ; end spkr at 14
____SPKR_DUTY____4 ; 4
ad6: ldx $A8FF ; 8
WASTE_2 ; 10
____SPKR_DUTY____4 ; 14
vs6: lda $99FF ; 18
and #HAS_BYTE ; 20
beq no_vid6 ; 22/23
vd6: ldy $98FF ; 26
stx next+1 ; 29
WASTE_10 ; 39
jmp video_direct ; 42=>73 (takes 31 cycles, jumps to next)
no_vid6:
ad6b: ldx $A8FF ; 27
stx next+1 ; 30
WASTE_37 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; --------------------------------------------------------------
duty_start:
; Init cycle destination
lda #<(duty_start)
sta next
lda #>(duty_start)
sta next+1
ass: lda $A9FF
and #HAS_BYTE
beq vss
ads: ldx $A8FF
stz next ; video_direct will jump there
stx next+1
vss: lda $99FF
and #HAS_BYTE
beq ass
vds: ldy $98FF
jmp video_direct
; --------------------------------------------------------------
.align $100
.assert * = _SAMPLES_BASE + $700, error
duty_cycle7: ; end spkr at 15
____SPKR_DUTY____4 ; 4
ad7: ldx $A8FF ; 8
WASTE_3 ; 11
____SPKR_DUTY____4 ; 15
vs7: lda $99FF ; 19
and #HAS_BYTE ; 21
beq no_vid7 ; 23/24
vd7: ldy $98FF ; 27
stx next+1 ; 30
WASTE_9 ; 39
jmp video_direct ; 42=>73 (takes 31 cycles, jumps to next)
no_vid7:
ad7b: ldx $A8FF ; 28
stx next+1 ; 31
WASTE_36 ; 67 waste extra cycles
jmp (next) ; 73 jump to next duty cycle
; -----------------------------------------------------------------
calc_bases:
; Precalculate addresses inside pages, so we can easily jump
; from one to another without complicated computations. X
; contains the base page address's high byte on entry ($20 for
; page 0, $40 for page 1)
ldy #0 ; Y is the index - Start at base 0
lda #$00 ; A is the address's low byte
; (and X the address's high byte)
clc
calc_next_base:
calc_addr_low:
sta page0_addrs_arr_low,y ; Store AX
pha
txa
calc_addr_high:
sta page0_addrs_arr_high,y
pla
iny
adc #(MAX_OFFSET) ; Compute next base
bcc :+
inx
clc
: cpy #(N_BASES)
bcc calc_next_base
rts
; -----------------------------------------------------------------
; -----------------------------------------------------------------
calc_text_bases:
; Precalculate text lines 20-23 adresses, so we can easily jump
; from one to another without complicated computations. X
; contains line 20's base page address high byte on entry ($02 for
; page 0, $06 for page 1).
ldy #(N_BASES) ; Y is the index - Start after HGR bases
clc
calc_next_text_base:
calc_addr_text_low:
sta page0_addrs_arr_low,y ; Store AX
pha
txa
calc_addr_text_high:
sta page0_addrs_arr_high,y
pla
iny
adc #$80 ; Compute next base
bcc :+
inx
clc
: cpy #(N_BASES+4+1)