-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathForth500.s
11114 lines (11114 loc) · 332 KB
/
Forth500.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
;-------------------------------------------------------------------------------
;
; FFFFFFFF OOOOOO RRRRRRR TTTTTTTT HH HH 55555555 0000 0000
; FF OO OO RR RR TT HH HH 55 00 00 00 00
; FF OO OO RR RR TT HH HH 55 00 00 00 00
; FF OO OO RR RR TT HH HH 55 00 00 00 00
; FFFFFF OO OO RRRRRRR TT HHHHHHHH 5555555 00 00 00 00
; FF OO OO RR RR TT HH HH 55 00 00 00 00
; FF OO OO RR RR TT HH HH 55 00 00 00 00
; FF OO OO RR RR TT HH HH 55 55 00 00 00 00
; FF OOOOOO RR RR TT HH HH 555555 0000 0000 v2.1
;
;
; Authors:
; Sébastien Furic (original incomplete pce500forth-v1)
; Dr. Robert van Engelen (Forth500)
;
;-------------------------------------------------------------------------------
;
; BSD 3-Clause License
;
; Copyright (c) 2021, Robert van Engelen
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice, this
; list of conditions and the following disclaimer.
;
; 2. Redistributions in binary form must reproduce the above copyright notice,
; this list of conditions and the following disclaimer in the documentation
; and/or other materials provided with the distribution.
;
; 3. Neither the name of the copyright holder nor the names of its
; contributors may be used to endorse or promote products derived from
; this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;
;-------------------------------------------------------------------------------
;
; Change org in Forth500.s according to the available RAM memory:
;
; 1. for machines with extra 64KB RAM card or larger and MEM$="B":
; org $b0000
;
; 2. for machines with extra 32KB RAM card and MEM$="B" and no E: drive:
; org $b1000
;
; 3. for unexpanded 32KB machines or MEM$="S1" and no E: drive:
; org $b9000
;
; Assemble to produce binary file Forth500.OBJ:
; $ XASM Forth500.s -O -L -S -TB
;
; Then remove the leading 6 header bytes from the Forth500.OBJ file:
; $ tail -c +7 Forth500.OBJ > Forth500.bin
;
; With a cassette interface such as CE-126p use PocketTools to load (use
; Forth500 org address &Bxx00 for option --addr):
; $ bin2wav --pc=E500 --type=bin --addr=0xBxx00 --sync=9 Forth500.bin
;
; On the PC-E500 execute (the specified Forth500 org address is &Bxx00):
; > POKE &BFE03,&1A,&FD,&0B,0,&FC-&xx,0: CALL &FFFD8
; This reserves &Bxx00-&BFC00 and resets the machine.
;
; Warning: memory cannot be allocated when in use by programs. To check if
; memory was allocated:
;
; > HEX PEEK &BFD1B
; xx
;
; The value xx shows that memory was allocated from &Bxx00 on (&BFD1C contains
; the low-order address byte, which is zero).
;
; Play the wav file, load and run Forth (the specified org address is &xx00):
; > CLOADM
; > CALL &Bxx00
;
; To remove Forth from memory and release its allocated RAM space:
; > POKE &BFE03,&1A,&FD,&0B,0,0,0: CALL &FFFD8
;
;-------------------------------------------------------------------------------
;
; FORTH500 CPU REGISTERS AND INTERNAL RAM USAGE
;
;-------------------------------------------------------------------------------
;
; BA top of stack (TOS)
; I unassigned, free to use`
; U stack pointer (SP) points to 2OS, grows to lower addresses
; S return stack pointer (RP), grows to lower addresses
; X instruction pointer (IP)
; Y unassigned, free to use
; (ll) floating point stack depth (8 bit)
; (wi) HERE pointer (3 bytes) in sync with [wi_value]
; (xi) FP floating point stack pointer (3 bytes)
; (yi) 11th segment $bxxxx for conversion of 16 bit to 20 bit addresses
; (fp) floating point working area (36 bytes)
;
;-------------------------------------------------------------------------------
;
; BP REGISTER OFFSET FOR BP-RELATIVE ADDRESSES
;
;-------------------------------------------------------------------------------
bp0: equ $70
;-------------------------------------------------------------------------------
;
; IMPLEMENTATION LOGIC REGISTERS (BP-RELATIVE ADDRESSES)
;
;-------------------------------------------------------------------------------
; floating point arguments for fop__
fp: equ $00 ; Floating point working area (36 bytes)
; 16 bit (8+8) registers
el: equ $20 ; Floating point extra working area
eh: equ $21 ; Floating point extra working area
ex: equ $20
fl: equ $22 ; Floating point extra working area
fh: equ $23 ; Floating point extra working area
fx: equ $22
gl: equ $24
gh: equ $25
gx: equ $24
hl: equ $26
hh: equ $27
hx: equ $26
il: equ $28
ih: equ $29
ix: equ $28
jl: equ $2a
jh: equ $2b
jx: equ $2a
kl: equ $2c
kh: equ $2d
kx: equ $2c
ll: equ $2e ; Floating point stack depth
lh: equ $2f
lx: equ $2e
; 20 bit registers
wi: equ $30 ; HERE pointer saved in [wi_value]
xi: equ $33 ; FP floating point stack pointer
yi: equ $36 ; Fixed segment $bxxxx address
zi: equ $39 ; Fixed segment $bxxxx address
;-------------------------------------------------------------------------------
;
; STANDARD LOGIC REGISTERS
;
;-------------------------------------------------------------------------------
bl: equ $d4
bh: equ $d5
bx: equ $d4
cl: equ $d6
ch: equ $d7
cx: equ $d6
dl: equ $d8
dh: equ $d9
dx: equ $d8
si: equ $da
di: equ $dd
;-------------------------------------------------------------------------------
;
; SYSTEM FILE AND IO CONTROL VECTORS
;
;-------------------------------------------------------------------------------
fcs: equ $fffe4
iocs: equ $fffe8
;-------------------------------------------------------------------------------
;
; FORTH500 SYSTEM PARAMETERS
;
;-------------------------------------------------------------------------------
base_address: equ $b0000 ; 11th segment address (do not change)
ib_size: equ 256 ; TIB and FIB size
hold_size: equ 40 ; ENVIRONMENT? /HOLD size in bytes
r_size: equ 256 ; The return stack size in bytes (must be 256)
s_size: equ 256 ; The stack size in bytes (must be 256, see ?STACK)
f_dmax: equ 10 ; The FP stack max depth
f_size: equ 120 ; The FP stack size in number of bytes, multiple of 12
r_beginning: equ $bfc00 ; The return stack's beginning
r_limit: equ r_beginning-r_size ; The return stack's low limit ($bfb00)
s_beginning: equ r_limit ; The stack's beginning
s_limit: equ s_beginning-s_size ; The stack's low limit ($bfa00)
f_beginning: equ s_limit ; The floating-point stack's beginning
f_limit: equ f_beginning-f_size ; The floating-point stack's low limit
dict_limit: equ f_limit ; The upper limit of the dictionary space
colon_sys: equ $fdef ; The colon-sys magic number
do_sys: equ $fedf ; The do-sys magic number
dest: equ $fdee ; The dest magic number
orig: equ $feed ; The orig magic number
;-------------------------------------------------------------------------------
;
; FORTH500 LOCATION AND BOOT ADDRESS
;
;-------------------------------------------------------------------------------
org $b0000 ; $b0000 or $b1000 or $b9000 ...
;-------------------------------------------------------------------------------
;
; FORTH500 BOOTING
;
;-------------------------------------------------------------------------------
boot: local
pre_on
and ($fb),$7f ; Disable interrupts
mv x,!bp_value ; System and Forth parameters
mv [x++],($ec) ; Save BP's current value
mv [x++],u ; Save U's current value
mv y,s
mv [x++],y ; Save S's current value
mv ($ec),!bp0 ; Set BP to bp0
mv u,!s_beginning ; Set U to its new value
mv s,!r_beginning ; Set S to its new value
or ($fb),$80 ; Enable interrupts
pre_off
mv ba,[$bfc97] ; Save symbols
mv [x++],ba ; to restore them
mv ba,[$bfc99] ; when returning to
mv [x],ba ; BASIC
mv (!ll),0 ; Set floating point stack depth to zero
mvp (!wi),[!wi_value] ; Set HERE value
mvp (!xi),!f_beginning ; Set FP to its new value
mvp (!yi),!startup_xt ; Set execution token STARTUP
mvp (!zi),(!yi) ; Set (zi) (base segment byte)
jp (!yi) ; STARTUP
endl
;-------------------------------------------------------------------------------
;
; SAVED E500 SYSTEM PARAMETERS
;
;-------------------------------------------------------------------------------
bp_value: ds 1 ; To restore BP
u_value: ds 3 ; To restore U
s_value: ds 3 ; To restore S
symbols: ds 4 ; To restore display's symbols when returning to BASIC
;-------------------------------------------------------------------------------
;
; FORTH500 REGISTERS IN EXTERNAL RAM
;
;-------------------------------------------------------------------------------
wi_value: dp _end_ ; Saved (wi) HERE pointer
hp_value: dp _end_ ; Temporary hold area pointer
;-------------------------------------------------------------------------------
;
; FORTH500 INTERNALS
;
;-------------------------------------------------------------------------------
docol_: dw $0000 ; Marks the start of the dictionary
db $03
db '(:)' ; Execute a colon definition in 40 cycles
docol__xt: local
mv i,x ; 2 ; I holds the IP
pushs i ; 6 ; Push IP (return address)
mv x,(!yi) ; 5 ; X holds the new IP
mv y,[x++] ; 7 ; Add 3 to IP to skip over jp docol__xt, Y is unused
endl ; + =20 cycles
;---------------
interp__: pre_on
test ($ff),$08 ; 5 ; Is break pushed?
pre_off
jrnz break__ ; 2/3 ; Break was pushed
;--------------- ; + =7 cycles
next__: mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
;--------------- ; + =13 cycles
break__: local
lbl1: mv i,1181 ; Set I to count 20ms debounce time
pre_on
lbl2: test ($ff),$08 ; 5 ; Test if the break key
pre_off
jrnz lbl1 ; 2/3 ; was intentionally released
dec i ; 3 ; (break action is triggered
jrnz lbl2 ; 2/3 ; when the break key is released)
endl
mv il,-28 ; User interrupt
;---------------
throw__: pushu ba ; Save TOS
mv ba,$ff00 ; Set high-order bits, standard error codes are always negative
add ba,il ; Set TOS to error code
mvw (!yi),!throw_xt ; Execution token of THROW
jp (!yi) ; Execute THROW
;-------------------------------------------------------------------------------
doret_: dw docol_
db $03
db '(;)' ; Return from a colon definition in 25 cycles
doret__xt: local
mvw (!yi),[s++] ; 7 ; Pop IP (return address)
mv x,(!yi) ; 5 ; X holds the IP
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =25 cycles
;-------------------------------------------------------------------------------
doexit_: dw doret_
db $06
db '(EXIT)' ; Exit a colon definition
doexit__xt: local
jr !doret__xt ; Same as (;)
endl
;-------------------------------------------------------------------------------
dolit0: dw doexit_
db $01
db '0' ; ( -- 0 )
dolit0_xt: local
pushu ba ; 4 ; Save old TOS
mv ba,0 ; 3 ; Set new TOS to 0 (FALSE)
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =20 cycles
;-------------------------------------------------------------------------------
dolit1: dw dolit0
db $01
db '1' ; ( -- 1 )
dolit1_xt: local
pushu ba ; 4 ; Save old TOS
mv ba,1 ; 3 ; Set new TOS to 1
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =20 cycles
;-------------------------------------------------------------------------------
dolit2: dw dolit1
db $01
db '2' ; ( -- 2 )
dolit2_xt: local
pushu ba ; 4 ; Save old TOS
mv ba,2 ; 3 ; Set new TOS to 2
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =20 cycles
;-------------------------------------------------------------------------------
dolit3: dw dolit2
db $01
db '3' ; ( -- 3 )
dolit3_xt: local
pushu ba ; 4 ; Save old TOS
mv ba,3 ; 3 ; Set new TOS to 2
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =20 cycles
;-------------------------------------------------------------------------------
dolitm1: dw dolit3
db $02
db '-1' ; ( -- -1 )
dolitm1_xt: local
pushu ba ; 4 ; Save old TOS
mv ba,-1 ; 3 ; Set new TOS -1 (TRUE or $ffff)
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =20 cycles
;-------------------------------------------------------------------------------
doflit0: dw dolitm1
db $04
db '0E+0' ; ( F: -- 0e+0 )
doflit0_xt: local
mv il,12
sbcl (!fp),(!fp) ; Set (fp) to zero
jr !fppush__ ; Push (fp) to the FP stack
endl
;-------------------------------------------------------------------------------
true: dw doflit0
db $04
db 'TRUE' ; ( -- -1 )
true_xt: local
jr !dolitm1_xt ; TRUE is -1
endl
;-------------------------------------------------------------------------------
false: dw true
db $05
db 'FALSE' ; ( -- 0 )
false_xt: local
jr !dolit0_xt ; FALSE is 0
endl
;-------------------------------------------------------------------------------
dolit_: dw false
db $05
db '(LIT)' ; ( -- x )
dolit__xt: local
pushu ba ; 4 ; Save old TOS
mv ba,[x++] ; 5 ; Set new TOS and move IP to next token
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =22 cycles
;-------------------------------------------------------------------------------
do2lit_: dw dolit_
db $06
db '(2LIT)' ; ( -- xd )
do2lit__xt: local
pushu ba ; 4 ; Save old TOS
mv ba,[x++] ; 5 ; Fetch the high-order 16 bits (next token) and set new TOS
mv i,[x++] ; 5 ; Fetch the low-order 16 bits (next token)
pushu i ; 4 ; Push the low-order 16 bits one the stack as 2OS
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =31 cycles
;-------------------------------------------------------------------------------
doflit_: dw do2lit_
db $06
db '(FLIT)' ; ( F: -- r )
doflit__xt: local
mv il,12
mvl (!fp),[x++] ; Copy float literal to (fp) and update IP to skip the float
endl
;---------------
fppush__: mv y,(!xi) ; Y holds the FP
mv il,12
mvl [--y],(!fp+11) ; Copy (fp) to new FP TOS
mv (!xi),y ; Update FP
fppushcheck__: inc (!ll) ; Increment FP stack size
fpcheck__: cmp (!ll),!f_dmax+1 ; Check FP stack size
jrc !next__
mv il,-44 ; Floating-point stack overflow
jr !throw__
;-------------------------------------------------------------------------------
doslit_: dw doflit_
db $06
db '(SLIT)' ; ( -- c-addr u )
doslit__xt: local
pushu ba ; Save old TOS
mv ba,[x++] ; Read the length of the string from address IP
mv i,x ; I holds the short address of the string
pushu i ; Save it on the stack
add x,ba ; Update IP to skip up to the end of the string
jr !next__
endl
;-------------------------------------------------------------------------------
dovar_: dw doslit_
db $05
db '(VAR)' ; ( -- addr )
dovar__xt: local
pushu ba ; 4 ; Save old TOS
mv ba,3 ; 3
mv i,(!yi) ; 4
add ba,i ; 5
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =29 cycles
;-------------------------------------------------------------------------------
docon_: dw dovar_
db $05
db '(CON)' ; ( -- x )
docon__xt: local
pushu ba ; 4 ; Save old TOS
mv ba,[(!yi)+3] ; 10
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =27 cycles
;-------------------------------------------------------------------------------
do2con_: dw docon_
db $06
db '(2CON)' ; ( -- xd )
do2con__xt: local
pushu ba ; Save old TOS
mv y,(!yi) ; 5
mv ba,[y+3] ; 7
mv i,[y+5] ; 7
pushu i ; 4 ; Push the 16 low-order bits
mvw (!yi),[x++] ; 7 ; Fetch new execution token (yi)
jp (!yi) ; 6 ; Execute execution token
endl ; + =36 cycles
;-------------------------------------------------------------------------------
dofcon_: dw do2con_
db $06
db '(FCON)' ; ( F: -- r )
dofcon__xt: local
;mv (!yi),i
mv il,12 ; Copy 12 bytes
mvl (!fp),[(!yi)+3] ; Copy float constant to (fp)
jr !fppush__ ; Push (fp) to the FP stack
endl
;-------------------------------------------------------------------------------
doval_: dw dofcon_
db $05
db '(VAL)' ; ( -- x )
doval__xt: local
jr !docon__xt ; Same code as DOCON
endl
;-------------------------------------------------------------------------------
do2val_: dw doval_
db $06
db '(2VAL)' ; ( -- xd )
do2val__xt: local
jr !do2con__xt ; Same code as DO2CON
endl
;-------------------------------------------------------------------------------
dofval_: dw do2val_
db $06
db '(FVAL)' ; ( F: -- r )
dofval__xt: local
jr !dofcon__xt ; Same code as DOFCON
endl
;-------------------------------------------------------------------------------
dodefer_: dw dofval_
db $05
db '(DEF)' ; Execute deferred word
dodefer__xt: local
;pre_on ; Check break just in case a deferred word has an infinite cycle
;test ($ff),$08 ; Is break pushed?
;pre_off
;jpnz !break__ ; Break was pushed
mvw (!yi),[(!yi)+3] ; Read xt of the deferred word
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
does_: dw dodefer_
db $06
db '(DOES)' ; ( -- addr ; R: xt -- ret )
does__xt: local
pushu ba ; Save TOS
mv ba,3 ; Compute the
mv i,(!yi) ; address of the
add ba,i ; data on TOS
mvw (!yi),[s] ; Pop CALL does__xt return short address as the new IP
mv i,x ; Save the old IP
mv [s],i ; as return address
mv x,(!yi) ; X holds the IP of the execution tokens after the CALL does__xt
jp !next__
endl
;-------------------------------------------------------------------------------
sc_code_: dw does_
db $07
db '(;CODE)' ; ( -- )
sc_code__xt: local
mvw (!yi),[!lastxt_xt+3] ; (yi) holds the address of the 'jp' instruction
mv i,x ; I holds the address of the token after (;CODE)
mv [(!yi)+1],i ; Compile a 'jp' to the token after (;CODE) e.g. CALL does__xt
jp !doret__xt ; Perform a (;)
endl
;-------------------------------------------------------------------------------
ahead_: dw sc_code_
db $07
db '(AHEAD)' ; ( -- )
ahead__xt: local
mv i,[x++] ; Read the number of bytes to jump
add x,i ; Skip forward the specified number of bytes
mvw (!yi),[x++] ; Fetch new execution token (yi)
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
again_: dw ahead_
db $07
db '(AGAIN)' ; ( -- )
again__xt: local
mv i,[x++] ; Read the number of bytes to jump
sub x,i ; Skip backward the specified number of bytes
jp !quest_stack_xt ; Check stack overflow/underflow
endl
;-------------------------------------------------------------------------------
if_: dw again_
db $04
db '(IF)' ; ( flag -- )
if__xt: local
mv i,[x++] ; Read the number of bytes to jump
inc ba ; Test the TOS
dec ba ;
popu ba ; Set new TOS
jpnz !next__ ; If TOS is nonzero, continue next token
add x,i ; Skip forward the specified number of bytes
jp !next__
endl
;-------------------------------------------------------------------------------
of_: dw if_
db $04
db '(OF)' ; ( x x -- |x )
of__xt: local
popu i ; I holds the 2OS
sub ba,i ; Test if the TOS equals 2OS
mv ba,[x++] ; Read the number of bytes to jump
jpz !drop_xt ; If match, set new TOS and continue next token
add x,ba ; Skip forward the specified number of bytes
mv ba,i ; Set new TOS to old 2OS
jp !next__
endl
;-------------------------------------------------------------------------------
until_: dw of_
db $07
db '(UNTIL)' ; ( flag -- )
until__xt: local
mv i,[x++] ; Read the number of bytes to jump
inc ba ; Test the TOS
dec ba ;
popu ba ; Set new TOS
jpnz !next__
sub x,i ; Skip backward the specified number of bytes
jp !quest_stack_xt ; Check stack overflow/underflow
endl
;-------------------------------------------------------------------------------
do_: dw until_
db $04
db '(DO)' ; ( n|u n|u -- )
do__xt: popu i ; I holds the loop limit and BA the initial value
pushu ba ; Save the initial value on the parameter stack
mv ba,[x++] ; BA holds the LEAVE address (to exit DO statement)
;---------------
do__: local
add ba,x ; The effective LEAVE address is a jump forward
pushs ba ; Save the LEAVE address
mv ba,$8000 ; Perform a 'slice'
add i,ba ; of the loop limit
pushs i ; Save the 'sliced' loop limit
popu ba ; Restore the initial value
sub ba,i ; Perform the 'slice' operation on the initial value
pushs ba ; Save the initial value
jp !drop_xt ; Set new TOS and continue next token
endl
;-------------------------------------------------------------------------------
quest_do_: dw do_
db $05
db '(?DO)' ; ( n|u n|u -- )
quest_do__xt: local
popu i ; I holds the loop limit and BA the initial value
pushu ba ; Save the initial value
sub ba,i ; Test if these two values are equal
mv ba,[x++] ; BA holds the short LEAVE address (to exit ?DO statement)
jrnz !do__ ; Execute (DO) if the initial value is not the final value
add x,ba ; Jump forward to the end of the DO statement
popu i ; Discard the initial value
jp !drop_xt ; Set new TOS and continue next token
endl
;-------------------------------------------------------------------------------
loop_: dw quest_do_
db $06
db '(LOOP)' ; ( -- )
loop__xt: local
pushu ba ; Save TOS
pops i ; Restore the loop counter's current value
inc i ; Increment the loop counter
mv ba,$8000 ; Test if
sub ba,i ; overflow occurred
mv ba,[x++] ; Read the number of bytes to jump
jrz lbl1
sub x,ba ; Jump backward to the beginning of the DO statement
pushs i ; Save the new loop counter's value
popu ba ; Restore the TOS
jp !quest_stack_xt ; Check stack overflow/underflow
lbl1: pops i ; Discard the loop parameters
pops i ; (only the loop limit and LEAVE address are on the stack)
jp !drop_xt ; Set new TOS and continue next token
endl
;-------------------------------------------------------------------------------
plus_loop_: dw loop_
db $07
db '(+LOOP)' ; ( n -- )
plus_loop__xt: local
pops i ; Restore the loop counter's current value
ex a,b
test a,$80
ex a,b
jrnz lbl2
add ba,i ; Increment the loop counter
pushs ba ; Save its value on the stack
add ba,ba ; Test the sign of the result
jrnc lbl1
add i,i ; Test the sign of the previous value
jrnc lbl3
lbl1: mv ba,[x++] ; Read the number of bytes to jump
sub x,ba ; Jump backward to the beginning of the DO statement
popu ba ; Set new TOS
jp !quest_stack_xt ; Check stack overflow/underflow
lbl2: add ba,i ; Increment the loop counter with negative BA
pushs ba ; Save its value on the stack
add ba,ba ; Test the sign of the result
jrc lbl1
add i,i ; Test the sign of the previous value
jrnc lbl1
lbl3: mv ba,[x++] ; Discard the number of bytes to jump
popu ba ; Set new TOS
jr !unloop__xt ; Discard the loop parameters
endl
;-------------------------------------------------------------------------------
unloop_: dw plus_loop_
db $08
db '(UNLOOP)' ; ( R: loop-sys -- )
unloop__xt: local
mv il,6 ; Discard
add s,il ; all three loop parameters
jp !next__
endl
;-------------------------------------------------------------------------------
leave_: dw unloop_
db $07
db '(LEAVE)' ; ( R: loop-sys -- )
leave__xt: local
pops i ; Discard two loop parameters
pops i ;
mvw (!yi),[s++]
mv x,(!yi) ; Skip up to the end of the DO statement
jp !next__
endl
;-------------------------------------------------------------------------------
qst_leave_: dw leave_
db $08
db '(?LEAVE)' ; ( x -- ; R: loop-sys -- ) or ( 0 -- )
qst_leave__xt: local
inc ba ; Test the
dec ba ; TOS
popu ba ; Set new TOS
jrnz !leave__xt ; If TOS is nonzero, leave the loop
jp !next__ ; Else continue
endl
;-------------------------------------------------------------------------------
noop: dw qst_leave_
db $04
db 'NOOP' ; ( -- )
noop_xt: local
jp !next__ ; Does nothing
endl
;-------------------------------------------------------------------------------
blnk: dw noop
db $02
db 'BL' ; ( -- 32 )
blnk_xt: local
pushu ba ; Save the TOS
mv ba,32 ; Set new TOS to space (ASCII 32)
jp !next__
endl
;-------------------------------------------------------------------------------
align: dw blnk
db $05
db 'ALIGN'
align_xt: local
jr !noop_xt ; Does nothing
endl
;-------------------------------------------------------------------------------
aligned: dw align
db $07
db 'ALIGNED'
aligned_xt: local
jr !noop_xt ; Does nothing
endl
;-------------------------------------------------------------------------------
f_align: dw aligned
db $06
db 'FALIGN'
f_align_xt: local
jr !noop_xt ; Does nothing
endl
;-------------------------------------------------------------------------------
f_aligned: dw f_align
db $08
db 'FALIGNED'
f_aligned_xt: local
jr !noop_xt ; Does nothing
endl
;-------------------------------------------------------------------------------
cell_plus: dw f_aligned
db $05
db 'CELL+' ; ( addr -- addr )
cell_plus_xt: local
jp !two_plus_xt ; Same as 2+
endl
;-------------------------------------------------------------------------------
cells: dw cell_plus
db $05
db 'CELLS' ; ( u -- u )
cells_xt: local
jp !two_star_xt ; Same as 2*
endl
;-------------------------------------------------------------------------------
cell: dw cells
db $04
db 'CELL' ; ( -- 2 )
cell_xt: local
jp !dolit2_xt ; Same as 2
endl
;-------------------------------------------------------------------------------
char_plus: dw cell
db $05
db 'CHAR+' ; ( c-addr -- c-addr )
char_plus_xt: local
jp !one_plus_xt ; Same as 1+
endl
;-------------------------------------------------------------------------------
chars: dw char_plus
db $05
db 'CHARS' ; ( u -- u )
chars_xt: local
jr !noop_xt ; Does nothing
endl
;-------------------------------------------------------------------------------
float_plus: dw chars
db $06
db 'FLOAT+' ; ( f-addr -- f-addr )
float_plus_xt: local
mv il,12 ; Set I to 12
add ba,i ; Increment TOS by 12
jp !next__
endl
;-------------------------------------------------------------------------------
floats: dw float_plus
db $06
db 'FLOATS' ; ( u -- u )
floats_xt: local
add ba,ba
add ba,ba
mv i,ba
add ba,ba
add ba,i ; Set TOS to 12 times old TOS
jp !next__
endl
;-------------------------------------------------------------------------------
store: dw floats
db $01
db '!' ; ( x addr -- )
store_xt: local
mv (!yi),ba ; The address where to store the value
popu ba ; BA holds the value to store
endl
;---------------
store__: local
mv [(!yi)],ba ; Store the value in memory
popu ba ; Set new TOS
mvw (!yi),[x++] ; Fetch new execution token (yi)
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
doto_: dw store
db $04
db '(TO)' ; ( x "<spaces>name" -- )
doto__xt: local
mvw (!yi),[x++] ; The address where to store the value
jr !store__
endl
;-------------------------------------------------------------------------------
fetch: dw doto_
db $01
db '@' ; ( addr -- x)
fetch_xt: local
mv (!yi),ba ; The address where to fetch the value
mv ba,[(!yi)] ; Set new TOS
mvw (!yi),[x++] ; Fetch new execution token (yi)
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
two_store: dw fetch
db $02
db '2!' ; ( xd addr -- )
two_store_xt: local
mv (!yi),ba
popu ba ; BA holds the 16 high-order bits to store
endl
;---------------
two_store__: local
mv y,(!yi) ; Y holds the address where to store the value
mv [y++],ba ; Store the 16 high-order bits in memory
popu ba ; BA holds the 16 low-order bits to store
mv [y],ba ; Store the 16 low-order bits in memory
popu ba ; Set new TOS
mvw (!yi),[x++] ; Fetch new execution token (yi)
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
do2to_: dw two_store
db $05
db '(2TO)' ; ( xd "<spaces>name" -- )
do2to__xt: local
mvw (!yi),[x++] ; The address where to store the value
jr !two_store__
endl
;-------------------------------------------------------------------------------
two_fetch: dw do2to_
db $02
db '2@' ; ( addr -- xd )
two_fetch_xt: local
mv (!yi),ba
mv y,(!yi) ; Y holds the address where to fetch the 16 low-order bits
mv ba,[y++] ; Fetch the 16 high-order bits (and set new TOS)
mv i,[y] ; Fetch the 16 low-order bits
pushu i ; Push the 16 low-order bits
mvw (!yi),[x++] ; Fetch new execution token (yi)
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
c_store: dw two_fetch
db $02
db 'C!' ; ( char c-addr -- )
c_store_xt: local
mv (!yi),ba ; The address where to store the value
popu ba ; A holds the character value
mv [(!yi)],a ; Store the 8 low-order bits in memory
popu ba ; Set new TOS
mvw (!yi),[x++] ; Fetch new execution token (yi)
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
c_fetch: dw c_store
db $02
db 'C@' ; ( c-addr -- char )
c_fetch_xt: local
mv (!yi),ba ; The address where to fetch the value
mv il,[(!yi)] ; Read byte into I (IL extends 0 high byte to I)
mv ba,i ; Set new TOS
mvw (!yi),[x++] ; Fetch new execution token (yi)
jp (!yi) ; Execute execution token
endl
;-------------------------------------------------------------------------------
comma: dw c_fetch
db $01
db ',' ; ( x -- )
comma_xt: local
mv [(!wi)],ba ; Copy TOS to [HERE]
popu ba ; Set new TOS
mv il,2 ; Increment HERE by 2
jr !allot__
endl
;-------------------------------------------------------------------------------
compile_com: dw comma
db $08
db 'COMPILE,' ; ( x -- )
compile_com_xt: local
jr !comma_xt ; Same code as comma in this implementation
endl
;-------------------------------------------------------------------------------
cfa_comma: dw compile_com
db $04
db 'CFA,' ; ( jp-addr -- )
cfa_comma_xt: local
mv y,(!wi) ; Y holds HERE value
mv il,$02 ; IL holds the 'jp' opcode
mv [y++],il ; Compile 'jp' instruction
mv [y],ba ; Compile the address of the interpretation routine
popu ba ; Set new TOS
mv il,3 ; Increment HERE by 3
jr !allot__
endl
;-------------------------------------------------------------------------------
does_comma: dw cfa_comma
db $05
db 'DOES,' ; ( -- )
does_comma_xt: local
mv y,(!wi) ; Y holds HERE value
mv il,$04 ; IL holds the 'call' opcode
mv [y++],il ; Compile 'call does__xt' instruction
mv i,!does__xt
mv [y],i ; Compile the address of the (DOES) routine
mv il,3 ; Increment HERE by 3
jr !allot__
endl
;-------------------------------------------------------------------------------
allot: dw does_comma
db $05
db 'ALLOT' ; ( n -- )
allot_xt: local
mv i,ba ; I holds the size to allocate
popu ba ; Set new TOS
endl
;---------------
allot__: local
pushu ba ; Save TOS
mv ba,(!wi) ; BA holds HERE short address
add ba,i ; New HERE value short address
jrc lbl2 ; Negative increment or overflow?
add i,i ; Check if positive increment
jrc lbl3 ; Overflow if increment is negative
mv i,!dict_limit-!hold_size
sub ba,i ; Check overflow
jrnc lbl3 ; for positive increment
lbl1: add ba,i ; Restore new HERE short address
mv (!wi),ba ; Save new HERE register short address
mv [!wi_value],ba ; Save new HERE constant short address
jp !drop_xt ; Set new TOS and continue next token
lbl2: add i,i ; Check if negative increment
jrnc lbl3 ; Overflow if increment is positive
mv i,!_end_ ; Check
sub ba,i ; if
jrnc lbl1 ; underflow
lbl3: mv il,-8 ; Dictionary overflow
jp !throw__
endl
;-------------------------------------------------------------------------------
c_comma: dw allot
db $02
db 'C,' ; ( char -- )
c_comma_xt: local
mv [(!wi)],a ; Store lower-order TOS at HERE
popu ba ; Set new TOS
mv il,1 ; Increment HERE by 1
jr !allot__
endl
;-------------------------------------------------------------------------------
two_comma: dw c_comma
db $02
db '2,' ; ( xd -- )
two_comma_xt: local
mv y,(!wi) ; Y holds HERE value
mv [y++],ba ; Store the 16 high-order bits in memory and post-increment Y
popu ba
mv [y],ba ; Store the 16 low-order bits in memory
popu ba ; Set new TOS
mv il,4 ; Increment HERE by 4
jr !allot__
endl
;-------------------------------------------------------------------------------
f_comma: dw two_comma