/ T0 / kern.t0
kern.t0
  1  : \ `\n parse drop ; immediate
  2  
  3  \ This file defines the core non-native functions (mainly used for
  4  \ parsing words, i.e. not part of the generated output). The line above
  5  \ defines the syntax for comments.
  6  
  7  \ Define parenthesis comments.
  8  \ : ( `) parse drop ; immediate
  9  
 10  : else postpone ahead 1 cs-roll postpone then ; immediate
 11  : while postpone if 1 cs-roll ; immediate
 12  : repeat postpone again postpone then ; immediate
 13  
 14  : ['] ' ; immediate
 15  : [compile] compile ; immediate
 16  
 17  : 2drop drop drop ;
 18  : dup2 over over ;
 19  
 20  \ Local variables are defined with the native word '(local)'. We define
 21  \ a helper construction that mimics what is found in Apple's Open Firmware
 22  \ implementation. The syntax is: { a b ... ; c d ... }
 23  \ I.e. there is an opening brace, then some names. Names appearing before
 24  \ the semicolon are locals that are both defined and then filled with the
 25  \ values on stack (in stack order: { a b } fills 'b' with the top-of-stack,
 26  \ and 'a' with the value immediately below). Names appearing after the
 27  \ semicolon are not initialized.
 28  : __deflocal ( from_stack name -- )
 29  	dup (local) swap if
 30  		compile-local-write
 31  	else
 32  		drop
 33  	then ;
 34  : __deflocals ( from_stack -- )
 35  	next-word
 36  	dup "}" eqstr if
 37  		2drop ret
 38  	then
 39  	dup ";" eqstr if
 40  		2drop 0 __deflocals ret
 41  	then
 42  	over __deflocals
 43  	__deflocal ;
 44  : {
 45  	-1 __deflocals ; immediate
 46  
 47  \ Data building words.
 48  : data:
 49  	new-data-block next-word define-data-word ;
 50  : hexb|
 51  	0 0 { acc z }
 52  	begin
 53  		char
 54  		dup `| = if
 55  			z if "Truncated hexadecimal byte" puts cr exitvm then
 56  			ret
 57  		then
 58  		dup 0x20 > if
 59  			hexval
 60  			z if acc 4 << + data-add8 else >acc then
 61  			z not >z
 62  		then
 63  	again ;
 64  
 65  \ Convert hexadecimal character to number. Complain loudly if conversion
 66  \ is not possible.
 67  : hexval ( char -- x )
 68  	hexval-nf dup 0 < if "Not an hex digit: " puts . cr exitvm then ;
 69  
 70  \ Convert hexadecimal character to number. If not an hexadecimal digit,
 71  \ return -1.
 72  : hexval-nf ( char -- x )
 73  	dup dup `0 >= swap `9 <= and if `0 - ret then
 74  	dup dup `A >= swap `F <= and if `A - 10 + ret then
 75  	dup dup `a >= swap `f <= and if `a - 10 + ret then
 76  	drop -1 ;
 77  
 78  \ Convert decimal character to number. Complain loudly if conversion
 79  \ is not possible.
 80  : decval ( char -- x )
 81  	decval-nf dup 0 < if "Not a decimal digit: " puts . cr exitvm then ;
 82  
 83  \ Convert decimal character to number. If not a decimal digit,
 84  \ return -1.
 85  : decval-nf ( char -- x )
 86  	dup dup `0 >= swap `9 <= and if `0 - ret then
 87  	drop -1 ;
 88  
 89  \ Commonly used shorthands.
 90  : 1+ 1 + ;
 91  : 2+ 2 + ;
 92  : 1- 1 - ;
 93  : 2- 2 - ;
 94  : 0= 0 = ;
 95  : 0<> 0 <> ;
 96  : 0< 0 < ;
 97  : 0> 0 > ;
 98  
 99  \ Get a 16-bit value from the constant data block. This uses big-endian
100  \ encoding.
101  : data-get16 ( addr -- x )
102  	dup data-get8 8 << swap 1+ data-get8 + ;
103  
104  \ The case..endcase construction is the equivalent of 'switch' is C.
105  \ Usage:
106  \     case
107  \         E1 of C1 endof
108  \         E2 of C2 endof
109  \         ...
110  \         CN
111  \     endcase
112  \
113  \ Upon entry, it considers the TOS (let's call it X). It will then evaluate
114  \ E1, which should yield a single value Y1; at that point, the X value is
115  \ still on the stack, just below Y1, and must remain untouched. The 'of'
116  \ word compares X with Y1; if they are equal, C1 is executed, and then
117  \ control jumps to after the 'endcase'. The X value is popped from the
118  \ stack immediately before evaluating C1.
119  \
120  \ If X and Y1 are not equal, flow proceeds to E2, to obtain a value Y2 to
121  \ compare with X. And so on.
122  \
123  \ If none of the 'of' clauses found a match, then CN is evaluated. When CN
124  \ is evaluated, the X value is on the TOS, and CN must either leave it on
125  \ the stack, or replace it with exactly one value; the 'endcase' word
126  \ expects (and drops) one value.
127  \
128  \ Implementation: this is mostly copied from ANS Forth specification,
129  \ although simplified a bit because we know that our control-flow stack
130  \ is independent of the data stack. During compilation, the number of
131  \ clauses is maintained on the stack; each of..endof clause really is
132  \ an 'if..else' that must be terminated with a matching 'then' in 'endcase'.
133  
134  : case 0 ; immediate
135  : of 1+ postpone over postpone = postpone if postpone drop ; immediate
136  : endof postpone else ; immediate
137  : endcase
138  	postpone drop
139  	begin dup while 1- postpone then repeat drop ; immediate
140  
141  \ A simpler and more generic "case": there is no management for a value
142  \ on the stack, and each test is supposed to come up with its own boolean
143  \ value.
144  : choice 0 ; immediate
145  : uf 1+ postpone if ; immediate
146  : ufnot 1+ postpone ifnot ; immediate
147  : enduf postpone else ; immediate
148  : endchoice begin dup while 1- postpone then repeat drop ; immediate
149  
150  \ C implementations for native words that can be used in generated code.
151  add-cc: co { T0_CO(); }
152  add-cc: execute { T0_ENTER(ip, rp, T0_POP()); }
153  add-cc: drop { (void)T0_POP(); }
154  add-cc: dup { T0_PUSH(T0_PEEK(0)); }
155  add-cc: swap { T0_SWAP(); }
156  add-cc: over { T0_PUSH(T0_PEEK(1)); }
157  add-cc: rot { T0_ROT(); }
158  add-cc: -rot { T0_NROT(); }
159  add-cc: roll { T0_ROLL(T0_POP()); }
160  add-cc: pick { T0_PICK(T0_POP()); }
161  add-cc: + {
162  	uint32_t b = T0_POP();
163  	uint32_t a = T0_POP();
164  	T0_PUSH(a + b);
165  }
166  add-cc: - {
167  	uint32_t b = T0_POP();
168  	uint32_t a = T0_POP();
169  	T0_PUSH(a - b);
170  }
171  add-cc: neg {
172  	uint32_t a = T0_POP();
173  	T0_PUSH(-a);
174  }
175  add-cc: * {
176  	uint32_t b = T0_POP();
177  	uint32_t a = T0_POP();
178  	T0_PUSH(a * b);
179  }
180  add-cc: / {
181  	int32_t b = T0_POPi();
182  	int32_t a = T0_POPi();
183  	T0_PUSHi(a / b);
184  }
185  add-cc: u/ {
186  	uint32_t b = T0_POP();
187  	uint32_t a = T0_POP();
188  	T0_PUSH(a / b);
189  }
190  add-cc: % {
191  	int32_t b = T0_POPi();
192  	int32_t a = T0_POPi();
193  	T0_PUSHi(a % b);
194  }
195  add-cc: u% {
196  	uint32_t b = T0_POP();
197  	uint32_t a = T0_POP();
198  	T0_PUSH(a % b);
199  }
200  add-cc: < {
201  	int32_t b = T0_POPi();
202  	int32_t a = T0_POPi();
203  	T0_PUSH(-(uint32_t)(a < b));
204  }
205  add-cc: <= {
206  	int32_t b = T0_POPi();
207  	int32_t a = T0_POPi();
208  	T0_PUSH(-(uint32_t)(a <= b));
209  }
210  add-cc: > {
211  	int32_t b = T0_POPi();
212  	int32_t a = T0_POPi();
213  	T0_PUSH(-(uint32_t)(a > b));
214  }
215  add-cc: >= {
216  	int32_t b = T0_POPi();
217  	int32_t a = T0_POPi();
218  	T0_PUSH(-(uint32_t)(a >= b));
219  }
220  add-cc: = {
221  	uint32_t b = T0_POP();
222  	uint32_t a = T0_POP();
223  	T0_PUSH(-(uint32_t)(a == b));
224  }
225  add-cc: <> {
226  	uint32_t b = T0_POP();
227  	uint32_t a = T0_POP();
228  	T0_PUSH(-(uint32_t)(a != b));
229  }
230  add-cc: u< {
231  	uint32_t b = T0_POP();
232  	uint32_t a = T0_POP();
233  	T0_PUSH(-(uint32_t)(a < b));
234  }
235  add-cc: u<= {
236  	uint32_t b = T0_POP();
237  	uint32_t a = T0_POP();
238  	T0_PUSH(-(uint32_t)(a <= b));
239  }
240  add-cc: u> {
241  	uint32_t b = T0_POP();
242  	uint32_t a = T0_POP();
243  	T0_PUSH(-(uint32_t)(a > b));
244  }
245  add-cc: u>= {
246  	uint32_t b = T0_POP();
247  	uint32_t a = T0_POP();
248  	T0_PUSH(-(uint32_t)(a >= b));
249  }
250  add-cc: and {
251  	uint32_t b = T0_POP();
252  	uint32_t a = T0_POP();
253  	T0_PUSH(a & b);
254  }
255  add-cc: or {
256  	uint32_t b = T0_POP();
257  	uint32_t a = T0_POP();
258  	T0_PUSH(a | b);
259  }
260  add-cc: xor {
261  	uint32_t b = T0_POP();
262  	uint32_t a = T0_POP();
263  	T0_PUSH(a ^ b);
264  }
265  add-cc: not {
266  	uint32_t a = T0_POP();
267  	T0_PUSH(~a);
268  }
269  add-cc: << {
270  	int c = (int)T0_POPi();
271  	uint32_t x = T0_POP();
272  	T0_PUSH(x << c);
273  }
274  add-cc: >> {
275  	int c = (int)T0_POPi();
276  	int32_t x = T0_POPi();
277  	T0_PUSHi(x >> c);
278  }
279  add-cc: u>> {
280  	int c = (int)T0_POPi();
281  	uint32_t x = T0_POP();
282  	T0_PUSH(x >> c);
283  }
284  add-cc: data-get8 {
285  	size_t addr = T0_POP();
286  	T0_PUSH(t0_datablock[addr]);
287  }
288  
289  add-cc: . {
290  	extern int printf(const char *fmt, ...);
291  	printf(" %ld", (long)T0_POPi());
292  }
293  add-cc: putc {
294  	extern int printf(const char *fmt, ...);
295  	printf("%c", (char)T0_POPi());
296  }
297  add-cc: puts {
298  	extern int printf(const char *fmt, ...);
299  	printf("%s", &t0_datablock[T0_POPi()]);
300  }
301  add-cc: cr {
302  	extern int printf(const char *fmt, ...);
303  	printf("\n");
304  }
305  add-cc: eqstr {
306  	const void *b = &t0_datablock[T0_POPi()];
307  	const void *a = &t0_datablock[T0_POPi()];
308  	T0_PUSH(-(int32_t)(strcmp(a, b) == 0));
309  }