-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathintybasic_prologue.asm
More file actions
3177 lines (2760 loc) · 118 KB
/
Copy pathintybasic_prologue.asm
File metadata and controls
3177 lines (2760 loc) · 118 KB
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
;
; Prologue for IntyBASIC programs
; by Oscar Toledo G. http://nanochess.org/
;
; Revision: Jan/30/2014. Spacing adjustment and more comments.
; Revision: Apr/01/2014. It now sets the starting screen pos. for PRINT
; Revision: Aug/26/2014. Added PAL detection code.
; Revision: Dec/12/2014. Added optimized constant multiplication routines.
; by James Pujals.
; Revision: Jan/25/2015. Added marker for automatic title replacement.
; (option --title of IntyBASIC)
; Revision: Aug/06/2015. Turns off ECS sound. Seed random generator using
; trash in 16-bit RAM. Solved bugs and optimized
; macro for constant multiplication.
; Revision: Jan/12/2016. Solved bug in PAL detection.
; Revision: May/03/2016. Changed in _mode_select initialization.
; Revision: Jul/31/2016. Solved bug in multiplication by 126 and 127.
; Revision: Sep/08/2016. Now CLRSCR initializes screen position for PRINT,
; this solves bug when user programs goes directly
; to PRINT.
; Revision: Oct/21/2016. Accelerated MEMSET.
; Revision: Jan/09/2018. Adjusted PAL/NTSC constant.
; Revision: Feb/05/2018. Forces initialization of Intellivoice if included.
; So VOICE INIT ceases to be dangerous.
; Revision: Oct/30/2018. Redesigned PAL/NTSC detection using intvnut code,
; also now compatible with Tutorvision. Reformatted.
; Revision: Jan/10/2018. Added ECS detection.
;
LISTING "off"
;;==========================================================================;;
;; IntyBASIC SDK Library: romseg-bs.mac ;;
;; Version: 1.2 ;;
;; Updated: 2026-03-20 ;;
;;--------------------------------------------------------------------------;;
;; This macro library is used by the IntyBASIC SDK to manage ROM address ;;
;; segments and generate statistics on program ROM usage. It is an ;;
;; extension of the "romseg.mac" macro library with added support for ;;
;; bank-switching. ;;
;; ;;
;; The library is based on a similar module created for the P-Machinery ;;
;; programming framework, which itself was based on the "CART.MAC" macro ;;
;; library originally created by Joe Zbiciak and distributed as part of ;;
;; the SDK-1600 development kit. ;;
;;--------------------------------------------------------------------------;;
;; The file is placed into the public domain by its author. ;;
;; All copyrights are hereby relinquished on the routines and data in ;;
;; this file. -- James Pujals (DZ-Jay), 2024-2026 ;;
;;==========================================================================;;
;; ======================================================================== ;;
;; ROM MANAGEMENT STRUCTURES ;;
;; ======================================================================== ;;
; Internal ROM information structure
_rom STRUCT 0
@@null QEQU 0
@@invalid QEQU -1
@@legacy QEQU 0
@@static QEQU 1
@@dynamic QEQU 2
@@mapcnt QEQU 9
@@pgsize QEQU 4096
@@open QSET @@invalid
@@error QSET @@null
@@segcnt QSET 0
@@segs QSET 0
ENDS
.ROM STRUCT 0
@@CurrentSeg QSET _rom.invalid ; No open segment
@@Size QSET 0
@@Used QSET 0
@@Available QSET 0
; Initialize segment counters
@@Segments[_rom.legacy ] QSET 0
@@Segments[_rom.static ] QSET 0
@@Segments[_rom.dynamic] QSET 0
ENDS
_rom_stat STRUCT 0
@@space QEQU " " ; 75
@@single QEQU "---------------------------------------------------------------------------"
@@double QEQU "==========================================================================="
ENDS
;; ======================================================================== ;;
;; __rom_raise_error(err, desc) ;;
;; Generates an assembler error and sets the global error flag. ;;
;; ;;
;; NOTE: Both strings must be devoid of semi-colons and commas, or ;;
;; Bad Things(tm) may happen during pre-processing. ;;
;; ;;
;; ARGUMENTS ;;
;; err The error message. ;;
;; desc Optional error description, or _rom.null if none. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 (failed). ;;
;; ======================================================================== ;;
MACRO __rom_raise_error(err, desc)
;
LISTING "off"
_rom.error QSET _rom.invalid
_rom.err_len QSET _rom.null
_rom.err_len QSET %desc%
IF (_rom.err_len <> _rom.null)
ERR $(%err%, ": ", %desc%)
ELSE
ERR $(%err%)
ENDI
LISTING "prev"
ENDM
;; ======================================================================== ;;
;; __rom_reset_error ;;
;; Resets the global error flag. ;;
;; ;;
;; ARGUMENTS ;;
;; None. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error 0 (no error) ;;
;; ======================================================================== ;;
MACRO __rom_reset_error
;
_rom.error QSET _rom.null
ENDM
;; ======================================================================== ;;
;; __rom_validate_map(map) ;;
;; Validates the requested ROM map. ;;
;; ;;
;; ARGUMENTS ;;
;; map The ROM map selected. Valid values are 0 to 7. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_validate_map(map)
;
_rom.max QSET (_rom.mapcnt - 1)
IF (((%map%) < 0) OR ((%map%) > _rom.max))
__rom_raise_error(["Invalid ROM map number (", $#(%map%), ")"], ["Valid maps are from 0 to ", $#(_rom.max), "."])
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_validate_type(type) ;;
;; Validates the requested segment type symbol. ;;
;; ;;
;; ARGUMENTS ;;
;; type The segment type to validate. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_validate_type(type)
;
IF ((CLASSIFY(_rom.%type%) = -10000))
__rom_raise_error("Invalid ROM segment type \"%type%\".", _rom.null)
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_validate_segment(seg) ;;
;; Validates the requested segment number to ensure it is supported by the ;;
;; active memory map. ;;
;; ;;
;; ARGUMENTS ;;
;; seg The segment number to validate. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_validate_segment(seg)
;
IF ((CLASSIFY(_rom.segidx[%seg%]) = -10000))
__rom_raise_error(["Invalid ROM segment number #", $#(%seg%), " for selected memory map."], _rom.null)
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_validate_seg_bank(seg, bank) ;;
;; Validates the requested dynamic segment and bank number to ensure it is ;;
;; supported by the active memory map. ;;
;; ;;
;; ARGUMENTS ;;
;; seg The segment number to validate, or -1 for the first one.;;
;; bank The dynamic segment bank number to validate. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_validate_seg_bank(seg, bank)
;
; First, check if the map supports dynamic segments at all
IF (.ROM.Segments[_rom.dynamic] = 0)
__rom_raise_error("The selected memory map does not support dynamic segments.", _rom.null)
ENDI
; Validate the segment
IF (_rom.error = _rom.null)
__rom_validate_segment(%seg%)
ENDI
; Validate the bank
IF (_rom.error = _rom.null)
_rom.max QSET (_rom.bnkcnt[%seg%] - 1)
IF (((%bank%) < 0) OR ((%bank%) > _rom.max))
__rom_raise_error(["Invalid bank number #", $#(%bank%), " for dynamic ROM segment #", $#(%seg%)], ["Must be between 0 and ", $#(_rom.max), "."])
ENDI
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_validate_segnum(segnum) ;;
;; Validates the requested internal segment number to ensure that it is ;;
;; valid within the active memory map. ;;
;; ;;
;; ARGUMENTS ;;
;; segnum The internal memory map segment number to validate. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_validate_segnum(segnum)
;
IF _EXPMAC ((CLASSIFY(_rom.t[%segnum%]) = -10000))
__rom_raise_error(["Unknown internal ROM segment number #", $#(%segnum%), "."], _rom.null)
ELSE
_rom.max QSET (_rom.segcnt - 1) ' Max internal index
IF _EXPMAC (((%segnum%) < 0) OR ((%segnum%) > _rom.max))
__rom_raise_error(["Invalid internal segment number #", $#(%segnum%), " for selected memory map"], ["Must be a value between 0 and ", $#(_rom.max), "."])
ENDI
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_assert_setup(label) ;;
;; Ensures that ROM.Setup has been called. ;;
;; ;;
;; ARGUMENTS ;;
;; label A quoted-string containing the label of the asserting ;;
;; macro or function. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_assert_setup(label)
;
IF ((CLASSIFY(_rom.init) = -10000))
__rom_raise_error(["ROM", ".Setup directive must be used before calling ROM.", %label%, "."], _rom.null)
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_assert_romseg_support(label) ;;
;; Prevents the invocation of a feature that is not supported by the ;;
;; legacy memory map when ROM map #0 is selected. ;;
;; ;;
;; ARGUMENTS ;;
;; label A quoted-string containing the label of the asserting ;;
;; macro or function. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_assert_romseg_support(label)
;
IF (_rom.map = 0)
__rom_raise_error([%label%, " failed"], ["Legacy ROM map #", $#(_rom.map), " does not support it."])
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_assert_def_order(type) ;;
;; Ensures that segments are defined in the proper order: all legacy and ;;
;; static segments first, followed by all dynamic ones. ;;
;; ;;
;; ARGUMENTS ;;
;; type The segment type to check. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_assert_def_order(type)
;
_rom.type QSET _rom.%type%
; Make sure all dynamic segments are defined last
IF (((_rom.type = _rom.legacy) OR (_rom.type = _rom.static)) AND (.ROM.Segments[_rom.dynamic] > 0))
__rom_raise_error("Invalid ROM segment definition order", "All static and legacy segments must be defined before any dynamic ones.")
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_segmem_size(segnum) ;;
;; Computes the total size of a ROM segment. ;;
;; ;;
;; ARGUMENTS ;;
;; segnum The internal segment for which to compute the size. ;;
;; ;;
;; OUTPUT ;;
;; None. ;;
;; ======================================================================== ;;
MACRO __rom_segmem_size(segnum)
;
.ROM.SegSize[%segnum%] QSET (_rom.e[%segnum%] - _rom.b[%segnum%] + 1)
ENDM
;; ======================================================================== ;;
;; __rom_segmem_used(segnum) ;;
;; Computes the usage of a ROM segment. ;;
;; ;;
;; ARGUMENTS ;;
;; segnum The internal segment for which to compute the usage. ;;
;; ;;
;; OUTPUT ;;
;; None. ;;
;; ======================================================================== ;;
MACRO __rom_segmem_used(segnum)
;
.ROM.SegUsed[%segnum%] QSET (_rom.pos[%segnum%] - _rom.b[%segnum%])
ENDM
;; ======================================================================== ;;
;; __rom_segmem_available(segnum) ;;
;; Computes the available space of a ROM segment. ;;
;; ;;
;; ARGUMENTS ;;
;; segnum The internal segment for which to compute the available ;;
;; space. ;;
;; ;;
;; OUTPUT ;;
;; None. ;;
;; ======================================================================== ;;
MACRO __rom_segmem_available(segnum)
;
.ROM.SegAvlb[%segnum%] QSET (_rom.e[%segnum%] - _rom.pos[%segnum%] + 1)
ENDM
;; ======================================================================== ;;
;; __rom_calculate_stats ;;
;; Computes the total ROM size and usage statistics. ;;
;; ;;
;; ARGUMENTS ;;
;; None. ;;
;; ;;
;; OUTPUT ;;
;; None. ;;
;; ======================================================================== ;;
MACRO __rom_calculate_stats
;
_rom.segnum QSET 0
REPEAT (_rom.segcnt)
.ROM.Size SET (.ROM.Size + .ROM.SegSize[_rom.segnum])
.ROM.Used SET (.ROM.Used + .ROM.SegUsed[_rom.segnum])
.ROM.Available SET (.ROM.Available + .ROM.SegAvlb[_rom.segnum])
_rom.segnum QSET (_rom.segnum + 1)
ENDR
ENDM
;; ======================================================================== ;;
;; __rom_init_segmem(seg, start, end, page, type) ;;
;; Initializes and configures the requested memory map segment indicated ;;
;; by "seg," using the provided arguments. All internal data structures ;;
;; for memory integrity and accounting are also initialized. ;;
;; ;;
;; ;;
;; ;;
;; ARGUMENTS ;;
;; seg The number of the segment to initialize. ;;
;; start The start address of the segment. ;;
;; end The end address of the segment. ;;
;; page An optional page number to switch the segment to. ;;
;; type The type of segment: "legacy," "static," or "dynamic". ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_init_segmem(seg, start, end, page, type)
;
__rom_validate_type(%type%)
__rom_assert_def_order(%type%)
IF (_rom.error = _rom.null)
_rom.type QSET _rom.%type%
_rom.num QSET _rom.segcnt ; New internal segment number
_rom.segcnt QSET (_rom.segcnt + 1) ; Total internal segments (so far)
; Keep track of the index for the given segment
IF ((CLASSIFY(_rom.segidx[%seg%]) = -10000))
_rom.segs QSET (_rom.segs + 1)
_rom.segidx[%seg%] QSET _rom.num
_rom.bnkcnt[%seg%] QSET 0
ENDI
_rom.b [_rom.num] QSET %start% ; Start address
_rom.e [_rom.num] QSET %end% ; End address
_rom.p [_rom.num] QSET %page% ; Page number
_rom.t [_rom.num] QSET _rom.type ; Segment type
IF ((%page%) <> _rom.invalid)
_rom.sbase QSET (_rom.b[_rom.num] AND $F000)
_rom.send QSET (_rom.e[_rom.num] OR $0FFF)
_rom.spages QSET (((_rom.send - _rom.sbase) + 1) / _rom.pgsize)
_rom.pgs [_rom.num] QSET _rom.spages ; Physical pages in segment
; For dynamic segments, keep track of
; the number of banks.
IF (_rom.type = _rom.dynamic)
_rom.bnk [_rom.num] QSET _rom.bnkcnt[%seg%] ; Logical bank number
_rom.bnkcnt[%seg%] QSET (_rom.bnkcnt[%seg%] + 1) ; Banks in segment
ENDI
ELSE
_rom.bnk [_rom.num] QSET _rom.invalid
_rom.pgs [_rom.num] QSET _rom.invalid
ENDI
_rom.seg [_rom.num] QSET %seg%
_rom.pos [_rom.num] QSET %start% ; Starting position
.ROM.Segments[_rom.type] QSET (.ROM.Segments[_rom.type] + 1)
IF (_rom.type <> _rom.legacy)
; Initialize accounting statistics
__rom_segmem_size (_rom.num)
__rom_segmem_used (_rom.num)
__rom_segmem_available(_rom.num)
ENDI
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_check_segmem_range(addr, segnum) ;;
;; Checks an address to make sure it falls within a given ROM segment. ;;
;; ;;
;; ARGUMENTS ;;
;; addr The address to check. ;;
;; segnum The internal segment for which to check the range. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; _rom.ovrflo The number of words in excess. ;;
;; _rom.sidx The logical segment number. ;;
;; _rom.bidx The logical bank number. ;;
;; ======================================================================== ;;
MACRO __rom_check_segmem_range(addr, segnum)
;
IF (_rom.open = %segnum%)
_rom.rbase QSET _rom.cb
_rom.rend QSET _rom.ce
ELSE
_rom.rbase QSET _rom.b[%segnum%]
_rom.rend QSET _rom.e[%segnum%]
ENDI
IF _EXPMAC ((%addr%) < _rom.rbase) OR (((%addr%) - 1) > _rom.rend)
_rom.ovrflo QSET ((%addr%) - _rom.rend - 1)
_rom.sidx QSET _rom.seg[%segnum%]
_rom.bidx QSET _rom.bnk[%segnum%]
; NOTE: Overflows are significant, so we want to
; display such errors in STDOUT as well.
IF _EXPMAC (_rom.t[%segnum%] = _rom.dynamic)
__rom_raise_error(["Dynamic ROM segment overflow in segment #", $#(_rom.sidx), ", bank #", $#(_rom.bidx)], ["Total ", $#(_rom.ovrflo), " words in excess."])
SMSG $("ERROR: Overflow in dynamic ROM segment #", $#(_rom.sidx), ", bank #", $#(_rom.bidx), ": Total ", $#(_rom.ovrflo), " words in excess.")
ELSE
__rom_raise_error(["ROM segment overflow in segment #", $#(_rom.sidx)], ["Total ", $#(_rom.ovrflo), " words in excess."])
SMSG $("ERROR: Overflow in ROM segment #", $#(_rom.sidx), ": Total ", $#(_rom.ovrflo), " words in excess.")
ENDI
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_set_pc_addrs(addr, page) ;;
;; Relocates the program counter to the given address, selecting a ;;
;; specific page if requested. ;;
;; ;;
;; ARGUMENTS ;;
;; addr The new address to set the program counter. ;;
;; page An optional page to select (or _rom.invalid if none). ;;
;; ;;
;; OUTPUT ;;
;; None. ;;
;; ======================================================================== ;;
MACRO __rom_set_pc_addrs(addr, page)
;
IF (%page% <> _rom.invalid)
LISTING "on"
; Open segment page
ORG %addr%:%page%
LISTING "prev"
ELSE
LISTING "on"
; Open segment
ORG %addr%
LISTING "prev"
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_open_seg(segnum) ;;
;; Opens a ROM segment. If the segment is already open, it checks the ;;
;; current program counter to ensure it is still within valid range. ;;
;; ;;
;; ARGUMENTS ;;
;; segnum The internal memory map segment to open. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_open_seg(segnum)
;
__rom_reset_error
__rom_validate_segnum(%segnum%)
IF _EXPMAC (_rom.error = _rom.null)
IF _EXPMAC (_rom.open <> %segnum%)
_rom.cb QSET _rom.b [%segnum%] ; Current base address
_rom.ce QSET _rom.e [%segnum%] ; Current end address
_rom.cp QSET _rom.p [%segnum%] ; Current page
_rom.cpos QSET _rom.pos[%segnum%]
__rom_set_pc_addrs(_rom.cpos, _rom.cp)
_rom.open QSET %segnum%
.ROM.CurrentSeg QSET _rom.open
ELSE
_rom.pc QSET $
; If the segment is already open, just
; verify we're still within in range.
__rom_check_segmem_range(_rom.pc, %segnum%)
ENDI
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_close_seg(segnum) ;;
;; Closes an open ROM segment. It also checks that the current program ;;
;; counter falls within the valid range of the open segment. Nothing will ;;
;; be done if "segnum" is _rom.invalid. An error is raised if the given ;;
;; segment is not open. ;;
;; ;;
;; ARGUMENTS ;;
;; segnum The internal memory map segment to close. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_close_seg(segnum)
;
__rom_reset_error
IF ((%segnum%) <> _rom.invalid)
__rom_validate_segnum(%segnum%)
IF (_rom.error = _rom.null)
IF (_rom.open <> %segnum%)
IF (_rom.t[%segnum%] = _rom.dynamic)
__rom_raise_error("Dynamic ROM segment closure failed", ["Bank #", $#(_rom.bnk[%segnum%]), " is not opened."])
ELSE
__rom_raise_error("ROM segment closure failed", ["Segment #", $#(_rom.seg[%segnum%]), " is not opened."])
ENDI
ELSE
_rom.pc QSET $
; Ignore legacy segments
IF (_rom.t[%segnum%] <> _rom.legacy)
; Close segment
__rom_check_segmem_range(_rom.pc, %segnum%)
; Keep track of current segment position
_rom.pos[%segnum%] QSET _rom.pc
; Compute usage statistics
__rom_segmem_used(%segnum%)
__rom_segmem_available(%segnum%)
ENDI
_rom.open QSET _rom.invalid ; Close segment %segnum%
.ROM.CurrentSeg QSET _rom.open
ENDI
ENDI
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_try_open_seg(segnum, min) ;;
;; Opens a given ROM segment if it has a minimum of "min" words available. ;;
;; ;;
;; NOTE: If a ROM segment is currently opened, this macro will not do ;;
;; anything. This lets us chain calls to __rom_try_open_seg() for ;;
;; all available segments, in order to attempt to find one with ;;
;; sufficient capacity. ;;
;; ;;
;; ARGUMENTS ;;
;; segnum The internal memory map segment to test and open. ;;
;; min The minimum size required, in 16-bit words. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_try_open_seg(segnum, min)
;
IF _EXPMAC ((_rom.open = _rom.invalid) AND (%segnum% < _rom.segcnt) AND ((_rom.pos[%segnum%] + (%min%)) < _rom.e[%segnum%]))
__rom_open_seg(%segnum%)
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_select_segment(seg) ;;
;; Relocates the program counter to a static ROM segment. Also closes the ;;
;; currently open segment, keeping track of its usage. ;;
;; ;;
;; ARGUMENTS ;;
;; seg The static ROM segment to open. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_select_segment(seg)
;
IF (_rom.error = _rom.null)
__rom_validate_segment(%seg%)
ENDI
IF (_rom.error = _rom.null)
_rom.segnum QSET _rom.segidx[%seg%]
_rom.type QSET _rom.t[_rom.segnum]
; Fail if the segment is dynamic
IF (_rom.type = _rom.dynamic)
__rom_raise_error(["Cannot select ROM segment #", $#(%seg%), " without a bank"], "Segment is dynamic.")
ENDI
; Open static segment
IF (_rom.type = _rom.static)
__rom_close_seg(_rom.open)
__rom_open_seg(_rom.segnum)
ENDI
ENDI
ENDM
;; ======================================================================== ;;
;; __rom_switch_mem_page(base, page) ;;
;; Switches a range of memory addresses to a target page. ;;
;; ;;
;; ARGUMENTS ;;
;; base The base address of the range to switch. ;;
;; page The target page number. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO __rom_switch_mem_page(base, page)
;
_rom.b_addr QSET ((%base%) AND $F000)
_rom.b_src QSET (_rom.b_addr OR $0A50 OR (%page%))
_rom.b_trg QSET (_rom.b_addr OR $0FFF)
LISTING "on"
MVII #_rom.b_src, R0 ; \_ Switch bank: [$s000 - $sFFF] to page
MVO R0, _rom.b_trg ; /
LISTING "prev"
ENDM
;; ======================================================================== ;;
;; __rom_stats_scale(val, scale) ;;
;; Returns value "val" scaled by "scale." The formula used for scaling is: ;;
;; ;;
;; return = ceil(val / scale) ;;
;; = [((val * base) / scale) + (base - 1)] / base ;;
;; ;;
;; ARGUMENTS ;;
;; val The value to scale. ;;
;; scale The scale to apply. ;;
;; ;;
;; OUTPUT ;;
;; ceil(val / scale). ;;
;; ======================================================================== ;;
MACRO __rom_stats_scale(val, scale)
(((((%val%) * 10) / (%scale%)) + 9) / 10)
ENDM
;; ======================================================================== ;;
;; __rom_stats_draw_line(style, len) ;;
;; Outputs a horizontal line, useful for displaying tabular information. ;;
;; ;;
;; ARGUMENTS ;;
;; style The line style to draw. Available values are: ;;
;; single Single (thin) line. ;;
;; double Double (thick) line. ;;
;; ;;
;; len The length of the line to draw, in characters. ;;
;; ;;
;; OUTPUT ;;
;; A horizontal line in the given style. ;;
;; ======================================================================== ;;
MACRO __rom_stats_draw_line(style, len)
_rom_stat.%style%[0, ((%len%) - 1)]
ENDM
;; ======================================================================== ;;
;; __rom_stats_pad_left(str, len) ;;
;; Outputs a string in a field of "len" characters, justified to the right ;;
;; and padded on the left with blank spaces. ;;
;; ;;
;; ARGUMENTS ;;
;; str The string to output. ;;
;; len The length of the field, in characters. ;;
;; ;;
;; OUTPUT ;;
;; The string, left-padded in the field. ;;
;; ======================================================================== ;;
MACRO __rom_stats_pad_left(str, len)
$(_rom_stat.space[0, ((%len%) - STRLEN(%str%) - 1)], %str%)
ENDM
;; ======================================================================== ;;
;; __rom_stats_pad_right(str, len) ;;
;; Outputs a string in a field of "len" characters, justified to the left ;;
;; and padded on the right with blank spaces. ;;
;; ;;
;; ARGUMENTS ;;
;; str The string to output. ;;
;; len The length of the field, in characters. ;;
;; ;;
;; OUTPUT ;;
;; The string, right-padded in the field. ;;
;; ======================================================================== ;;
MACRO __rom_stats_pad_right(str, len)
$(%str%, _rom_stat.space[0, ((%len%) - STRLEN(%str%) - 1)])
ENDM
;; ======================================================================== ;;
;; ROM.Setup map ;;
;; Configures and initializes the memory map indicated by "map." ;;
;; ;;
;; ARGUMENTS ;;
;; map The memory map number to initialize. ;;
;; ;;
;; OUTPUT ;;
;; _rom.error -1 on failure. ;;
;; ======================================================================== ;;
MACRO ROM.Setup map
;
LISTING "code"
__rom_reset_error
; Make sure the directive is called only once!
IF ((CLASSIFY(_rom.init) = -10000))
__rom_validate_map(%map%)
ELSE
__rom_raise_error(["ROM", ".Setup directive must be called only once per program."], _rom.null)
ENDI
IF (_rom.error = _rom.null)
_rom.init QEQU 1
_rom.map QSET %map%
_rom.ecs_off QSET _rom.null
_rom.extra_rom QSET 0
IF (intybasic_ecs)
_rom.ecs_req QSET intybasic_ecs
ELSE
_rom.ecs_req QSET _rom.null
ENDI
IF (intybasic_jlp)
_rom.jlp_req QSET intybasic_jlp
ELSE
_rom.jlp_req QSET _rom.null
ENDI
IF (intybasic_cc3 OR intybasic_jlp)
_rom.cart_ram QSET 1
ELSE
_rom.cart_ram QSET _rom.null
ENDI
; ---------------------------------------------------------
; Initialize ROM segments for active memory map.
;
; NOTE: Define below the segments available for each memory
; map supported. When defining a map, the following
; rules must be observed:
;
; - Map #0 must always be the "legacy" map.
; - All static segments in a map must be defined
; before any dynamic ones.
; - Segment numbers must start at zero.
; - Segment numbers should be defined in order.
; - There must not be gaps in segment numbers.
; ---------------------------------------------------------
; MAP #0: Legacy memory map wit no ROM management
IF (_rom.map = 0)
__rom_init_segmem(0, $5000, $FFFF, _rom.invalid, legacy)
ENDI
; MAP #1: Original Mattel 16K static memory map
IF (_rom.map = 1)
__rom_init_segmem(0, $5000, $6FFF, _rom.invalid, static)
__rom_init_segmem(1, $D000, $DFFF, _rom.invalid, static)
__rom_init_segmem(2, $F000, $FFFF, _rom.invalid, static)
ENDI
; MAP #2: JLP 42K static memory map
IF (_rom.map = 2)
_rom.ecs_off QSET 1
__rom_init_segmem(0, $5000, $6FFF, _rom.invalid, static)
__rom_init_segmem(1, $A000, $BFFF, _rom.invalid, static)
__rom_init_segmem(2, $C040, $FFFF, _rom.invalid, static)
IF (_rom.jlp_req <> _rom.null)
__rom_init_segmem(3, $2000, $2FFF, $F, static)
__rom_init_segmem(4, $7000, $7FFF, $F, static)
ELSE
__rom_init_segmem(3, $2100, $2FFF, _rom.invalid, static)
__rom_init_segmem(4, $7100, $7FFF, _rom.invalid, static)
ENDI
__rom_init_segmem(5, $4810, $4FFF, _rom.invalid, static)
ENDI
; MAP #3: Dynamic bank-switching 98K memory map - 4K banks
IF (_rom.map = 3)
_rom.ecs_off QSET 1
__rom_init_segmem(0, $5000, $6FFF, _rom.invalid, static)
__rom_init_segmem(1, $A000, $BFFF, _rom.invalid, static)
__rom_init_segmem(2, $C040, $FFFF, _rom.invalid, static)
__rom_init_segmem(3, $2000, $2FFF, $F, static)
__rom_init_segmem(4, $4810, $4FFF, _rom.invalid, static)
_rom.dynseg QSET _rom.segcnt
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $1, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $2, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $3, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $4, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $5, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $6, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $7, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $8, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $9, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $A, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $B, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $C, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $D, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $E, dynamic)
__rom_init_segmem(_rom.dynseg, $7000, $7FFF, $F, dynamic)
ENDI
; MAP #4: Dynamic bank-switching 154K memory map - 8K banks
IF (_rom.map = 4)
_rom.ecs_off QSET 1
__rom_init_segmem(0, $5000, $6FFF, _rom.invalid, static)
__rom_init_segmem(1, $A000, $BFFF, _rom.invalid, static)
__rom_init_segmem(2, $C040, $DFFF, _rom.invalid, static)
__rom_init_segmem(3, $2000, $2FFF, $F, static)
__rom_init_segmem(4, $7000, $7FFF, $F, static)
__rom_init_segmem(5, $4810, $4FFF, _rom.invalid, static)
_rom.dynseg QSET _rom.segcnt
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $0, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $2, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $3, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $4, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $5, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $6, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $7, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $8, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $9, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $A, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $B, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $C, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $D, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $E, dynamic)
__rom_init_segmem(_rom.dynseg, $E000, $FFFF, $F, dynamic)
ENDI
; MAP #5: Dynamic bank-switching 254K memory map - 16K banks
IF (_rom.map = 5)
_rom.ecs_off QSET 1
__rom_init_segmem(0, $5000, $6FFF, _rom.invalid, static)
__rom_init_segmem(1, $A000, $BFFF, _rom.invalid, static)
__rom_init_segmem(2, $2000, $2FFF, $F, static)
__rom_init_segmem(3, $7000, $7FFF, $F, static)
__rom_init_segmem(4, $4810, $4FFF, _rom.invalid, static)
_rom.dynseg QSET _rom.segcnt
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $0, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $2, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $3, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $4, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $5, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $6, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $7, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $8, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $9, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $A, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $B, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $C, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $D, dynamic)
__rom_init_segmem(_rom.dynseg, $C040, $FFFF, $E, dynamic)
ENDI
; MAP #6: Dynamic bank-switching 256K map -- 2 dynamic segments
IF (_rom.map = 6)
_rom.ecs_off QSET 1
__rom_init_segmem(0, $5000, $6FFF, _rom.invalid, static)
__rom_init_segmem(1, $C040, $DFFF, _rom.invalid, static)
__rom_init_segmem(2, $A000, $BFFF, $0, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $1, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $2, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $3, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $4, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $5, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $6, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $7, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $8, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $9, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $A, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $B, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $C, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $D, dynamic)
__rom_init_segmem(2, $A000, $BFFF, $E, dynamic)
__rom_init_segmem(3, $E000, $FFFF, $0, dynamic)