-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathresearch.txt
More file actions
2635 lines (1480 loc) · 43.5 KB
/
Copy pathresearch.txt
File metadata and controls
2635 lines (1480 loc) · 43.5 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
research.txt
Document
I have a p-code VM and a compiler for Pascal to p-code. The VM runs on a COR24 ISA emulator (and hardware, but not tested there yet). I want to create a BASIC interpreter that runs on the VM. Can you come up with an implementation plan and design for a 1977's-inspired BASIC with PEEK/POKE
Thought
Thought for 16s
You want a 1977-inspired BASIC interpreter that:
runs on your p-code VM, not directly on COR24,
is small, interactive, and educational,
exposes PEEK/POKE for low-level access,
fits a 24-bit integer-only machine,
can coexist cleanly with your Pascal→p-code toolchain,
and ideally helps validate the VM as a second front end, not just as an end-user language.
That is a very good fit for the layered design you were already converging on: generic VM/runtime first, languages above it. Your earlier notes already point toward keeping the VM language-neutral, with generic allocation, memory movement, traps, debug hooks, and primitive I/O below Pascal-specific behavior; BASIC slots naturally into that same model as another front end/runtime layer rather than as a special case.
Recommendation
Build this in three layers:
Layer 0 — p-code VM and runtime substrate
Keep this language-neutral.
It should own:
instruction set
globals / stack / heap layout
trap handling
primitive I/O hooks
debug/source mapping hooks
raw memory access primitives
That matches the separation you were already considering for Pascal and future languages.
Layer 1 — BASIC runtime
This is a thin library on top of the VM, providing:
console input/output
line editor
token buffer
program storage
variable table
expression evaluator helpers
PEEK/POKE helpers
GOSUB/RETURN stack
FOR/NEXT stack
string buffer utilities
Layer 2 — BASIC interpreter
This parses and executes tokenized BASIC lines.
For a first version, make it an interpreter of tokenized BASIC source, not a compiler to p-code. That keeps the project historically faithful and reduces risk.
Later, you can add:
optional “compile BASIC to p-code blocks”
optional direct-threaded/token-threaded execution
optional structured BASIC subset that compiles more aggressively
High-level design choice
There are two plausible designs.
Option A — classic interpreted BASIC
Store tokenized program lines and interpret them line-by-line.
Pros
closest to 1977 home-computer BASIC
easy to get a REPL working early
easy to save/list/edit programs
great for monitor-style hardware bring-up
natural fit for PEEK, POKE, PRINT, INPUT
Cons
slower
control-flow implementation is a little messier than compiled code
expression evaluator must be robust
Option B — BASIC front end compiling to p-code
Parse BASIC and emit p-code for each line or whole program.
Pros
faster
reuses more of the VM
aligns with long-term multi-language goals
Cons
farther from 1977 feel
harder to implement incrementally
more moving parts before first success
My recommendation
Start with Option A first, then optionally add Option B later.
That gives you:
an early interactive environment,
a second consumer of the VM runtime,
and a good hardware bring-up language.
BASIC dialect recommendation
Do not start with Microsoft BASIC compatibility as a hard goal.
Instead, define your own dialect:
“SWBASIC-77” goals
Inspired by late-1970s 8-bit BASICs:
line-numbered
integer-first
interactive
immediate mode + stored program mode
simple strings
GOTO, GOSUB, RETURN
IF ... THEN
FOR ... TO ... STEP ... NEXT
PRINT, INPUT
LIST, RUN, NEW
PEEK, POKE
maybe USR later
But with deliberate simplifications:
integer only at first
24-bit signed integers
no floating point
no arrays at v0 unless really needed
no user-defined functions at first
no DATA/READ/RESTORE initially
no file I/O initially
That keeps it aligned with your integer-only target and your VM-first plan. Your earlier design notes also emphasized a neutral VM with raw memory access and simple trap/debug support, which maps well to a small integer BASIC.
Language surface: v0 and v1
v0 — minimal interactive BASIC
Enough to prove the system.
Statements
LET A = expr
PRINT expr [, expr ...]
INPUT A
IF expr THEN line
GOTO line
END
REM ...
LIST
RUN
NEW
Functions
PEEK(addr)
ABS(x)
Example
10 LET A = PEEK(FF0101)
20 PRINT A
30 GOTO 10
v1 — actually useful 1977-style BASIC
Add:
GOSUB line
RETURN
FOR V = start TO end [STEP s]
NEXT V
POKE addr, value
PRINT "text"
string literals
LOAD / SAVE if you later add storage
STOP
CONT maybe later
Example
10 PRINT "BLINK"
20 POKE 65280,1
30 GOSUB 100
40 POKE 65280,0
50 GOSUB 100
60 GOTO 20
100 FOR I = 1 TO 1000
110 NEXT I
120 RETURN
Core semantic choices
Numeric model
Use exactly one numeric type in v0/v1:
signed 24-bit integer
wraparound arithmetic or trap-on-overflow — choose one and document it
I would recommend:
wraparound at VM level
optional BASIC overflow trap in debug mode
Why
matches the machine
makes POKE/address arithmetic simple
avoids soft-float complexity
very 1970s-friendly
Variable model
Start with:
scalar variables only
case-insensitive names
one or two characters plus optional suffix if you want retro flavor
Example:
A
I
X
AB
I would not start with full long identifiers unless you want a less period-authentic feel.
Recommendation
Support:
A..Z
optionally A0..Z9
later longer names
That makes the runtime table trivial.
String model
Keep strings simple:
string literals in source
print-only at first
optional string variables later
For v0:
allow "HELLO"
no string variables
For v1:
add string variables with fixed max length or heap-backed buffers
Because your VM plan already leaves room for string/object block types later, strings can be added without corrupting the generic runtime boundary.
Program representation
Use classic line-numbered storage.
Internal representation
Each stored line:
line number
byte length
tokenized content
next-line link or contiguous packed layout
Two layout choices
Option 1 — linked list of lines
Pros
simple insertion/deletion
natural for editing
Cons
more pointer chasing
more heap use
Option 2 — packed sorted buffer
Pros
compact
faster listing/run traversal
Cons
insertion/deletion requires shifting
Recommendation
For your environment, use:
packed program area for stored lines
sorted by line number
linear search at first
later optional line index table
This feels right for a small interpreter and is easy to inspect in the debugger.
Tokenization
Tokenize on line entry.
For example:
10 PRINT "HI",A
could become conceptually:
[LINE=10][TOK_PRINT][STR "HI"][TOK_COMMA][TOK_VAR A][EOL]
Why tokenize
smaller stored program
faster execution
easier parser
historically appropriate
Token categories
keywords: PRINT, IF, THEN, GOTO, FOR, ...
operators: + - * / = <> < <= > >=
delimiters: , ; ( )
identifiers
integer literals
string literals
Execution model
Immediate mode
If input line has no leading number:
tokenize
execute immediately
Example:
PRINT 2+2
POKE 65280,1
Program mode
If input line begins with a line number:
tokenize and store
replace existing line with same number
empty body deletes line
Example:
100 PRINT "HELLO"
Parser design
Do not build a full AST for stored BASIC initially.
That is overkill for 1977-style BASIC.
Instead:
Recommended parser model
tokenized line buffer
recursive-descent expression parser
statement dispatcher for each line
evaluation directly from token stream
Expression parser
Use Pratt or precedence-climbing.
Support:
unary + -
* /
+ -
comparisons
parentheses
variables
literals
PEEK(expr)
Statement dispatcher
Switch on first token:
LET
PRINT
INPUT
IF
GOTO
GOSUB
RETURN
FOR
NEXT
END
REM
implicit assignment if identifier starts line
Runtime state
A compact interpreter state struct is enough.
Suggested interpreter state
InterpreterState
program_start
program_end
current_line_ptr
next_line_ptr
vars[26 or N]
gosub_stack[]
for_stack[]
input_buffer
token_buffer
string_pool
running_flag
stopped_flag
error_code
Stacks needed
GOSUB stack
Store:
return line pointer
maybe return token offset if you later allow multiple statements per line
FOR stack
Store:
variable id
limit value
step value
line pointer to loop body restart point
PEEK / POKE design
This is one of the most important parts.
Semantics
PEEK(addr)
Returns a value from memory.
POKE addr, value
Writes a value to memory.
Width problem
Because your machine is 24-bit, decide what “one memory unit” means.
You have three plausible meanings:
Option A — byte-based
PEEK(addr) reads 8 bits
POKE addr,val writes 8 bits
Option B — word-based
reads/writes 24-bit words
Option C — both, with extensions
PEEK/POKE are byte-based
DEEK/DOKE or PEEKW/POKEW for words
Recommendation
Use byte-based PEEK/POKE, then add word access separately.
Why
historically familiar
best for UART/MMIO
best for character and raw hardware work
avoids ambiguity on non-word-aligned addresses
Then add one of:
PEEKW(addr)
POKEW addr,val
or
PEEK24(addr)
POKE24 addr,val
for your native word size.
Safety policy
This matters a lot.
In debug mode
bounds-check RAM access
trap writes to forbidden regions unless explicitly enabled
optionally distinguish code/data/MMIO
In raw mode
allow full access
Best compromise
Add a mode flag:
SAFE
UNSAFE
or a runtime command:
SYSTEM
MONITOR
But for v1, just document that PEEK/POKE can hit MMIO and code/data and are intentionally dangerous.
Hardware and system calls
You have two design choices for BASIC hardware access.
Option A — pure PEEK/POKE against memory map
Examples:
10 POKE 65280,1
20 A = PEEK(65281)
Pros
simple
authentic
transparent
Cons
exposes raw addresses everywhere
harder to port if memory map changes
Option B — add small built-ins
Examples:
LED 1
A = INP(port)
OUT port,val
Pros
nicer user experience
less magic-number dependence
Cons
less minimal
duplicates PEEK/POKE power
Recommendation
Start with PEEK/POKE only, then optionally add sugar later:
IN(addr)
OUT addr,val
CALL addr or USR(n) later
Error handling
Classic BASIC often printed terse errors. That is fine.
Suggested v1 errors
SYNTAX ERROR
WHAT?
BAD LINE NUMBER
OUT OF MEMORY
DIVISION BY ZERO
RETURN WITHOUT GOSUB
NEXT WITHOUT FOR
OUT OF DATA later if needed
BAD ADDRESS
STOPPED
Debug mode
Because you also want a debugger ecosystem, add richer internal error codes underneath the user-facing messages. Your earlier VM notes already emphasized explicit traps, frame visibility, and debugger-friendly metadata; keep that same philosophy here.
Interaction with your p-code VM
This is the architecture I recommend.
BASIC implementation language
Write the BASIC interpreter in one of two ways:
Path 1 — interpreter written in Pascal, compiled to p-code
This is elegant long-term.
Path 2 — interpreter written in p-code assembly first
This is probably the better bring-up path.
Given your earlier idea of adding a p-code assembler for VM/runtime testing, I would lean toward this split:
low-level runtime helpers in p-code assembly
BASIC interpreter core maybe in Pascal later
or write the initial BASIC in Pascal once Pascal is stable enough
That fits your already-established layering: generic VM first, then language-specific layers on top.
Practical recommendation
Do this in phases:
Phase A
Implement BASIC interpreter on the host first in C or Rust to validate semantics.
Phase B
Port/rewrite it to run on your VM.
Phase C
Optionally rewrite core parts in Pascal compiled to p-code.
That mirrors the historical “prototype in one language, later re-express/self-host” pattern you were already considering for Pascal itself.
Suggested command set
Immediate commands
LIST
RUN
NEW
CONT later
MEM
VARS
BYE
Statements
REM
LET
PRINT
INPUT
IF ... THEN
GOTO
GOSUB
RETURN
FOR ... TO ... STEP
NEXT
STOP
END
Functions
PEEK
ABS
RND later
USR later
PEEKW later
What I would postpone
Do not start with:
floating point
multidimensional arrays
DATA/READ/RESTORE
ON ... GOTO
string slicing
garbage collection
file I/O
tokenized binary save format
Microsoft BASIC compatibility edge cases
multiple statements per line with :
You can add : later, but it complicates return targets and line execution state.
Memory management
For BASIC specifically:
v0/v1
Use:
static program area
static variable table
fixed stacks for FOR and GOSUB
fixed string literal storage in tokenized lines
optional small heap only if you add dynamic strings
That works well with the generic runtime model you outlined earlier, where the VM owns raw allocation and memory primitives, while each language chooses its own policy. You even already called out BASIC as a good candidate for dynamic strings plus simple reference counting later, which is exactly the right “not now, maybe later” posture.
Recommendation
For first BASIC:
no GC
ideally no heap at all, unless strings force it
fixed-size buffers are fine
Debugger implications
Because this language will be used partly for bring-up, give it a debugger-friendly design.
Useful debugger views:
current BASIC source line
tokenized line bytes
current statement token pointer
variable table
FOR stack
GOSUB stack
interpreter state flags
recent PEEK/POKE activity
That matches your broader debugger direction: expose frames, memory, and interpreter-visible structures rather than only low-level machine state.
Implementation plan
Milestone 1 — language and runtime spec
Write:
basic77-language.md
basic77-tokens.md
basic77-memory.md
basic77-errors.md
Define exact syntax and behavior before coding too much.
Milestone 2 — tokenizer
Implement:
line number detection
keyword recognition
integer literals
string literals
identifier recognition
token dump tests
Tests:
tokenize PRINT 1+2
tokenize 10 GOTO 200
tokenize POKE 65280,1
Milestone 3 — immediate-mode evaluator
Support immediate execution only:
PRINT
assignment
expressions
PEEK
POKE
Tests:
PRINT 2+3*4
POKE addr,val
PRINT PEEK(addr)
Milestone 4 — stored program representation
Implement:
insert line
replace line
delete line
LIST
NEW
Tests:
add out-of-order lines
verify sorted listing
replace existing line
Milestone 5 — program runner
Add:
RUN
GOTO
IF THEN
END
Tests:
counter loop
branch test
line number lookup
Milestone 6 — subroutines and loops
Add:
GOSUB
RETURN
FOR
NEXT
Tests:
nested GOSUB
nested FOR
error cases
Milestone 7 — strings and console polish
Add:
quoted string printing
commas/semicolons in PRINT
better prompts and error messages
Milestone 8 — hardware demos
Write demo programs for: