-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathnative_words.asm
11991 lines (9860 loc) · 361 KB
/
native_words.asm
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
; Low-level Forth word routines
; Tali Forth 2 for the 65c02
; Scot W. Stevenson <[email protected]>
; First version: 19. Jan 2014
; This version: 03. Jan 2018
; This list is ordered alphabetically by the names of the words, not their
; strings (so "!" is sorted as "STORE"). However, we start off with COLD,
; ABORT, and QUIT as the natural start sequence. Each word has two special
; status lines that begins with "; ## ", which allows auto-generation of the
; WORDLIST.md file and other entries in the docs folder. Status entries are:
; TBA --> fragment --> coded --> tested --> auto
; "Auto" means that the word is automatically tested by the test suite (good),
; "tested" means that it was tested by hand in some way (okay), "coded" means
; it hasn't been tested at all (bad). See the test suite for more details.
; ## COLD ( -- ) "Reset the Forth system"
; ## "cold" tested Tali Forth
; """Reset the Forth system. Does not restart the kernel,
; use the 65c02 reset for that. Flows into ABORT.
; """
xt_cold:
cld
; Set the OUTPUT vector to the default kernel_putc
; We do this really early so we can print error messages
; during debugging
lda #<kernel_putc
sta output
lda #>kernel_putc
sta output+1
; Load all of the important zero page variables from ROM
ldx #cold_zp_table_end-cold_zp_table-1
_load_zp_loop:
; This loop loads them back to front. We can use X here
; because Tali hasn't started using the stack yet.
lda cold_zp_table,x
sta zpage,x
dex
bne _load_zp_loop
; Copy the 0th element.
lda cold_zp_table
sta zpage
; Initialize 65c02 stack (Return Stack)
ldx #rsp0
txs
; Clear Data Stack. This is repeated in ABORT, but this way we
; can load high-level words with EVALUATE
ldx #dsp0
; Initialize the user variables.
ldy #cold_user_table_end-cold_user_table-1
lda #0
_load_user_vars_loop:
; Like the zero page variables, these are initialized
; back to front.
lda cold_user_table,y
sta (up),y
dey
bne _load_user_vars_loop
; Copy the 0th element.
lda cold_user_table
sta (up)
jsr xt_cr
; Define high-level words in forth_words.asm via EVALUATE. If
; you do not have any high-level words, this part can be
; commented out.
dex
dex
dex
dex
; start address goes NOS
lda #<forth_words_start
sta 2,x
lda #>forth_words_start
sta 3,x
; length goes TOS
lda #<forth_words_end
sec
sbc #<forth_words_start
sta 0,x
lda #>forth_words_end
sbc #>forth_words_start
sta 1,x
jsr xt_evaluate
; Now define any user words via EVALUATE. If you do not have
; any user-defined words, this part can be commented out as
; well.
dex
dex
dex
dex
; start address goes NOS
lda #<user_words_start
sta 2,x
lda #>user_words_start
sta 3,x
; length goes TOS
lda #<user_words_end
sec
sbc #<user_words_start
sta 0,x
lda #>user_words_end
sbc #>user_words_start
sta 1,x
jsr xt_evaluate
; Initialize all of the history buffers by putting a zero in
; each length byte.
stz hist_buff
stz hist_buff+$80
stz hist_buff+$100
stz hist_buff+$180
stz hist_buff+$200
stz hist_buff+$280
stz hist_buff+$300
stz hist_buff+$380
; fall through to ABORT
; ## ABORT ( -- ) "Reset the Data Stack and restart the CLI"
; ## "abort" tested ANS core
; """https://forth-standard.org/standard/core/ABORT
; Clear Data Stack and continue into QUIT. We can jump here via
; subroutine if we want to because we are going to reset the 65c02's
; stack pointer (the Return Stack) anyway during QUIT. Note we don't
; actually delete the stuff on the Data Stack.
; """
xt_abort:
ldx #dsp0
; fall through to QUIT
; ## QUIT ( -- ) "Reset the input and get new input"
; ## "quit" tested ANS core
; """https://forth-standard.org/standard/core/QUIT
; Rest the input and start command loop
; """
.scope
xt_quit:
; Clear the Return Stack. This is a little screwed up
; because the 65c02 can only set the Return Stack via X,
; which is our Data Stack pointer. The ANS specification
; demands, however, that ABORT reset the Data Stack pointer
txa ; Save the DSP that we just defined
ldx #rsp0
txs
tax ; Restore the DSP. Dude, seriously.
; make sure instruction pointer is empty
stz ip
stz ip+1
; SOURCE-ID is zero (keyboard input)
stz insrc
stz insrc+1
; STATE is zero (interpret, not compile)
stz state
stz state+1
_get_line:
lda #<buffer0 ; input buffer, this is paranoid
sta cib
lda #>buffer0
sta cib+1
; Size of current input buffer (CIB) is zero
stz ciblen
stz ciblen+1
; Accept a line from the current import source. This is how
; modern Forths do it.
jsr xt_refill ; ( -- f )
; Test flag: LSB of TOS
lda 0,x
bne _success
; If REFILL returned a FALSE flag, something went wrong and we
; need to print an error message and reset the machine. We
; don't need to save TOS because we're going to clobber it
; anyway when we go back to ABORT.
lda #err_refill
jmp error
_success:
; Assume we have successfully accepted a string of input from
; a source, with address cib and length of input in ciblen. We
; arrive here still with the TRUE flag from REFILL as TOS
inx ; drop
inx
; Main compile/execute routine
jsr interpret
; Test for Data Stack underflow. Tali Forth does not check for
; overflow because it is so rare
cpx #dsp0
beq _stack_ok
bcc _stack_ok ; DSP must always be smaller than DSP0
jmp underflow_error
_stack_ok:
; Display system prompt if all went well. If we're interpreting,
; this is " ok", if we're compiling, it's " compiled". Note
; space at beginning of the string.
lda state
beq _print
lda #1 ; number for "compile" string
_print:
jsr print_string
; Awesome line, everybody! Now get the next one.
bra _get_line
z_cold:
z_abort:
z_quit: ; no RTS required
.scend
; This table holds all of the initial values for the variables in zero page.
; This table is used by COLD.
cold_zp_table:
.word cp0+256+1024 ; cp moved to make room for user vars and
; block buffer
.word dictionary_start ; dp
.word 0 ; workword
.word 0 ; insrc (SOURCE-ID is 0 for keyboard)
.word buffer0 ; cib
.word 0 ; ciblen
.word 0 ; toin
.word 0 ; ip
.word kernel_putc ; output
.word kernel_getc ; input
.word 0 ; havekey
.word 0 ; state (0 = interpret)
.word 10 ; base
.word 20 ; nc-limit
.word 0 ; uf_strip (off by default)
.word cp0 ; up (user vars put right at beginning of
; available RAM)
.word 0 ; status
cold_zp_table_end:
; No further ZP variables are initialized. The variables past this point are
; all temporaries.
; This table holds the inital values for the user variables. This table is
; used by COLD.
cold_user_table:
.word 0 ; BLK
.word 0 ; SCR
.byte 0 ; CURRENT = FORTH-WORDLIST
.byte 4 ; #WORDLISTS (FORTH EDITOR ASSEMBLER ROOT)
.word dictionary_start ; FORTH-WORDLIST
.word editor_dictionary_start ; EDITOR-WORDLIST
.word assembler_dictionary_start ; ASSEMBLER-WORDLIST
.word root_dictionary_start ; ROOT-WORDLIST
.word 0,0,0,0,0,0,0,0 ; User wordlists
.byte 1 ; #ORDER
.byte 0,0,0,0,0,0,0,0,0 ; search-order
.word cp0+256 ; Address of buffer (right after USER vars)
.word 0 ; block in buffer
.word 0 ; buffer status (not in use)
.word xt_block_word_error ; block-read vector
.word xt_block_word_error ; block-write vector
cold_user_table_end:
; ## ABORT_QUOTE ( "string" -- ) "If flag TOS is true, ABORT with message"
; ## "abort"" tested ANS core
; """https://forth-standard.org/standard/core/ABORTq
; Abort and print a string.
; """
.scope
xt_abort_quote:
; save the string
jsr xt_s_quote ; S"
; compile run-time part
ldy #>abort_quote_runtime
lda #<abort_quote_runtime
jsr cmpl_subroutine ; may not be JMP as JSR/RTS
z_abort_quote: rts
.scend
abort_quote_runtime:
; """Runtime aspect of ABORT_QUOTE"""
.scope
; We arrive here with ( f addr u )
lda 4,x
ora 5,x
beq _done ; if FALSE, we're done
; We're true, so print string and ABORT. We follow Gforth
; in going to a new line after the string
jsr xt_type
jsr xt_cr
jmp xt_abort ; not JSR, so never come back
_done:
; Drop three entries from the Data Stack
txa
clc
adc #6
tax
rts
.scend
; ## ABS ( n -- u ) "Return absolute value of a number"
; ## "abs" auto ANS core
; """https://forth-standard.org/standard/core/ABS
; Return the absolute value of a number.
; """
.scope
xt_abs:
jsr underflow_1
lda 1,x
bpl _done ; positive number, easy money!
; negative: calculate 0 - n
sec
lda #0
sbc 0,x ; LSB
sta 0,x
lda #0 ; MSB
sbc 1,x
sta 1,x
_done:
z_abs: rts
.scend
; ## ACCEPT ( addr n -- n ) "Receive a string of characters from the keyboard"
; ## "accept" auto ANS core
; """https://forth-standard.org/standard/core/ACCEPT
; Receive a string of at most n1 characters, placing them at
; addr. Return the actual number of characters as n2. Characters
; are echoed as they are received. ACCEPT is called by REFILL in
; modern Forths.
; """
.scope
xt_accept:
jsr underflow_2
; Abort if we were asked to receive 0 chars
lda 0,x
ora 1,x
bne _not_zero
inx
inx
stz 0,x
stz 1,x
jmp _done
_not_zero:
lda 0,x ; number of chars to get in tmp2 ...
sta tmp2
stz tmp2+1 ; ... but we only accept max 255 chars
lda 2,x ; address of buffer is NOS, to tmp1
sta tmp1
lda 3,x
sta tmp1+1
inx
inx
ldy #0
; Select the next history buffer. Clear bit 3 first (so overflow
; from bit 2 to 3 is OK)
lda status
and #$f7
; Increment the buffer number (overflow from 7 to 0 OK)
inc
; Set bit 3 for detecting if CTRL-n has been pressed the first
; time. This bit will be cleared on the first CTRL-n or CTRL-p
; received and won't be used to calculate the history buffer
; offset.
ora #$08
sta status
_loop:
; Out of the box, py65mon catches some CTRL sequences such as
; CTRL-c. We also don't need to check for CTRL-l because a
; vt100 terminal clears the screen automatically.
; This is the internal version of KEY without all the mucking
; about with the Data Stack while still using the input vector
jsr key_a
; We quit on both line feed and carriage return
cmp #AscLF
beq _eol
cmp #AscCR
beq _eol
; BACKSPACE and DEL do the same thing for the moment
cmp #AscBS
beq _backspace
cmp #AscDEL ; (CTRL-h)
beq _backspace
; Check for CTRL-p and CTRL-n to recall input history
cmp #AscCP
beq _ctrl_p
cmp #AscCN
beq _ctrl_n
; That's enough for now. Save and echo character.
sta (tmp1),y
iny
; EMIT_A sidesteps all the fooling around with the Data Stack
jsr emit_a
cpy tmp2 ; reached character limit?
bne _loop ; fall through if buffer limit reached
bra _buffer_full
_eol:
jsr xt_space ; print final space
_buffer_full:
; REFILL updates ciblen and toin, we don't need to do it here
sty 0,x ; Y contains number of chars accepted already
stz 1,x ; we only accept 256 chars
jmp _done
_backspace:
; Handle backspace and delete kex, which currently do the same
; thing
cpy #0 ; buffer empty?
bne +
lda #AscBELL ; complain and don't delete beyond the start of line
jsr emit_a
iny
*
dey
lda #AscBS ; move back one
jsr emit_a
lda #AscSP ; print a space (rubout)
jsr emit_a
lda #AscBS ; move back over space
jsr emit_a
bra _loop
_ctrl_p:
; CTRL-p was pressed. Recall the previous input buffer.
; Select the previous buffer
lda status
; Check for 0 (need to wrap back to 7)
and #7
bne _ctrl_p_dec
; We need to wrap back to 7.
lda status
ora #7
sta status
bra _recall_history
_ctrl_p_dec:
; It's safe to decrement the buffer index directly.
dec status
bra _recall_history
_ctrl_n:
; CTRL-n was pressed. Recall the next input buffer. Select
; the next buffer Check bit 3. If it's set, this is the first
; time CTRL-n has been pressed and we should select the CURRENT
; history buffer.
lda #$8
bit status
bne _recall_history
; This isn't the first time CTRL-n has been pressed, select the
; next history buffer. Clear bit 3 first (so overflow is OK)
lda status
and #$f7
; Increment the buffer number (overflow from 7 to 0 OK)
inc
; Bit 3 (if it got set by going from buffer 7 to 0) will
; be cleared below.
sta status
; Falls through to _recall_history
_recall_history:
; Clear bit 3 (first time ctrl-n recall) bit in status
lda #%00001000
trb status
jsr _total_recall
; tmp3 now has the address of the previous history buffer.
; First byte of buffer is length. Clear the line by sending
; CR, Y spaces, then CR.
lda #AscCR
jsr emit_a
input_clear:
cpy #0
beq input_cleared
lda #AscSP
jsr emit_a
dey
bra input_clear
input_cleared:
lda #AscCR
jsr emit_a
; Save the history length byte into histinfo+1
; ldy #0 ; Y is already 0 by clearing the line.
lda (tmp3),y
sta status+1
; Increment the tmp3 pointer so we can use ,y addressing
; on both tmp1 (the input buffer) and tmp3 (the history
; buffer)
inc tmp3
bne + ; Increment the upper byte on carry.
inc tmp3+1
*
; Copy the history buffer into the input buffer,
; sending the characters to the output as we go.
lda #AscCR
jsr emit_a
_history_loop:
; See if we have reached the end of the history buffer.
cpy status+1
bne +
jmp _loop ; Needs a long jump
*
; See if we have reached the end of the input buffer.
; (only comparing to lower byte as we currently limit
; to 255 characters max)
cpy tmp2
beq _hist_filled_buffer
; Copy a character and echo.
lda (tmp3),y
sta (tmp1),y
jsr emit_a
; Move to the next character.
iny
bra _history_loop
_hist_filled_buffer:
; We don't want a history recall to EOL our buffer,
; so back up one character and return to editing.
dey
jmp _loop
_done:
; Copy the input buffer into the currently
; selected history buffer.
jsr _total_recall
sta status+1
; Also save it in the first buffer byte.
ldy #0
sta (tmp3),y
; Move path the count to the data bytes
inc tmp3
bne + ; Increment the upper byte on carry.
inc tmp3+1
*
; Copy the characters from the input buffer to the
; history buffer.
_save_history_loop:
cpy status+1
beq _save_history_done
lda (tmp1),y
sta (tmp3),y
iny
bra _save_history_loop
_save_history_done:
z_accept:
rts
_total_recall:
; """Internal subroutine for ACCEPT that recalls history entry"""
; Generate the address of the buffer in tmp3. Start with the
; base address.
lda #<hist_buff
sta tmp3
lda #>hist_buff
sta tmp3+1
; This is a bit annoying as some bits go into each byte.
; .....xxx gets put into address like ......xx x.......
lda status
ror
and #3
clc
adc tmp3+1
sta tmp3+1
lda status
ror ; Rotate through carry into msb.
ror
and #$80
clc
adc tmp3
sta tmp3
bcc + ; Increment the upper byte on carry.
inc tmp3+1
*
; Save the current length of the input buffer in
; histinfo+1 temporarily. Reduce to 127 if larger.
tya
cmp #$80
bcc +
lda #$7F
*
rts
.scend
; ## ACTION_OF ( "name" -- xt ) "Get named deferred word's xt"
; ## "action-of" auto ANS core ext
; """http://forth-standard.org/standard/core/ACTION-OF"""
.scope
xt_action_of:
; This is a state aware word with differet behavior
; when used while compiling vs interpreting.
; Check STATE
lda state
ora state+1
beq _interpreting
_compiling:
; Run ['] to compile the xt of the next word
; as a literal.
jsr xt_bracket_tick
; Postpone DEFER@ by compiling a JSR to it.
ldy #>xt_defer_fetch
lda #<xt_defer_fetch
jsr cmpl_subroutine
bra _done
_interpreting:
jsr xt_tick
jsr xt_defer_fetch
_done:
z_action_of: rts
.scend
; ## AGAIN ( addr -- ) "Code backwards branch to address left by BEGIN"
; ## "again" tested ANS core ext
; """https://forth-standard.org/standard/core/AGAIN"""
.scope
xt_again:
jsr underflow_1
; Add the opcode for a JMP. We use JMP instead of BRA
; so we have the range and don't have to calculate the
; offset.
ldy #0
lda #$4C ; JMP
sta (cp),y
iny
lda 0,x ; LSB of address
sta (cp),y
iny
lda 1,x ; MSB of address
sta (cp),y
iny
; Allot the space we just used
tya
clc
adc cp
sta cp
bcc _done
inc cp+1
_done:
inx
inx
z_again: rts
.scend
; ## ALIGN ( -- ) "Make sure CP is aligned on word size"
; ## "align" auto ANS core
; """https://forth-standard.org/standard/core/ALIGN
; On a 8-bit machine, this does nothing. ALIGNED uses this
; routine as well, and also does nothing
; """
; ## ALIGNED ( addr -- addr ) "Return the first aligned address"
; ## "aligned" auto ANS core
; """https://forth-standard.org/standard/core/ALIGNED"""
.scope
xt_align:
xt_aligned:
z_align:
z_aligned: rts ; stripped out during native compile
.scend
; ## ALLOT ( n -- ) "Reserve or release memory"
; ## "allot" auto ANS core
; """https://forth-standard.org/standard/core/ALLOT
; Reserve a certain number of bytes (not cells) or release them.
; If n = 0, do nothing. If n is negative, release n bytes, but only
; to the beginning of the Dictionary. If n is positive (the most
; common case), reserve n bytes, but not past the end of the
; Dictionary. See http://forth-standard.org/standard/core/ALLOT
; """
.scope
xt_allot:
jsr underflow_1
; Releasing memory is going to be a very rare operation,
; so we check for it at the beginning and try to make
; the most common case as fast as possible
lda 1,x
bmi _release
; Common case: We are reserving memory, not releasing it
clc
lda cp
adc 0,x
sta cp
lda cp+1
adc 1,x
sta cp+1
; Wait, did we just grant more space than we have? This is
; a check we only do here, not for other situations like cmpl_a
; where smaller amounts are reserved.
ldy #<cp_end
cpy cp
lda #>cp_end
sbc cp+1
bcs _done ; we're fine.
; Oops, that was too much, we're beyond the end of
; legal Dictionary RAM. Reduce to max memory and report
; an error
sty cp ; still #<cp_end
lda #>cp_end
sta cp+1
lda #err_allot
jmp error
_release:
; The ANS standard doesn't really say what to do if too much
; memory is freed ("negatively alloted"). In fact, there isn't
; even an official test. Gforth is little help either. The good
; news is, this is going to be a rare case. We want to use as
; few bytes as possible.
; What we do is let the user free anything up to the beginning
; of the RAM area assigned to the Dicionary (CP0), but at
; their own risk. This means that the Dictionary pointer DP
; might end up pointing to garbage. However, an attempt to
; free more than RAM than CP0 will lead to CP being set to CP0,
; the DP pointing to the last word in RAM (should be DROP) and
; an error message.
; We arrive here with ( n ) which is negative. First step,
; subtract the number TOS from the CP for a new CP
dex
dex
lda cp
sta 0,x
lda cp+1
sta 1,x
jsr xt_plus ; new CP is now TOS
; Second step, see if we've gone too far. We compare the new
; CP on TOS (which, if we've really screwed up, might be
; negative) with CP0. This is a signed comparison
dex
dex ; new CP now NOS
lda #<cp0
sta 0,x
lda #>cp0
sta 1,x ; CP0 is TOS
jsr compare_16bit ; still ( CP CP0 )
; If CP (NOS) is smaller than CP0 (TOS), we're in trouble.
; This means we want Z=1 or N=1
beq _nega_done
bmi _nega_done
; Yep, we're in trouble. Set CP to CP0, set DP to the first
; word in ROM (should be DROP), and abort with an error
lda #<cp0
sta cp
lda #>cp0
sta cp+1
lda #<dictionary_start
sta dp
lda #>dictionary_start
sta dp+1
lda #err_negallot
jmp error
_nega_done:
; Save new CP, which is NOS
lda 2,x
sta cp
lda 3,x
sta cp+1
inx
inx ; drop through to _done
_done:
inx
inx
z_allot:
rts
.scend
; ## ALLOW_NATIVE ( -- ) "Flag last word to allow native compiling"
; ## "allow-native" auto Tali Forth
xt_allow_native:
jsr current_to_dp
ldy #1 ; offset for status byte
lda (dp),y
and #$ff-NN-AN ; AN and NN flag is clear.
sta (dp),y
z_allow_native:
rts
; ## ALSO ( -- ) "Make room in the search order for another wordlist"
; ## "also" auto ANS search ext
; """http://forth-standard.org/standard/search/ALSO"""
xt_also:
jsr xt_get_order
jsr xt_over
jsr xt_swap
jsr xt_one_plus
jsr xt_set_order
z_also: rts
; ## ALWAYS_NATIVE ( -- ) "Flag last word as always natively compiled"
; ## "always-native" auto Tali Forth
xt_always_native:
jsr current_to_dp
ldy #1 ; offset for status byte
lda (dp),y
ora #AN ; Make sure AN flag is set
and #$ff-NN ; and NN flag is clear.
sta (dp),y
z_always_native:
rts
; ## AND ( n n -- n ) "Logically AND TOS and NOS"
; ## "and" auto ANS core
; """https://forth-standard.org/standard/core/AND"""
xt_and:
jsr underflow_2
lda 0,x
and 2,x
sta 2,x
lda 1,x
and 3,x
sta 3,x
inx
inx
z_and: rts
; ## ASSEMBLER_WORDLIST ( -- u ) "WID for the Assembler wordlist"
; ## "assembler-wordlist" tested Tali Assembler
; """ Commonly used like `assembler-wordlist >order` to add the
; assembler words to the search order so they can be used.
; See the tutorial on Wordlists and the Search Order for
; more information.
;
; This is a dummy entry, the code is shared with TWO
; """
; ## AT_XY ( n m -- ) "Move cursor to position given"
; ## "at-xy" tested ANS facility
; """https://forth-standard.org/standard/facility/AT-XY
; On an ANSI compatible terminal, place cursor at row n colum m.
; ANSI code is ESC[<n>;<m>H
;
; Do not use U. to print the numbers because the
; trailing space will not work with xterm
; """
xt_at_xy:
jsr underflow_2
lda #AscESC
jsr emit_a
lda #$5B ; ASCII for "["
jsr emit_a
jsr print_u
lda #$3B ; ASCII for ";"
jsr emit_a
jsr print_u
lda #'H
jsr emit_a
z_at_xy: rts
; ## BACKSLASH ( -- ) "Ignore rest of line"
; ## "\" auto ANS core ext
; """https://forth-standard.org/standard/core/bs"""
xt_backslash:
lda ciblen
sta toin
lda ciblen+1
sta toin+1
z_backslash: rts
; ## BASE ( -- addr ) "Push address of radix base to stack"
; ## "base" auto ANS core
; """https://forth-standard.org/standard/core/BASE
; The ANS Forth standard sees the base up to 36, so we can cheat and
; ingore the MSB
; """
xt_base:
dex
dex
lda #<base
sta 0,x ; LSB