Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / forth / bootstrap / bootstrap.fs
1 \ tag: bootstrap of basic forth words
2
3 \ Copyright (C) 2003-2005 Stefan Reinauer, Patrick Mauritz
4
5 \ See the file "COPYING" for further information about
6 \ the copyright and warranty status of this work.
7
8
9
10 \ this file contains almost all forth words described
11 \ by the open firmware user interface. Some more complex
12 \ parts are found in seperate files (memory management,
13 \ vocabulary support)
14
15
16
17 \ often used constants (reduces dictionary size)
18
19
20 1 constant 1
21 2 constant 2
22 3 constant 3
23 -1 constant -1
24 0 constant 0
25
26 0 value my-self
27
28
29 \ 7.3.5.1 Numeric-base control
30
31
32 : decimal 10 base ! ;
33 : hex 16 base ! ;
34 : octal 8 base ! ;
35 hex
36
37
38 \ vocabulary words
39
40
41 variable current forth-last current !
42
43 : last 
44   current @ 
45   ;
46
47 variable #order 0 #order !
48
49 defer context
50 0 value vocabularies?
51
52 defer locals-end
53 0 value locals-dict
54 variable locals-dict-buf
55
56
57 \ 7.3.7 Flag constants
58
59
60 1 1 = constant true
61 0 1 = constant false
62
63
64 \ 7.3.9.2.2 Immediate words (part 1)
65
66
67 : (immediate) ( xt -- )
68   1 - dup c@ 1 or swap c!
69   ;
70
71 : (compile-only)
72   1 - dup c@ 2 or swap c!
73   ;
74
75 : immediate 
76   last @ (immediate) 
77   ;
78   
79 : compile-only 
80   last @ (compile-only) 
81   ;
82
83 : flags? ( xt -- flags )
84   /n /c + - c@ 7f and
85   ;
86
87 : immediate? ( xt -- true|false )
88   flags? 1 and 1 =
89   ;
90
91 : compile-only? ( xt -- true|false )
92   flags? 2 and 2 =
93   ;
94
95 : [  0 state ! ; compile-only
96 : ] -1 state ! ; 
97
98
99
100
101 \ 7.3.9.2.1 Data space allocation
102
103
104 : allot here + here! ;
105 : ,  here /n allot ! ;
106 : c, here /c allot c! ;
107
108 : align
109   /n here /n 1 - and -   \ how many bytes to next alignment
110   /n 1 - and allot       \ mask out everything that is bigger 
111   ;                      \ than cellsize-1
112
113 : null-align
114   here dup align here swap - 0 fill 
115   ;
116
117 : w, 
118   here 1 and allot       \ if here is not even, we have to align.
119   here /w allot w! 
120   ;
121
122 : l, 
123   /l here /l 1 - and -   \ same as in align, with /l
124   /l 1 - and             \ if it's /l we are already aligned.
125   allot
126   here /l allot l! 
127   ;
128
129
130
131 \ 7.3.6 comparison operators (part 1)
132
133
134 : <> = invert ;
135
136
137
138 \ 7.3.9.2.4 Miscellaneous dictionary (part 1)
139
140
141 : (to) ( xt-new xt-defer -- )
142   /n + !
143   ;
144
145 : >body ( xt -- a-addr )  /n 1 lshift + ;
146 : body> ( a-addr -- xt )  /n 1 lshift - ;
147
148 : reveal latest @ last ! ;
149 : recursive reveal ; immediate
150 : recurse latest @ /n +  , ; immediate
151
152 : noop ;
153
154 defer environment?
155 : no-environment?
156   2drop false 
157   ;
158
159 ['] no-environment? ['] environment? (to)
160
161
162
163 \ 7.3.8.1 Conditional branches
164
165
166 \ A control stack entry is implemented using 2 data stack items
167 \ of the form ( addr type ). type can be one of the
168 \ following:
169 \   0 - orig
170 \   1 - dest
171 \   2 - do-sys
172
173 : resolve-orig here nip over /n + - swap ! ;
174 : (if) ['] do?branch , here 0 0 , ; compile-only
175 : (then) resolve-orig ; compile-only
176
177 variable tmp-comp-depth -1 tmp-comp-depth !
178 variable tmp-comp-buf 0 tmp-comp-buf !
179
180 : setup-tmp-comp ( -- )
181   state @ 0 = (if)
182     here tmp-comp-buf @ here! ,     \ save here and switch to tmp directory
183     1 ,                              \ DOCOL
184     depth tmp-comp-depth !          \ save control depth
185     ]
186   (then)
187 ;
188
189 : execute-tmp-comp ( -- )
190   depth tmp-comp-depth @ =
191   (if)
192     -1 tmp-comp-depth !
193     ['] (semis) ,
194     tmp-comp-buf @
195     dup @ here!
196     0 state !
197     /n + execute
198   (then)
199 ;
200
201 : if setup-tmp-comp ['] do?branch , here 0 0 , ; immediate
202 : then resolve-orig execute-tmp-comp ; compile-only
203 : else ['] dobranch , here 0 0 , 2swap resolve-orig ; compile-only
204
205
206 \ 7.3.8.3 Conditional loops
207
208
209 \ some dummy words for see
210 : (begin) ;
211 : (again) ;
212 : (until) ;
213 : (while) ;
214 : (repeat) ;
215
216 \ resolve-dest requires a loop...
217 : (resolve-dest) here /n + nip - , ;
218 : (resolve-begin) setup-tmp-comp ['] (begin) , here 1 ; immediate
219 : (resolve-until) ['] (until) , ['] do?branch , (resolve-dest) execute-tmp-comp ; compile-only
220
221 : resolve-dest ( dest origN ... orig )
222   2 >r
223   (resolve-begin)
224     \ Find topmost control stack entry with a type of 1 (dest)
225     r> dup dup pick 1 = if
226       \ Move it to the top
227       roll
228       swap 1 - roll
229       \ Resolve it
230       (resolve-dest)
231       1         \ force exit
232     else
233       drop
234       2 + >r
235       0
236     then
237   (resolve-until)
238 ;
239
240 : begin
241   setup-tmp-comp
242   ['] (begin) , 
243   here
244   1
245   ; immediate
246
247 : again
248   ['] (again) ,
249   ['] dobranch , 
250   resolve-dest
251   execute-tmp-comp
252   ; compile-only
253
254 : until
255   ['] (until) ,
256   ['] do?branch , 
257   resolve-dest
258   execute-tmp-comp
259   ; compile-only
260
261 : while
262   setup-tmp-comp
263   ['] (while) ,
264   ['] do?branch , 
265   here 0 0 , 2swap  
266   ; immediate
267
268 : repeat
269   ['] (repeat) ,
270   ['] dobranch , 
271   resolve-dest resolve-orig
272   execute-tmp-comp
273   ; compile-only
274
275
276
277 \ 7.3.8.4 Counted loops
278
279
280 variable leaves 0 leaves !
281
282 : resolve-loop
283   leaves @
284   begin
285     ?dup 
286   while 
287     dup @               \ leaves -- leaves *leaves )
288     swap                \ -- *leaves leaves )
289     here over -         \ -- *leaves leaves here-leaves
290     swap !              \ -- *leaves
291   repeat
292   here nip - , 
293   leaves !
294   ;
295
296 : do
297   setup-tmp-comp
298   leaves @
299   here 2
300   ['] (do) , 
301   0 leaves !
302   ; immediate
303
304 : ?do
305   setup-tmp-comp
306   leaves @ 
307   ['] (?do) ,
308   here 2
309   here leaves !
310   0 ,
311   ; immediate
312
313 : loop
314   ['] (loop) ,
315   resolve-loop
316   execute-tmp-comp
317   ; immediate 
318
319 : +loop
320   ['] (+loop) ,
321   resolve-loop
322   execute-tmp-comp
323   ; immediate
324
325
326 \ Using primitive versions of i and j
327 \ speeds up loops by 300%
328 \ : i r> r@ swap >r ;
329 \ : j r> r> r> r@ -rot >r >r swap >r ;
330
331 : unloop r> r> r> 2drop >r ;
332
333 : leave 
334   ['] unloop , 
335   ['] dobranch , 
336   leaves @ 
337   here leaves !  
338   , 
339   ; immediate
340
341 : ?leave if leave then ;
342
343
344 \ 7.3.8.2  Case statement
345
346  
347 : case
348   setup-tmp-comp
349   0
350 ; immediate
351
352 : endcase
353   ['] drop , 
354   0 ?do
355     ['] then execute
356   loop
357   execute-tmp-comp
358 ; immediate
359
360 : of
361   1 + >r 
362   ['] over , 
363   ['] = , 
364   ['] if execute 
365   ['] drop , 
366   r> 
367   ; immediate
368
369 : endof
370   >r 
371   ['] else execute 
372   r> 
373   ; immediate
374
375
376 \ 7.3.8.5    Other control flow commands
377
378
379 : exit r> drop ;
380
381
382
383 \ 7.3.4.3 ASCII constants (part 1)
384
385
386 20 constant bl
387 07 constant bell
388 08 constant bs
389 0d constant carret
390 0a constant linefeed
391
392
393
394 \ 7.3.1.1 - stack duplication
395
396 : tuck swap over ;
397 : 3dup 2 pick 2 pick 2 pick ;
398
399
400 \ 7.3.1.2 - stack removal
401
402 : clear 0 depth! ;
403 : 3drop 2drop drop ;
404
405
406 \ 7.3.1.3 - stack rearrangement
407
408
409 : 2rot >r >r 2swap r> r> 2swap ;
410
411 \
412 \ 7.3.1.4 - return stack
413 \
414
415 \ Note: these words are not part of the official OF specification, however
416 \ they are part of the ANSI DPANS94 core extensions (see section 6.2) and
417 \ so this seems an appropriate place for them.
418 : 2>r r> -rot swap >r >r >r ;
419 : 2r> r> r> r> rot >r swap ;
420 : 2r@ r> r> r> 2dup >r >r rot >r swap ;
421
422
423 \ 7.3.2.1 - single precision integer arithmetic (part 1)
424
425
426 : u/mod 0 swap mu/mod drop ;
427 : 1+ 1 + ;
428 : 1- 1 - ;
429 : 2+ 2 + ;
430 : 2- 2 - ;
431 : even 1+ -2 and ;
432 : bounds over + swap ;
433
434
435 \ 7.3.2.2 bitwise logical operators
436
437 : << lshift ;
438 : >> rshift ;
439 : 2* 1 lshift ;
440 : u2/ 1 rshift ;
441 : 2/ 1 >>a ;
442 : not invert ;
443
444
445 \ 7.3.2.3 double number arithmetic
446
447
448 : s>d      dup 0 < ; 
449 : dnegate  0 0 2swap d- ;
450 : dabs     dup 0 < if dnegate then ;
451 : um/mod   mu/mod drop ;
452
453 \ symmetric division
454 : sm/rem  ( d n -- rem quot )
455   over >r >r dabs r@ abs um/mod r> 0 < 
456   if 
457     negate 
458   then 
459   r> 0 < if 
460     negate swap negate swap
461   then
462   ;
463
464 \ floored division
465 : fm/mod ( d n -- rem quot ) 
466   dup >r 2dup xor 0 < >r sm/rem over 0 <> r> and if 
467     1 - swap r> + swap exit 
468   then
469   r> drop
470   ;
471
472
473 \ 7.3.2.1 - single precision integer arithmetic (part 2)
474
475
476 : */mod ( n1 n2 n3 -- quot rem ) >r m* r> fm/mod  ;
477 : */ ( n1 n2 n3 -- n1*n2/n3 ) */mod nip ;
478 : /mod >r s>d r> fm/mod ;
479 : mod /mod drop ;
480 : / /mod nip ;
481
482
483
484 \ 7.3.2.4 Data type conversion
485
486
487 : lwsplit ( quad -- w.lo w.hi )
488   dup ffff and swap 10 rshift ffff and
489 ;
490
491 : wbsplit ( word -- b.lo b.hi )
492   dup ff and swap 8 rshift ff and
493 ;
494
495 : lbsplit ( quad -- b.lo b2 b3 b.hi )
496   lwsplit swap wbsplit rot wbsplit
497 ;
498
499 : bwjoin ( b.lo b.hi -- word )
500   ff and 8 lshift swap ff and or
501 ;
502
503 : wljoin ( w.lo w.hi -- quad )
504   ffff and 10 lshift swap ffff and or
505 ;
506
507 : bljoin ( b.lo b2 b3 b.hi -- quad )
508   bwjoin -rot bwjoin swap wljoin
509 ;
510
511 : wbflip ( word -- word ) \ flips bytes in a word
512   dup 8 rshift ff and swap ff and bwjoin
513 ;
514
515 : lwflip ( q1 -- q2 ) 
516   dup 10 rshift ffff and swap ffff and wljoin
517 ;
518
519 : lbflip ( q1 -- q2 )
520   dup 10 rshift ffff and wbflip swap ffff and wbflip wljoin
521 ;
522
523
524 \ 7.3.2.5 address arithmetic
525
526
527 : /c* /c * ;
528 : /w* /w * ;
529 : /l* /l * ;
530 : /n* /n * ;
531 : ca+ /c* + ;
532 : wa+ /w* + ;
533 : la+ /l* + ;
534 : na+ /n* + ;
535 : ca1+ /c + ;
536 : wa1+ /w + ;
537 : la1+ /l + ;
538 : na1+ /n + ;
539 : aligned /n 1- + /n negate and ;
540 : char+ ca1+ ;
541 : cell+ na1+ ;
542 : chars /c* ;
543 : cells /n* ;
544 /n constant cell
545
546
547 \ 7.3.6 Comparison operators
548
549
550 : <= > not ;
551 : >= < not ;
552 : 0= 0 = ;
553 : 0<= 0 <= ;
554 : 0< 0 < ;
555 : 0<> 0 <> ;
556 : 0> 0 > ;
557 : 0>=  0 >= ;
558 : u<= u> not ;
559 : u>= u< not ;
560 : within  >r over > swap r> >= or not ;
561 : between 1 + within ;
562
563
564 \ 7.3.3.1 Memory access
565
566
567 : 2@ dup cell+ @ swap @  ;
568 : 2! dup >r ! r> cell+ ! ;
569
570 : <w@ w@ dup 8000 >= if 10000 - then ;
571
572 : comp ( str1 str2 len -- 0|1|-1 )
573   >r 0 -rot r>
574   bounds ?do
575     dup c@ i c@ - dup if
576       < if 1 else -1 then swap leave
577     then 
578     drop ca1+
579   loop
580   drop
581 ;
582
583 \ compare two string
584
585 : $= ( str1 len1 str2 len2 -- true|false )
586     rot ( str1 str2 len2 len1 )
587     over ( str1 str2 len2 len1 len2 )  
588     <> if ( str1 str2 len2 )
589         3drop
590         false
591     else ( str1 str2 len2 )
592         comp
593         0=
594     then
595 ;
596
597 \ : +! tuck @ + swap ! ;
598 : off false swap ! ;
599 : on true swap ! ;
600 : blank bl fill ;
601 : erase 0 fill ;
602 : wbflips ( waddr len -- )
603   bounds do i w@ wbflip i w! /w +loop
604 ;
605
606 : lwflips ( qaddr len -- )
607   bounds do i l@ lwflip i l! /l +loop
608 ;
609
610 : lbflips ( qaddr len -- )
611   bounds do i l@ lbflip i l! /l +loop
612 ;
613
614
615
616 \ 7.3.8.6    Error handling (part 1)
617
618
619 variable catchframe
620 0 catchframe !
621
622 : catch
623   my-self >r
624   depth >r
625   catchframe @ >r
626   rdepth catchframe !
627   execute
628   r> catchframe !
629   r> r> 2drop 0
630   ;
631
632 : throw
633   ?dup if
634     catchframe @ rdepth!
635     r> catchframe !
636     r> swap >r depth!
637     drop r>
638     r> ['] my-self (to)
639   then
640   ;
641
642
643 \ 7.3.3.2 memory allocation
644
645
646 include memory.fs
647
648
649
650 \ 7.3.4.4 Console output (part 1)
651
652
653 defer emit
654
655 : type bounds ?do i c@ emit loop ;
656
657 \ this one obviously only works when called 
658 \ with a forth string as count fetches addr-1.
659 \ openfirmware has no such req. therefore it has to go:
660
661 \ : type 0 do count emit loop drop ;
662
663 : debug-type bounds ?do i c@ (emit) loop ;
664
665
666 \ 7.3.4.1 Text Input
667
668
669 0 value source-id 
670 0 value ib
671 variable #ib 0 #ib !
672 variable >in 0 >in !
673
674 : source ( -- addr len )
675   ib #ib @
676   ;
677
678 : /string  ( c-addr1 u1 n -- c-addr2 u2 )
679    tuck - -rot + swap 
680
681
682
683
684 \ pockets implementation for 7.3.4.1
685
686 100 constant pocketsize
687 4   constant numpockets
688 variable pockets 0 pockets !
689 variable whichpocket 0 whichpocket !
690
691 \ allocate 4 pockets to begin with
692 : init-pockets     ( -- )
693   pocketsize numpockets * alloc-mem pockets !
694   ;
695
696 : pocket ( ?? -- ?? )
697   pocketsize whichpocket @ *
698   pockets @ +
699   whichpocket @ 1 + numpockets mod
700   whichpocket !
701   ;
702
703 \ span variable from 7.3.4.2
704 variable span 0 span !
705
706 \ if char is bl then any control character is matched
707 : findchar ( str len char -- offs true | false )
708   swap 0 do
709     over i + c@
710     over dup bl = if <= else = then if
711       2drop i dup dup leave
712       \ i nip nip true exit \ replaces above
713     then
714   loop
715   =
716   \ drop drop false
717   ;
718
719 : parse ( delim  text<delim>  -- str len )
720   >r              \ save delimiter
721   ib >in @ +
722   span @ >in @ -  \ ib+offs len-offset.
723   dup 0 < if      \ if we are already at the end of the string, return an empty string
724     + 0           \ move to end of input string
725     r> drop
726     exit
727   then
728   2dup r>         \ ib+offs len-offset ib+offs len-offset delim
729   findchar if     \ look for the delimiter. 
730     nip dup 1+
731   else
732      dup
733   then
734   >in +!
735   \ dup -1 = if drop 0 then \ workaround for negative length
736   ;
737
738 : skipws ( -- )
739   ib span @        ( -- ib recvchars )
740   begin
741     dup >in @ > if    ( -- recvchars>offs )
742       over >in @ +
743       c@ bl <=
744     else
745       false
746     then
747   while
748       1 >in +!
749   repeat
750   2drop
751   ;
752
753 : parse-word (  < >text< >  -- str len )
754   skipws bl parse
755   ;
756
757 : word ( delim  <delims>text<delim>  -- pstr )
758   pocket >r parse dup r@ c! bounds r> dup 2swap
759   do
760     char+ i c@ over c!
761   loop
762   drop
763   ;
764
765 : ( 29 parse 2drop ; immediate
766 : \ span @ >in !   ; immediate
767
768
769
770
771 \ 7.3.4.7 String literals
772
773
774 : ",
775   bounds ?do
776     i c@ c,
777   loop
778   ;
779
780 : (")  ( -- addr len )
781   r> dup 
782   2 cells +                   ( r-addr addr )
783   over cell+ @                ( r-addr addr len )
784   rot over + aligned cell+ >r ( addr len R: r-addr )
785   ;
786  
787 : handle-text ( temp-addr len -- addr len )
788   state @ if
789     ['] (") , dup , ", null-align
790   else
791     pocket swap
792     dup >r
793     0 ?do
794       over i + c@ over i + c!
795     loop
796     nip r>
797   then
798   ;
799
800 : s"
801   22 parse handle-text
802   ; immediate
803
804
805
806
807 \ 7.3.4.4 Console output (part 2)
808
809
810 : ."
811   22 parse handle-text
812   ['] type
813   state @ if
814     ,
815   else
816     execute
817   then
818   ; immediate
819
820 : .(
821   29 parse handle-text
822   ['] type
823   state @ if
824     ,
825   else
826     execute
827   then
828   ; immediate
829
830
831
832
833 \ 7.3.4.8 String manipulation
834
835
836 : count ( pstr -- str len ) 1+ dup 1- c@ ;
837
838 : pack  ( str len addr -- pstr )
839   2dup c!     \ store len
840   1+ swap 0 ?do
841     over i + c@ over i + c!
842   loop nip 1-
843   ;
844
845 : lcc   ( char1 -- char2 ) dup 41 5a between if 20 + then ;
846 : upc   ( char1 -- char2 ) dup 61 7a between if 20 - then ;
847
848 : -trailing ( str len1 -- str len2 )
849   begin 
850     dup 0<> if  \ len != 0 ?
851       2dup 1- + 
852       c@ bl =
853     else 
854       false 
855     then
856   while
857     1-
858   repeat
859   ;
860
861
862
863 \ 7.3.4.5   Output formatting
864
865
866 : cr linefeed emit ;
867 : debug-cr linefeed (emit) ;
868 : (cr carret emit ;
869 : space bl emit ;
870 : spaces 0 ?do space loop ;
871 variable #line 0 #line !
872 variable #out  0 #out  !
873
874
875
876 \ 7.3.9.2.3 Dictionary search
877
878
879 \ helper functions
880
881 : lfa2name ( lfa -- name len )
882   1-                   \ skip flag byte
883   begin                \ skip 0 padding 
884     1- dup c@ ?dup 
885   until
886   7f and               \ clear high bit in length
887
888   tuck - swap          ( ptr-to-len len - name len )
889   ;
890
891 : comp-nocase ( str1 str2 len -- true|false )
892   0 do
893     2dup i + c@ upc    ( str1 str2 byteX )
894     swap i + c@ upc ( str1 str2 byte1 byte2 )
895     <> if
896       0 leave
897     then
898   loop
899   if -1 else drop 0 then
900   swap drop
901   ;
902
903 : comp-word ( b-str len lfa -- true | false )
904   lfa2name        ( str len str len -- )
905   >r swap r>      ( str str len len )
906   over = if       ( str str len )
907     comp-nocase
908   else
909     drop drop drop false   \ if len does not match, string does not match
910   then
911 ;
912
913 \ $find is an fcode word, but we place it here since we use it for find.
914
915 : find-wordlist ( name-str name-len last -- xt true | name-str name-len false )
916
917   @ >r
918
919   begin
920     2dup r@ dup if comp-word dup false = then
921   while
922     r> @ >r drop
923   repeat
924
925   r@ if \ successful?
926     -rot 2drop r> cell+ swap
927   else
928     r> drop drop drop false
929   then
930
931   ;
932
933 : $find ( name-str name-len -- xt true | name-str name-len false )
934   locals-dict 0<> if
935     locals-dict-buf @ find-wordlist ?dup if
936       exit
937     then
938   then
939   vocabularies? if
940     #order @ 0 ?do
941       i cells context + @
942       find-wordlist
943       ?dup if
944         unloop exit
945       then
946     loop
947     false
948   else
949     forth-last find-wordlist
950   then
951   ;
952
953 \ look up a word in the current wordlist
954 : $find1 ( name-str name-len -- xt true | name-str name-len false )
955   vocabularies? if
956     current @
957   else
958     forth-last
959   then
960   find-wordlist
961   ;
962
963   
964 : '
965   parse-word $find 0= if 
966     type 3a emit -13 throw
967   then
968   ;
969
970 : ['] 
971   parse-word $find 0= if
972     type 3a emit -13 throw
973   then 
974   state @ if
975     ['] (lit) , , 
976   then
977   ; immediate
978
979 : find ( pstr -- xt n | pstr false )
980   dup count $find           \  pstr xt true | pstr name-str name-len false
981   if
982     nip true
983     over immediate? if
984       negate                \ immediate returns 1
985     then
986   else
987     2drop false
988   then
989   ;
990
991
992
993 \ 7.3.9.2.2 Immediate words (part 2)
994
995
996 : literal ['] (lit) , , ; immediate
997 : compile, , ; immediate
998 : compile r> cell+ dup @ , >r ;
999 : [compile] ['] ' execute , ; immediate
1000
1001 : postpone
1002   parse-word $find if
1003     dup immediate? not if
1004       ['] (lit) , , ['] ,
1005     then
1006     ,
1007   else
1008     s" undefined word " type type cr
1009   then
1010   ; immediate
1011
1012
1013
1014 \ 7.3.9.2.4 Miscellaneous dictionary (part 2)
1015
1016
1017 variable #instance
1018
1019 : instance ( -- )
1020   true #instance !
1021 ;
1022
1023 : #instance-base
1024   my-self dup if @ then
1025 ;
1026
1027 : #instance-offs
1028   my-self dup if na1+ then
1029 ;
1030
1031 \ the following instance words are used internally
1032 \ to implement variable instantiation.
1033
1034 : instance-cfa? ( cfa -- true | false )
1035   b e within                              \ b,c and d are instance defining words
1036 ;
1037
1038 : behavior ( xt-defer -- xt )
1039   dup @ instance-cfa? if
1040     #instance-base ?dup if
1041       swap na1+ @ + @
1042     else
1043       3 /n* + @
1044     then
1045   else
1046     na1+ @
1047   then
1048 ;
1049
1050 : (ito) ( xt-new xt-defer -- )
1051   #instance-base ?dup if
1052     swap na1+ @ + !
1053   else
1054     3 /n* + !
1055   then
1056 ;
1057   
1058 : (to-xt) ( xt -- )  
1059   dup @ instance-cfa?
1060   state @ if
1061     swap ['] (lit) , , if ['] (ito) else ['] (to) then ,
1062   else
1063     if (ito) else /n + ! then
1064   then
1065 ;
1066
1067 : to
1068   ['] ' execute
1069   (to-xt)
1070   ; immediate
1071   
1072 : is ( xt "wordname<>" -- )
1073   parse-word $find if
1074     (to)
1075   else
1076     s" could not find " type type
1077   then
1078   ;
1079
1080
1081 \ 7.3.4.2 Console Input
1082
1083
1084 defer key?
1085 defer key
1086
1087 : accept ( addr len -- len2 )
1088   tuck 0 do
1089     key
1090     dup linefeed = if
1091       space drop drop drop i 0 leave
1092     then
1093     dup emit over c! 1 +
1094   loop
1095   drop ( cr )
1096   ;
1097
1098 : expect ( addr len -- )
1099   accept span !
1100   ;
1101
1102
1103
1104 \ 7.3.4.3 ASCII constants (part 2)
1105
1106
1107 : handle-lit
1108   state @ if
1109     2 = if
1110       ['] (lit) ,  ,
1111     then
1112     ['] (lit) ,  ,
1113   else
1114     drop
1115   then
1116   ;
1117
1118 : char
1119   parse-word 0<> if c@ else s" Unexpected EOL." type cr then ;
1120   ;
1121
1122 : ascii  char 1 handle-lit ; immediate
1123 : [char] char 1 handle-lit ; immediate
1124
1125 : control   
1126   char bl 1- and 1 handle-lit 
1127 ; immediate
1128
1129
1130
1131
1132 \ 7.3.8.6    Error handling (part 2)
1133
1134
1135 : abort 
1136   -1 throw
1137   ;
1138
1139 : abort"
1140   ['] if execute
1141   22 parse handle-text 
1142   ['] type , 
1143   ['] (lit) , 
1144   -2 , 
1145   ['] throw ,
1146   ['] then execute
1147   ; compile-only 
1148
1149
1150 \ 7.5.3.1 Dictionary search
1151
1152
1153 \ this does not belong here, but its nice for testing
1154
1155 : words ( -- )
1156   last
1157   begin @ 
1158     ?dup while
1159     dup lfa2name
1160
1161     \ Don't print spaces for headerless words
1162     dup if
1163       type space
1164     else
1165       type
1166     then
1167
1168   repeat
1169   cr
1170   ;
1171
1172
1173 \ 7.3.5.4 Numeric output primitives
1174
1175
1176 false value capital-hex?
1177
1178 : pad       ( -- addr )      here 100 + aligned ;
1179
1180 : todigit   ( num -- ascii ) 
1181   dup 9 > if 
1182     capital-hex? not if
1183       20 +
1184     then
1185     7 + 
1186   then 
1187   30 + 
1188   ;
1189
1190 : <#   pad dup ! ;
1191 : hold pad dup @ 1- tuck swap ! c! ;
1192 : sign 
1193   0< if 
1194     2d hold 
1195   then 
1196   ;
1197
1198 : #    base @ mu/mod rot todigit hold ;
1199 : #s   begin # 2dup or 0= until ;
1200 : #>   2drop pad dup @ tuck - ;
1201 : (.)  <# dup >r abs 0 #s r> sign #> ;
1202
1203 : u#   base @ u/mod swap todigit hold ;
1204 : u#s  begin u# dup 0= until ;
1205 : u#> 0 #> ;
1206 : (u.) <# u#s u#> ;
1207
1208
1209 \ 7.3.5.3 Numeric output
1210
1211
1212 : .    (.) type space ;
1213 : s.   . ;
1214 : u.   (u.) type space ;
1215 : .r   swap (.) rot 2dup < if over - spaces else drop then type ;
1216 : u.r  swap (u.) rot 2dup < if over - spaces else drop then type ;
1217 : .d   base @ swap decimal . base ! ;
1218 : .h   base @ swap hex . base ! ;
1219
1220 : .s 
1221   3c emit depth dup (.) type 3e emit space
1222   0 
1223   ?do
1224     depth i - 1- pick .
1225   loop 
1226   cr
1227   ;
1228
1229
1230 \ 7.3.5.2 Numeric input
1231
1232
1233 : digit ( char base -- n true | char false )
1234   swap dup upc dup 
1235   41 5a ( A - Z ) between if
1236     7 -
1237   else
1238     dup 39 > if \ protect from : and ;
1239       -rot 2drop false exit
1240     then
1241   then
1242   
1243   30 ( number 0 ) - rot over swap 0 swap within  if
1244     nip true
1245   else
1246     drop false
1247   then  
1248   ;
1249
1250 : >number
1251    begin 
1252       dup 
1253    while
1254       over c@ base @ digit 0= if    
1255          drop exit 
1256       then  >r 2swap r> swap base @ um* drop rot base @ um* d+ 2swap 
1257       1 /string 
1258    repeat 
1259    ;
1260
1261 : numdelim?   
1262    dup 2e = swap 2c = or 
1263
1264
1265
1266 : $dnumber?   
1267    0 0 2swap dup 0= if    
1268       2drop 2drop 0 exit 
1269    then  over c@ 2d = dup >r negate /string begin 
1270       >number dup 1 > 
1271    while
1272       over c@ numdelim? 0= if    
1273          2drop 2drop r> drop 0 exit 
1274       then  1 /string 
1275    repeat if    
1276       c@ 2e = if    
1277          true 
1278       else
1279          2drop r> drop 0 exit 
1280       then  
1281    else
1282       drop false 
1283    then  over or if    
1284       r> if    
1285          dnegate 
1286       then  2 
1287    else
1288      drop r> if    
1289          negate 
1290       then  1 
1291    then  
1292
1293
1294
1295 : $number (  )
1296    $dnumber? 
1297    case
1298    0 of   true endof
1299    1 of   false endof
1300    2 of   drop false endof
1301    endcase
1302
1303
1304 : d#
1305   parse-word
1306   base @ >r
1307
1308   decimal
1309
1310   $number if
1311     s" illegal number" type cr 0
1312   then
1313   r> base !
1314   1 handle-lit
1315   ; immediate
1316
1317 : h#
1318   parse-word
1319   base @ >r
1320
1321   hex
1322
1323   $number if
1324     s" illegal number" type cr 0
1325   then
1326   r> base !
1327   1 handle-lit
1328   ; immediate
1329
1330 : o#
1331   parse-word
1332   base @ >r
1333
1334   octal
1335
1336   $number if
1337     s" illegal number" type cr 0
1338   then
1339   r> base !
1340   1 handle-lit
1341   ; immediate
1342
1343
1344
1345 \ 7.3.4.7 String Literals (part 2)
1346
1347
1348 : "
1349   pocket dup
1350   begin
1351     span @ >in @ > if
1352       22 parse >r         ( pocket pocket str  R: len )
1353       over r@ move        \ copy string
1354       r> +                ( pocket nextdest )
1355       ib >in @ + c@       ( pocket nextdest nexchar )
1356       1 >in +!
1357       28 =                \ is nextchar a parenthesis?
1358       span @ >in @ >      \ more input?
1359       and
1360     else
1361       false
1362     then
1363   while
1364     29 parse              \ parse everything up to the next ')'
1365     bounds ?do
1366       i c@ 10 digit if
1367         i 1+ c@ 10 digit if
1368           swap 4 lshift or
1369         else
1370           drop
1371         then
1372         over c! 1+
1373         2
1374       else
1375         drop 1
1376       then
1377     +loop
1378   repeat
1379   over -
1380   handle-text
1381 ; immediate
1382
1383
1384
1385 \ 7.3.3.1 Memory Access (part 2)
1386
1387
1388 : dump ( addr len -- )
1389   over + swap
1390   cr
1391   do i u. space
1392     10 0 do
1393       j i + c@
1394       dup 10 / todigit emit
1395       10 mod todigit emit
1396       space
1397       i 7 = if space then
1398     loop
1399     3 spaces
1400     10 0 do
1401       j i + c@
1402       dup 20 < if drop 2e then \ non-printables as dots?
1403       emit
1404     loop
1405     cr
1406   10 +loop
1407 ;
1408
1409
1410
1411
1412 \ 7.3.9.1 Defining words
1413
1414
1415 : header ( name len -- )
1416   dup if                            \ might be a noname...
1417     2dup $find1 if
1418       drop 2dup type s"  isn't unique." type cr
1419     else
1420       2drop
1421     then
1422   then
1423   null-align
1424   dup -rot ", 80 or c,              \ write name and len
1425   here /n 1- and 0= if 0 c, then    \ pad and space for flags
1426   null-align
1427   80 here 1- c!                     \ write flags byte
1428   here last @ , latest !            \ write backlink and set latest
1429  ;
1430
1431
1432 : :
1433   parse-word header
1434   1 , ]
1435   ;
1436
1437 : :noname 
1438   0 0 header 
1439   here
1440   1 , ]
1441   ;
1442
1443 : ;
1444   locals-dict 0<> if
1445     0 ['] locals-dict /n + !
1446     ['] locals-end , 
1447   then
1448   ['] (semis) , reveal ['] [ execute
1449   ; immediate
1450
1451 : constant
1452   parse-word header
1453   3 , ,                             \ compile DOCON and value
1454   reveal
1455   ;
1456
1457 0 value active-package
1458 : instance, ( size -- )
1459   \ first word of the device node holds the instance size
1460   dup active-package @ dup rot + active-package !
1461   , ,      \ offset size
1462 ;
1463
1464 : instance? ( -- flag )
1465   #instance @ dup if
1466     false #instance !
1467   then
1468 ;
1469
1470 : value
1471   parse-word header
1472   instance? if
1473     /n b , instance, ,              \ DOIVAL
1474   else
1475     3 , ,
1476   then
1477   reveal
1478   ;
1479
1480 : variable
1481   parse-word header
1482   instance? if
1483     /n c , instance, 0 ,
1484   else
1485     4 , 0 ,
1486   then
1487   reveal
1488   ;
1489
1490 : $buffer: ( size str len -- where )
1491   header
1492   instance? if
1493     /n over /n 1- and - /n 1- and +     \ align buffer size
1494     dup c , instance,                   \ DOIVAR
1495   else
1496     4 ,
1497   then
1498   here swap
1499   2dup 0 fill                            \ zerofill
1500   allot
1501   reveal
1502 ;
1503
1504 : buffer: ( size -- )
1505   parse-word $buffer: drop
1506 ;
1507
1508 : (undefined-defer)  ( -- )
1509   \ XXX: this does not work with behavior ... execute
1510   r@ 2 cells - lfa2name
1511   s" undefined defer word " type type cr ;
1512
1513 : (undefined-idefer)  ( -- )
1514   s" undefined idefer word " type cr ;
1515
1516 : defer  (  new-name< >  -- )
1517   parse-word header
1518   instance? if
1519     2 /n* d , instance,                 \ DOIDEFER
1520     ['] (undefined-idefer)
1521   else
1522     5 ,
1523     ['] (undefined-defer)
1524   then
1525   ,
1526   ['] (semis) ,
1527   reveal
1528   ;
1529
1530 : alias  (  new-name< >old-name< >  -- )
1531   parse-word
1532   parse-word $find if
1533     -rot                     \ move xt behind.
1534     header
1535     1 ,                      \ fixme we want our own cfa here.
1536     ,                        \ compile old name xt
1537     ['] (semis) ,
1538     reveal
1539   else
1540     s" undefined word " type type space
1541     2drop
1542   then
1543   ;
1544
1545 : $create
1546   header 6 ,
1547   ['] noop ,
1548   reveal
1549   ;
1550
1551 : create
1552   parse-word $create
1553   ;
1554
1555 : (does>)
1556   r> cell+              \ get address of code to execute
1557   latest @              \ backlink of just "create"d word
1558   cell+ cell+ !         \ write code to execute after the
1559                         \ new word's CFA
1560   ;
1561
1562 : does>
1563   ['] (does>) ,         \ compile does handling
1564   1 ,                   \ compile docol
1565   ; immediate
1566
1567 0 constant struct
1568
1569 : field
1570   create
1571     over ,
1572     +
1573   does>
1574     @ +
1575   ;
1576
1577 : 2constant
1578   create , ,
1579   does> 2@ reveal
1580   ;
1581
1582
1583 \ initializer for the temporary compile buffer
1584
1585
1586 : init-tmp-comp
1587   here 200 allot tmp-comp-buf !
1588 ;
1589
1590 \ the end