-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathforth.s
More file actions
3020 lines (2787 loc) · 75.5 KB
/
Copy pathforth.s
File metadata and controls
3020 lines (2787 loc) · 75.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
; forth.s — sw-cor24-forth DTC Forth: Phases 1-4 (bootstrap, threading, dictionary, interpreter)
; COR24 DTC Forth kernel
;
; Register allocation (frozen):
; r0 = W (work/scratch)
; r1 = RSP (return stack pointer, grows down from 0x0F0000)
; r2 = IP (instruction pointer for threaded code)
; sp = DSP (data stack, hardware push/pop in EBR)
; fp = limited scratch (only pop/push/add-as-source work)
;
; UART: data at 0xFF0100 (-65280), status at 0xFF0101 (-65279)
; TX busy = status bit 7, RX ready = status bit 0
;
; DTC NEXT (inlined at tail of every primitive, 5 bytes):
; lw r0, 0(r2) ; W = mem[IP] — fetch code address from thread
; add r2, 3 ; IP += cell
; jmp (r0) ; execute code
;
; Colon word CFA formats:
; Near (hand-assembled, within 127B of do_docol):
; bra do_docol ; 2 bytes
; .byte 0 ; 1 byte pad — PFA at CFA+3
; Far (runtime-compiled or distant):
; push r0 ; 1 byte — save CFA on data stack
; la r0, do_docol_far ; 4 bytes
; jmp (r0) ; 1 byte — PFA at CFA+6
;
; Dictionary header layout:
; .word link ; 3 bytes — link to previous entry (0 = end)
; .byte flags ; 1 byte — bit7=IMMEDIATE, bit6=HIDDEN, bits0-5=namelen
; .byte c1..cN ; N bytes — name characters
; (CFA follows immediately: CFA = entry + 4 + namelen)
; ============================================================
; Entry point (address 0)
; ============================================================
_start:
la r1, 983040 ; r1 = 0x0F0000 return stack base
; Snapshot hardware-reset sp into var_sp_base for underflow checks
mov fp, sp
push fp
pop r0 ; r0 = initial sp (push/pop is net-zero on sp)
la r2, var_sp_base
sw r0, 0(r2)
; Initialize system variables (r0, r2 free before Phase 1)
la r2, entry_ver
la r0, var_latest_val
sw r2, 0(r0) ; LATEST = last dictionary entry
la r2, dict_end
la r0, var_here_val
sw r2, 0(r0) ; HERE = first free byte
; ============================================================
; Launch threaded code tests (Phase 2 + Phase 3)
; ============================================================
la r2, test_thread ; IP = start of test thread
; NEXT — bootstrap into threaded execution
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ============================================================
; DOCOL — shared entry for colon definitions
; ============================================================
; Near DOCOL: CFA is "bra do_docol; .byte 0" (3 bytes), r0 = CFA from NEXT
do_docol:
add r1, -3
sw r2, 0(r1) ; push IP to return stack
mov r2, r0 ; r2 = CFA (from NEXT's jmp)
add r2, 3 ; r2 = PFA = CFA + 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; Far DOCOL: CFA is "push r0; la r0, do_docol_far; jmp (r0)" (6 bytes)
; CFA address was pushed to data stack by "push r0" in the CFA
do_docol_far:
add r1, -3
sw r2, 0(r1) ; push IP to return stack
pop r2 ; r2 = CFA (from data stack)
add r2, 6 ; r2 = PFA = CFA + 6
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ============================================================
; Primitives with Dictionary Headers
; ============================================================
; Chain: entry_emit(link=0) → entry_key → ... → entry_immediate(LATEST)
; ------------------------------------------------------------
; EMIT ( c -- ) : Write character to UART with TX busy-wait
; ------------------------------------------------------------
entry_emit:
.word 0
.byte 4
.byte 69, 77, 73, 84
do_emit:
pop r0 ; r0 = character
add r1, -3
sw r2, 0(r1) ; save IP on return stack
add r1, -3
sw r0, 0(r1) ; save byte on return stack
la r2, -65280 ; r2 = UART base
emit_poll:
lb r0, 1(r2) ; status (sign-extended; bit 7 → negative)
cls r0, z ; C = (status < 0) = TX busy
brt emit_poll
lw r0, 0(r1) ; restore byte
add r1, 3
sb r0, 0(r2) ; write byte to UART TX
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; KEY ( -- c ) : Read character from UART with RX busy-wait
; ------------------------------------------------------------
entry_key:
.word entry_emit
.byte 3
.byte 75, 69, 89
do_key:
add r1, -3
sw r2, 0(r1) ; save IP on return stack
key_poll:
la r0, -65280 ; UART base
lbu r0, 1(r0) ; status byte (zero-extended)
lcu r2, 1 ; bit 0 mask
and r0, r2 ; isolate RX ready bit
ceq r0, z ; C = (not ready)
brt key_poll
la r0, -65280 ; reload UART base
lbu r0, 0(r0) ; read byte
push r0
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; EXIT ( -- ) : End colon definition, pop IP from return stack
; ------------------------------------------------------------
entry_exit:
.word entry_key
.byte 4
.byte 69, 88, 73, 84
do_exit:
lw r2, 0(r1) ; restore IP from return stack
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; LIT ( -- x ) : Push inline literal from thread [HIDDEN]
; ------------------------------------------------------------
entry_lit:
.word entry_exit
.byte 67
.byte 76, 73, 84
do_lit:
lw r0, 0(r2) ; r0 = literal at IP
add r2, 3 ; IP past literal
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; BRANCH ( -- ) : Unconditional relative branch [HIDDEN]
; ------------------------------------------------------------
entry_branch:
.word entry_lit
.byte 70
.byte 66, 82, 65, 78, 67, 72
do_branch:
lw r0, 0(r2) ; r0 = signed offset
add r2, r0 ; IP += offset
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; 0BRANCH ( flag -- ) : Branch if TOS is zero [HIDDEN]
; ------------------------------------------------------------
entry_zbranch:
.word entry_branch
.byte 71
.byte 48, 66, 82, 65, 78, 67, 72
do_zbranch:
pop r0 ; r0 = flag
ceq r0, z ; C = (flag == 0)
brt zbr_take ; if zero, take branch
add r2, 3 ; skip offset cell
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
zbr_take:
lw r0, 0(r2) ; r0 = offset
add r2, r0 ; IP += offset
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ============================================================
; Arithmetic Primitives
; ============================================================
; + ( n1 n2 -- n1+n2 )
entry_plus:
.word entry_zbranch
.byte 1
.byte 43
do_plus:
pop fp ; fp = n2
pop r0 ; r0 = n1
add r0, fp ; r0 = n1 + n2
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; * ( n1 n2 -- n1*n2 )
entry_star:
.word entry_plus
.byte 1
.byte 42
do_star:
add r1, -3
sw r2, 0(r1) ; save IP
pop r2 ; r2 = n2
pop r0 ; r0 = n1
mul r0, r2 ; r0 = n1 * n2
push r0
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; - ( n1 n2 -- n1-n2 )
entry_minus:
.word entry_star
.byte 1
.byte 45
do_minus:
add r1, -3
sw r2, 0(r1) ; save IP
pop r2 ; r2 = n2
pop r0 ; r0 = n1
sub r0, r2 ; r0 = n1 - n2
push r0
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; /MOD ( n1 n2 -- rem quot ) : unsigned divide n1 by n2
entry_slashmod:
.word entry_minus
.byte 4
.byte 47, 77, 79, 68 ; "/MOD"
do_slashmod:
add r1, -3
sw r2, 0(r1) ; save IP. RS: [IP]
pop r2 ; r2 = n2 (divisor)
pop r0 ; r0 = n1 (dividend)
; Divide r0 / r2 → quotient in fp, remainder in r0
; Use repeated subtraction (same algorithm as . word)
add r1, -3
sw r2, 0(r1) ; save divisor. RS: [divisor, IP]
lc r2, 0 ; quotient = 0
slashmod_loop:
push r2 ; save quotient on DS
lw r2, 0(r1) ; r2 = divisor
clu r0, r2 ; C = (dividend < divisor)?
brt slashmod_done
sub r0, r2 ; dividend -= divisor
pop r2 ; r2 = quotient
add r2, 1
bra slashmod_loop
slashmod_done:
; r0 = remainder, TOS on DS = quotient
pop r2 ; r2 = quotient
add r1, 3 ; pop divisor. RS: [IP]
push r0 ; push remainder
push r2 ; push quotient
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; AND ( n1 n2 -- n1&n2 )
entry_and:
.word entry_slashmod
.byte 3
.byte 65, 78, 68
do_and:
add r1, -3
sw r2, 0(r1)
pop r2
pop r0
and r0, r2
push r0
lw r2, 0(r1)
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; OR ( n1 n2 -- n1|n2 )
entry_or:
.word entry_and
.byte 2
.byte 79, 82
do_or:
add r1, -3
sw r2, 0(r1)
pop r2
pop r0
or r0, r2
push r0
lw r2, 0(r1)
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; XOR ( n1 n2 -- n1^n2 )
entry_xor:
.word entry_or
.byte 3
.byte 88, 79, 82
do_xor:
add r1, -3
sw r2, 0(r1)
pop r2
pop r0
xor r0, r2
push r0
lw r2, 0(r1)
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; = ( n1 n2 -- flag ) : -1 if equal, 0 otherwise
entry_equal:
.word entry_xor
.byte 1
.byte 61
do_equal:
add r1, -3
sw r2, 0(r1)
pop r2
pop r0
ceq r0, r2 ; C = (n1 == n2)
lc r0, 0
brf eq_done
lc r0, -1
eq_done:
push r0
lw r2, 0(r1)
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; < ( n1 n2 -- flag ) : -1 if n1 < n2 signed, 0 otherwise
entry_less:
.word entry_equal
.byte 1
.byte 60
do_less:
add r1, -3
sw r2, 0(r1)
pop r2 ; n2
pop r0 ; n1
cls r0, r2 ; C = (n1 < n2) signed
lc r0, 0
brf lt_done
lc r0, -1
lt_done:
push r0
lw r2, 0(r1)
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; 0= ( n -- flag ) : -1 if zero, 0 otherwise
entry_zequ:
.word entry_less
.byte 2
.byte 48, 61
do_zequ:
pop r0
ceq r0, z
lc r0, 0
brf zeq_done
lc r0, -1
zeq_done:
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ============================================================
; Stack Primitives
; ============================================================
; DROP ( x -- )
entry_drop:
.word entry_zequ
.byte 4
.byte 68, 82, 79, 80
do_drop:
pop r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; DUP ( x -- x x )
entry_dup:
.word entry_drop
.byte 3
.byte 68, 85, 80
do_dup:
pop r0
push r0
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; SWAP ( x1 x2 -- x2 x1 )
entry_swap:
.word entry_dup
.byte 4
.byte 83, 87, 65, 80
do_swap:
pop r0 ; x2
pop fp ; x1
push r0 ; x2
push fp ; x1
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; OVER ( x1 x2 -- x1 x2 x1 )
entry_over:
.word entry_swap
.byte 4
.byte 79, 86, 69, 82
do_over:
pop r0 ; x2
pop fp ; x1
push fp ; x1
push r0 ; x2
push fp ; x1 copy
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; >R ( x -- ) ( R: -- x )
entry_tor:
.word entry_over
.byte 2
.byte 62, 82
do_tor:
pop r0
add r1, -3
sw r0, 0(r1)
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; R> ( -- x ) ( R: x -- )
entry_rfrom:
.word entry_tor
.byte 2
.byte 82, 62
do_rfrom:
lw r0, 0(r1)
add r1, 3
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; R@ ( -- x ) ( R: x -- x )
entry_rfetch:
.word entry_rfrom
.byte 2
.byte 82, 64
do_rfetch:
lw r0, 0(r1)
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ============================================================
; Memory Primitives
; ============================================================
; @ ( addr -- x ) : Fetch cell from address
entry_fetch:
.word entry_rfetch
.byte 1
.byte 64
do_fetch:
pop r0
lw r0, 0(r0)
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ! ( x addr -- ) : Store cell at address
entry_store:
.word entry_fetch
.byte 1
.byte 33
do_store:
add r1, -3
sw r2, 0(r1) ; save IP
; underflow check: need 2 cells
mov fp, sp
push fp
pop r0
add r0, 6 ; r0 = sp + 6 (post-pop sp)
la r2, var_sp_base
lw r2, 0(r2)
clu r2, r0 ; C = (sp_base < sp+6) → underflow
brt do_store_uflw
pop r2 ; addr
pop r0 ; value
sw r0, 0(r2)
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
do_store_uflw:
la r0, stack_underflow_err
jmp (r0)
; C@ ( addr -- c ) : Fetch byte from address
entry_cfetch:
.word entry_store
.byte 2
.byte 67, 64
do_cfetch:
pop r0
lbu r0, 0(r0)
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; C! ( c addr -- ) : Store byte at address
entry_cstore:
.word entry_cfetch
.byte 2
.byte 67, 33
do_cstore:
add r1, -3
sw r2, 0(r1) ; save IP
; underflow check: need 2 cells
mov fp, sp
push fp
pop r0
add r0, 6
la r2, var_sp_base
lw r2, 0(r2)
clu r2, r0
brt do_cstore_uflw
pop r2 ; addr
pop r0 ; byte value
sb r0, 0(r2)
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
do_cstore_uflw:
la r0, stack_underflow_err
jmp (r0)
; ============================================================
; HALT — infinite loop (not in dictionary, just a code target)
; ============================================================
do_halt:
halt_loop:
bra halt_loop
; ============================================================
; Phase 3: New Primitives
; ============================================================
; ------------------------------------------------------------
; EXECUTE ( cfa -- ) : Execute word at cfa
; ------------------------------------------------------------
entry_execute:
.word entry_cstore
.byte 7
.byte 69, 88, 69, 67, 85, 84, 69
do_execute:
add r1, -3
sw r2, 0(r1) ; save IP temporarily for the underflow check
; underflow check: need 1 cell
mov fp, sp
push fp
pop r0
add r0, 3
la r2, var_sp_base
lw r2, 0(r2)
clu r2, r0
brt do_execute_uflw
lw r2, 0(r1) ; restore IP (the executed word expects r2=IP)
add r1, 3
pop r0
jmp (r0)
do_execute_uflw:
la r0, stack_underflow_err
jmp (r0)
; ------------------------------------------------------------
; HERE ( -- addr ) : Push address of HERE variable
; ------------------------------------------------------------
entry_here:
.word entry_execute
.byte 4
.byte 72, 69, 82, 69
do_here:
la r0, var_here_val
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; LATEST ( -- addr ) : Push address of LATEST variable
; ------------------------------------------------------------
entry_latest:
.word entry_here
.byte 6
.byte 76, 65, 84, 69, 83, 84
do_latest:
la r0, var_latest_val
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; STATE ( -- addr ) : Push address of STATE variable
; ------------------------------------------------------------
entry_state:
.word entry_latest
.byte 5
.byte 83, 84, 65, 84, 69
do_state:
la r0, var_state_val
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; BASE ( -- addr ) : Push address of BASE variable
; ------------------------------------------------------------
entry_base:
.word entry_state
.byte 4
.byte 66, 65, 83, 69
do_base:
la r0, var_base_val
push r0
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; , ( x -- ) : Store cell at HERE, advance HERE by 3
; ------------------------------------------------------------
entry_comma:
.word entry_base
.byte 1
.byte 44
do_comma:
add r1, -3
sw r2, 0(r1) ; save IP
; underflow check: need 1 cell
mov fp, sp
push fp
pop r0
add r0, 3
la r2, var_sp_base
lw r2, 0(r2)
clu r2, r0
brt do_comma_uflw
la r0, var_here_val
lw r2, 0(r0) ; r2 = HERE
pop r0 ; r0 = value
sw r0, 0(r2) ; mem[HERE] = x
add r2, 3 ; HERE += 3
la r0, var_here_val
sw r2, 0(r0) ; update HERE
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
do_comma_uflw:
la r0, stack_underflow_err
jmp (r0)
; ------------------------------------------------------------
; C, ( c -- ) : Store byte at HERE, advance HERE by 1
; ------------------------------------------------------------
entry_ccomma:
.word entry_comma
.byte 2
.byte 67, 44
do_ccomma:
add r1, -3
sw r2, 0(r1) ; save IP
; underflow check: need 1 cell
mov fp, sp
push fp
pop r0
add r0, 3
la r2, var_sp_base
lw r2, 0(r2)
clu r2, r0
brt do_ccomma_uflw
la r0, var_here_val
lw r2, 0(r0) ; r2 = HERE
pop r0 ; r0 = byte
sb r0, 0(r2) ; mem[HERE] = c
add r2, 1 ; HERE += 1
la r0, var_here_val
sw r2, 0(r0) ; update HERE
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
do_ccomma_uflw:
la r0, stack_underflow_err
jmp (r0)
; ------------------------------------------------------------
; ALLOT ( n -- ) : Advance HERE by n bytes
; ------------------------------------------------------------
entry_allot:
.word entry_ccomma
.byte 5
.byte 65, 76, 76, 79, 84
do_allot:
add r1, -3
sw r2, 0(r1) ; save IP
; underflow check: need 1 cell
mov fp, sp
push fp
pop r0
add r0, 3
la r2, var_sp_base
lw r2, 0(r2)
clu r2, r0
brt do_allot_uflw
la r0, var_here_val
lw r2, 0(r0) ; r2 = HERE
pop r0 ; r0 = n
add r2, r0 ; HERE += n
la r0, var_here_val
sw r2, 0(r0) ; update HERE
lw r2, 0(r1) ; restore IP
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
do_allot_uflw:
la r0, stack_underflow_err
jmp (r0)
; ------------------------------------------------------------
; [ ( -- ) : Enter interpret mode [IMMEDIATE]
; ------------------------------------------------------------
entry_lbrac:
.word entry_allot
.byte 129
.byte 91
do_lbrac:
add r1, -3
sw r2, 0(r1)
la r2, var_state_val
lc r0, 0
sw r0, 0(r2) ; STATE = 0
lw r2, 0(r1)
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ------------------------------------------------------------
; ] ( -- ) : Enter compile mode
; ------------------------------------------------------------
entry_rbrac:
.word entry_lbrac
.byte 1
.byte 93
do_rbrac:
add r1, -3
sw r2, 0(r1)
la r2, var_state_val
lc r0, -1
sw r0, 0(r2) ; STATE = -1
lw r2, 0(r1)
add r1, 3
; NEXT
lw r0, 0(r2)
add r2, 3
jmp (r0)
; ============================================================
; FIND ( c-addr -- c-addr 0 | cfa 1 | cfa -1 )
; Search dictionary for counted string at c-addr.
; Returns cfa and flag (1=immediate, -1=normal) or 0 if not found.
;
; Uses data stack to pass entry pointer between iterations (avoids
; long backward branches). RS base frame:
; r1+0 = search_start (c-addr + 1)
; r1+3 = search_len
; r1+6 = c-addr (for not-found return)
; r1+9 = saved IP
; ============================================================
entry_find:
.word entry_rbrac
.byte 4
.byte 70, 73, 78, 68
do_find:
add r1, -3
sw r2, 0(r1) ; save IP RS: [IP]
pop r0 ; r0 = c-addr
add r1, -3
sw r0, 0(r1) ; save c-addr RS: [c-addr, IP]
lbu r2, 0(r0) ; r2 = search length
add r1, -3
sw r2, 0(r1) ; save search_len RS: [search_len, c-addr, IP]
add r0, 1 ; r0 = search name start
add r1, -3
sw r0, 0(r1) ; save search_start RS: [ss, sl, ca, IP]
; Load LATEST and push on DS for find_loop
la r0, var_latest_val
lw r0, 0(r0)
push r0 ; DS: [entry]
find_loop:
; Entry pointer is on data stack
pop r0 ; r0 = entry (0 = end of chain)
ceq r0, z
brf find_have_entry
; === Not found (inline handler) ===
lw r0, 6(r1) ; c-addr (RS offset 6)
add r1, 9 ; pop ss, sl, ca. RS: [IP]
push r0 ; DS: [c-addr]
lc r0, 0
push r0 ; DS: [0, c-addr]
lw r2, 0(r1)
add r1, 3
lw r0, 0(r2)
add r2, 3
jmp (r0)
find_have_entry:
; r0 = entry pointer
; Save entry on RS
add r1, -3
sw r0, 0(r1) ; RS: [entry, ss, sl, ca, IP]
; Load flags_len byte
lbu r2, 3(r0) ; r2 = flags_len
add r1, -3
sw r2, 0(r1) ; RS: [fl, entry, ss, sl, ca, IP]
; Check HIDDEN (bit 6): if hidden, skip via la+jmp
lcu r0, 64
and r0, r2
ceq r0, z
brt find_not_hidden
la r0, find_skip_entry
jmp (r0)
find_not_hidden:
; Extract name_len = flags_len & 0x3F
lw r0, 0(r1) ; r0 = flags_len
lcu r2, 63
and r0, r2 ; r0 = name_len
; Compare with search_len
lw r2, 9(r1) ; r2 = search_len (RS offset 9)
ceq r0, r2
brt find_len_match
la r0, find_skip_entry
jmp (r0)
find_len_match:
; === Lengths match — compare characters ===
; r0 = name_len = counter
; Save counter
add r1, -3
sw r0, 0(r1) ; RS: [ctr, fl, entry, ss, sl, ca, IP]
; ename_ptr = entry + 4
lw r0, 6(r1) ; entry (RS offset 6)
add r0, 4
add r1, -3
sw r0, 0(r1) ; RS: [ep, ctr, fl, entry, ss, sl, ca, IP]
; sname_ptr = search_start
lw r0, 12(r1) ; search_start (RS offset 12)
add r1, -3
sw r0, 0(r1) ; RS: [sp, ep, ctr, fl, entry, ss, sl, ca, IP]
find_cmp_loop:
lw r0, 6(r1) ; counter (RS offset 6)
ceq r0, z
brt find_matched
; Load entry char
lw r0, 3(r1) ; ename_ptr (RS offset 3)
lbu r2, 0(r0) ; r2 = entry char
; Load search char
lw r0, 0(r1) ; sname_ptr (RS offset 0)
lbu r0, 0(r0) ; r0 = search char
ceq r0, r2
brf find_char_fail
; Advance ename_ptr
lw r0, 3(r1)
add r0, 1
sw r0, 3(r1)