xref: /illumos-gate/usr/src/common/ficl/primitives.c (revision d2a70789f056fc6c9ce3ab047b52126d80b0e3da)
1 /*
2  * w o r d s . c
3  * Forth Inspired Command Language
4  * ANS Forth CORE word-set written in C
5  * Author: John Sadler (john_sadler@alum.mit.edu)
6  * Created: 19 July 1997
7  * $Id: primitives.c,v 1.4 2010/09/13 18:43:04 asau Exp $
8  */
9 /*
10  * Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
11  * All rights reserved.
12  *
13  * Get the latest Ficl release at http://ficl.sourceforge.net
14  *
15  * I am interested in hearing from anyone who uses Ficl. If you have
16  * a problem, a success story, a defect, an enhancement request, or
17  * if you would like to contribute to the Ficl release, please
18  * contact me by email at the address above.
19  *
20  * L I C E N S E  and  D I S C L A I M E R
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the above copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
32  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
35  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41  * SUCH DAMAGE.
42  */
43 
44 #include "ficl.h"
45 #include <limits.h>
46 
47 /*
48  * Control structure building words use these
49  * strings' addresses as markers on the stack to
50  * check for structure completion.
51  */
52 static char doTag[]    = "do";
53 static char colonTag[] = "colon";
54 static char leaveTag[] = "leave";
55 
56 static char destTag[]  = "target";
57 static char origTag[]  = "origin";
58 
59 static char caseTag[]  = "case";
60 static char ofTag[]  = "of";
61 static char fallthroughTag[]  = "fallthrough";
62 
63 /*
64  * C O N T R O L   S T R U C T U R E   B U I L D E R S
65  *
66  * Push current dictionary location for later branch resolution.
67  * The location may be either a branch target or a patch address...
68  */
69 static void
70 markBranch(ficlDictionary *dictionary, ficlVm *vm, char *tag)
71 {
72 	ficlStackPushPointer(vm->dataStack, dictionary->here);
73 	ficlStackPushPointer(vm->dataStack, tag);
74 }
75 
76 static void
77 markControlTag(ficlVm *vm, char *tag)
78 {
79 	ficlStackPushPointer(vm->dataStack, tag);
80 }
81 
82 static void
83 matchControlTag(ficlVm *vm, char *wantTag)
84 {
85 	char *tag;
86 
87 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
88 
89 	tag = (char *)ficlStackPopPointer(vm->dataStack);
90 
91 	/*
92 	 * Changed the code below to compare the pointers first
93 	 * (by popular demand)
94 	 */
95 	if ((tag != wantTag) && strcmp(tag, wantTag)) {
96 		ficlVmThrowError(vm,
97 		    "Error -- unmatched control structure \"%s\"", wantTag);
98 	}
99 }
100 
101 /*
102  * Expect a branch target address on the param stack,
103  * FICL_VM_STATE_COMPILE a literal offset from the current dictionary location
104  * to the target address
105  */
106 static void
107 resolveBackBranch(ficlDictionary *dictionary, ficlVm *vm, char *tag)
108 {
109 	ficlCell *patchAddr, c;
110 
111 	matchControlTag(vm, tag);
112 
113 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
114 
115 	patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack);
116 	c.i = patchAddr - dictionary->here;
117 
118 	ficlDictionaryAppendCell(dictionary, c);
119 }
120 
121 /*
122  * Expect a branch patch address on the param stack,
123  * FICL_VM_STATE_COMPILE a literal offset from the patch location
124  * to the current dictionary location
125  */
126 static void
127 resolveForwardBranch(ficlDictionary *dictionary, ficlVm *vm, char *tag)
128 {
129 	ficlInteger offset;
130 	ficlCell *patchAddr;
131 
132 	matchControlTag(vm, tag);
133 
134 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
135 
136 	patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack);
137 	offset = dictionary->here - patchAddr;
138 	(*patchAddr).i = offset;
139 }
140 
141 /*
142  * Match the tag to the top of the stack. If success,
143  * sopy "here" address into the ficlCell whose address is next
144  * on the stack. Used by do..leave..loop.
145  */
146 static void
147 resolveAbsBranch(ficlDictionary *dictionary, ficlVm *vm, char *wantTag)
148 {
149 	ficlCell *patchAddr;
150 	char *tag;
151 
152 	FICL_STACK_CHECK(vm->dataStack, 2, 0);
153 
154 	tag = ficlStackPopPointer(vm->dataStack);
155 
156 	/*
157 	 * Changed the comparison below to compare the pointers first
158 	 * (by popular demand)
159 	 */
160 	if ((tag != wantTag) && strcmp(tag, wantTag)) {
161 		ficlVmTextOut(vm, "Warning -- Unmatched control word: ");
162 		ficlVmTextOut(vm, wantTag);
163 		ficlVmTextOut(vm, "\n");
164 	}
165 
166 	patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack);
167 	(*patchAddr).p = dictionary->here;
168 }
169 
170 /*
171  * c o l o n   d e f i n i t i o n s
172  * Code to begin compiling a colon definition
173  * This function sets the state to FICL_VM_STATE_COMPILE, then creates a
174  * new word whose name is the next word in the input stream
175  * and whose code is colonParen.
176  */
177 static void
178 ficlPrimitiveColon(ficlVm *vm)
179 {
180 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
181 	ficlString name = ficlVmGetWord(vm);
182 
183 	vm->state = FICL_VM_STATE_COMPILE;
184 	markControlTag(vm, colonTag);
185 	ficlDictionaryAppendWord(dictionary, name,
186 	    (ficlPrimitive)ficlInstructionColonParen,
187 	    FICL_WORD_DEFAULT | FICL_WORD_SMUDGED);
188 
189 #if FICL_WANT_LOCALS
190 	vm->callback.system->localsCount = 0;
191 #endif
192 }
193 
194 static void
195 ficlPrimitiveSemicolonCoIm(ficlVm *vm)
196 {
197 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
198 
199 	matchControlTag(vm, colonTag);
200 
201 #if FICL_WANT_LOCALS
202 	if (vm->callback.system->localsCount > 0) {
203 		ficlDictionary *locals;
204 		locals = ficlSystemGetLocals(vm->callback.system);
205 		ficlDictionaryEmpty(locals, locals->forthWordlist->size);
206 		ficlDictionaryAppendUnsigned(dictionary,
207 		    ficlInstructionUnlinkParen);
208 	}
209 	vm->callback.system->localsCount = 0;
210 #endif
211 
212 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionSemiParen);
213 	vm->state = FICL_VM_STATE_INTERPRET;
214 	ficlDictionaryUnsmudge(dictionary);
215 }
216 
217 /*
218  * e x i t
219  * CORE
220  * This function simply pops the previous instruction
221  * pointer and returns to the "next" loop. Used for exiting from within
222  * a definition. Note that exitParen is identical to semiParen - they
223  * are in two different functions so that "see" can correctly identify
224  * the end of a colon definition, even if it uses "exit".
225  */
226 static void
227 ficlPrimitiveExitCoIm(ficlVm *vm)
228 {
229 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
230 	FICL_IGNORE(vm);
231 
232 #if FICL_WANT_LOCALS
233 	if (vm->callback.system->localsCount > 0) {
234 		ficlDictionaryAppendUnsigned(dictionary,
235 		    ficlInstructionUnlinkParen);
236 	}
237 #endif
238 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionExitParen);
239 }
240 
241 /*
242  * c o n s t a n t
243  * IMMEDIATE
244  * Compiles a constant into the dictionary. Constants return their
245  * value when invoked. Expects a value on top of the parm stack.
246  */
247 static void
248 ficlPrimitiveConstant(ficlVm *vm)
249 {
250 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
251 	ficlString name = ficlVmGetWord(vm);
252 
253 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
254 
255 	ficlDictionaryAppendConstantInstruction(dictionary, name,
256 	    ficlInstructionConstantParen, ficlStackPopInteger(vm->dataStack));
257 }
258 
259 static void
260 ficlPrimitive2Constant(ficlVm *vm)
261 {
262 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
263 	ficlString name = ficlVmGetWord(vm);
264 
265 	FICL_STACK_CHECK(vm->dataStack, 2, 0);
266 
267 	ficlDictionaryAppend2ConstantInstruction(dictionary, name,
268 	    ficlInstruction2ConstantParen, ficlStackPop2Integer(vm->dataStack));
269 }
270 
271 /*
272  * d i s p l a y C e l l
273  * Drop and print the contents of the ficlCell at the top of the param
274  * stack
275  */
276 static void
277 ficlPrimitiveDot(ficlVm *vm)
278 {
279 	ficlCell c;
280 
281 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
282 
283 	c = ficlStackPop(vm->dataStack);
284 	ficlLtoa((c).i, vm->pad, vm->base);
285 	strcat(vm->pad, " ");
286 	ficlVmTextOut(vm, vm->pad);
287 }
288 
289 static void
290 ficlPrimitiveUDot(ficlVm *vm)
291 {
292 	ficlUnsigned u;
293 
294 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
295 
296 	u = ficlStackPopUnsigned(vm->dataStack);
297 	ficlUltoa(u, vm->pad, vm->base);
298 	strcat(vm->pad, " ");
299 	ficlVmTextOut(vm, vm->pad);
300 }
301 
302 static void
303 ficlPrimitiveHexDot(ficlVm *vm)
304 {
305 	ficlUnsigned u;
306 
307 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
308 
309 	u = ficlStackPopUnsigned(vm->dataStack);
310 	ficlUltoa(u, vm->pad, 16);
311 	strcat(vm->pad, " ");
312 	ficlVmTextOut(vm, vm->pad);
313 }
314 
315 /*
316  * s t r l e n
317  * Ficl   ( c-string -- length )
318  *
319  * Returns the length of a C-style (zero-terminated) string.
320  *
321  * --lch
322  */
323 static void
324 ficlPrimitiveStrlen(ficlVm *vm)
325 {
326 	char *address = (char *)ficlStackPopPointer(vm->dataStack);
327 	ficlStackPushInteger(vm->dataStack, strlen(address));
328 }
329 
330 /*
331  * s p r i n t f
332  * Ficl	( i*x c-addr-fmt u-fmt c-addr-buffer u-buffer --
333  *	c-addr-buffer u-written success-flag )
334  * Similar to the C sprintf() function.  It formats into a buffer based on
335  * a "format" string.  Each character in the format string is copied verbatim
336  * to the output buffer, until SPRINTF encounters a percent sign ("%").
337  * SPRINTF then skips the percent sign, and examines the next character
338  * (the "format character").  Here are the valid format characters:
339  *    s - read a C-ADDR U-LENGTH string from the stack and copy it to
340  *        the buffer
341  *    d - read a ficlCell from the stack, format it as a string (base-10,
342  *        signed), and copy it to the buffer
343  *    x - same as d, except in base-16
344  *    u - same as d, but unsigned
345  *    % - output a literal percent-sign to the buffer
346  * SPRINTF returns the c-addr-buffer argument unchanged, the number of bytes
347  * written, and a flag indicating whether or not it ran out of space while
348  * writing to the output buffer (FICL_TRUE if it ran out of space).
349  *
350  * If SPRINTF runs out of space in the buffer to store the formatted string,
351  * it still continues parsing, in an effort to preserve your stack (otherwise
352  * it might leave uneaten arguments behind).
353  *
354  * --lch
355  */
356 static void
357 ficlPrimitiveSprintf(ficlVm *vm)
358 {
359 	int bufferLength = ficlStackPopInteger(vm->dataStack);
360 	char *buffer = (char *)ficlStackPopPointer(vm->dataStack);
361 	char *bufferStart = buffer;
362 
363 	int formatLength = ficlStackPopInteger(vm->dataStack);
364 	char *format = (char *)ficlStackPopPointer(vm->dataStack);
365 	char *formatStop = format + formatLength;
366 
367 	int base = 10;
368 	int unsignedInteger = 0; /* false */
369 
370 	int append = 1; /* true */
371 
372 	while (format < formatStop) {
373 		char scratch[64];
374 		char *source;
375 		int actualLength;
376 		int desiredLength;
377 		int leadingZeroes;
378 
379 		if (*format != '%') {
380 			source = format;
381 			actualLength = desiredLength = 1;
382 			leadingZeroes = 0;
383 		} else {
384 			format++;
385 			if (format == formatStop)
386 				break;
387 
388 			leadingZeroes = (*format == '0');
389 			if (leadingZeroes) {
390 				format++;
391 				if (format == formatStop)
392 					break;
393 			}
394 
395 			desiredLength = isdigit((unsigned char)*format);
396 			if (desiredLength) {
397 				desiredLength = strtoul(format, &format, 10);
398 				if (format == formatStop)
399 					break;
400 			} else if (*format == '*') {
401 				desiredLength =
402 				    ficlStackPopInteger(vm->dataStack);
403 
404 				format++;
405 				if (format == formatStop)
406 					break;
407 			}
408 
409 			switch (*format) {
410 			case 's':
411 			case 'S':
412 				actualLength =
413 				    ficlStackPopInteger(vm->dataStack);
414 				source = (char *)
415 				    ficlStackPopPointer(vm->dataStack);
416 				break;
417 			case 'x':
418 			case 'X':
419 				base = 16;
420 			case 'u':
421 			case 'U':
422 				unsignedInteger = 1; /* true */
423 			case 'd':
424 			case 'D': {
425 				int integer;
426 				integer = ficlStackPopInteger(vm->dataStack);
427 				if (unsignedInteger)
428 					ficlUltoa(integer, scratch, base);
429 				else
430 					ficlLtoa(integer, scratch, base);
431 				base = 10;
432 				unsignedInteger = 0; /* false */
433 				source = scratch;
434 				actualLength = strlen(scratch);
435 				break;
436 			}
437 			case '%':
438 				source = format;
439 				actualLength = 1;
440 			default:
441 				continue;
442 			}
443 		}
444 
445 		if (append) {
446 			if (!desiredLength)
447 				desiredLength = actualLength;
448 			if (desiredLength > bufferLength) {
449 				append = 0; /* false */
450 				desiredLength = bufferLength;
451 			}
452 			while (desiredLength > actualLength) {
453 				*buffer++ = (char)((leadingZeroes) ? '0' : ' ');
454 				bufferLength--;
455 				desiredLength--;
456 			}
457 			memcpy(buffer, source, actualLength);
458 			buffer += actualLength;
459 			bufferLength -= actualLength;
460 		}
461 
462 		format++;
463 	}
464 
465 	ficlStackPushPointer(vm->dataStack, bufferStart);
466 	ficlStackPushInteger(vm->dataStack, buffer - bufferStart);
467 	ficlStackPushInteger(vm->dataStack, FICL_BOOL(!append));
468 }
469 
470 /*
471  * d u p   &   f r i e n d s
472  */
473 static void
474 ficlPrimitiveDepth(ficlVm *vm)
475 {
476 	int i;
477 
478 	FICL_STACK_CHECK(vm->dataStack, 0, 1);
479 
480 	i = ficlStackDepth(vm->dataStack);
481 	ficlStackPushInteger(vm->dataStack, i);
482 }
483 
484 /*
485  * e m i t   &   f r i e n d s
486  */
487 static void
488 ficlPrimitiveEmit(ficlVm *vm)
489 {
490 	char buffer[2];
491 	int i;
492 
493 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
494 
495 	i = ficlStackPopInteger(vm->dataStack);
496 	buffer[0] = (char)i;
497 	buffer[1] = '\0';
498 	ficlVmTextOut(vm, buffer);
499 }
500 
501 static void
502 ficlPrimitiveCR(ficlVm *vm)
503 {
504 	ficlVmTextOut(vm, "\n");
505 }
506 
507 static void
508 ficlPrimitiveBackslash(ficlVm *vm)
509 {
510 	char *trace = ficlVmGetInBuf(vm);
511 	char *stop = ficlVmGetInBufEnd(vm);
512 	char c = *trace;
513 
514 	while ((trace != stop) && (c != '\r') && (c != '\n')) {
515 		c = *++trace;
516 	}
517 
518 	/*
519 	 * Cope with DOS or UNIX-style EOLs -
520 	 * Check for /r, /n, /r/n, or /n/r end-of-line sequences,
521 	 * and point trace to next char. If EOL is \0, we're done.
522 	 */
523 	if (trace != stop) {
524 		trace++;
525 
526 		if ((trace != stop) && (c != *trace) &&
527 		    ((*trace == '\r') || (*trace == '\n')))
528 			trace++;
529 	}
530 
531 	ficlVmUpdateTib(vm, trace);
532 }
533 
534 /*
535  * paren CORE
536  * Compilation: Perform the execution semantics given below.
537  * Execution: ( "ccc<paren>" -- )
538  * Parse ccc delimited by ) (right parenthesis). ( is an immediate word.
539  * The number of characters in ccc may be zero to the number of characters
540  * in the parse area.
541  */
542 static void
543 ficlPrimitiveParenthesis(ficlVm *vm)
544 {
545 	ficlVmParseStringEx(vm, ')', 0);
546 }
547 
548 /*
549  * F E T C H   &   S T O R E
550  */
551 
552 /*
553  * i f C o I m
554  * IMMEDIATE
555  * Compiles code for a conditional branch into the dictionary
556  * and pushes the branch patch address on the stack for later
557  * patching by ELSE or THEN/ENDIF.
558  */
559 static void
560 ficlPrimitiveIfCoIm(ficlVm *vm)
561 {
562 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
563 
564 	ficlDictionaryAppendUnsigned(dictionary,
565 	    ficlInstructionBranch0ParenWithCheck);
566 	markBranch(dictionary, vm, origTag);
567 	ficlDictionaryAppendUnsigned(dictionary, 1);
568 }
569 
570 /*
571  * e l s e C o I m
572  *
573  * IMMEDIATE -- compiles an "else"...
574  * 1) FICL_VM_STATE_COMPILE a branch and a patch address;
575  *    the address gets patched
576  *    by "endif" to point past the "else" code.
577  * 2) Pop the the "if" patch address
578  * 3) Patch the "if" branch to point to the current FICL_VM_STATE_COMPILE
579  *    address.
580  * 4) Push the "else" patch address. ("endif" patches this to jump past
581  *    the "else" code.
582  */
583 static void
584 ficlPrimitiveElseCoIm(ficlVm *vm)
585 {
586 	ficlCell *patchAddr;
587 	ficlInteger offset;
588 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
589 
590 	/* (1) FICL_VM_STATE_COMPILE branch runtime */
591 	ficlDictionaryAppendUnsigned(dictionary,
592 	    ficlInstructionBranchParenWithCheck);
593 
594 	matchControlTag(vm, origTag);
595 						/* (2) pop "if" patch addr */
596 	patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack);
597 	markBranch(dictionary, vm, origTag);	/* (4) push "else" patch addr */
598 
599 			/* (1) FICL_VM_STATE_COMPILE patch placeholder */
600 	ficlDictionaryAppendUnsigned(dictionary, 1);
601 	offset = dictionary->here - patchAddr;
602 	(*patchAddr).i = offset;		/* (3) Patch "if" */
603 }
604 
605 /*
606  * e n d i f C o I m
607  */
608 static void
609 ficlPrimitiveEndifCoIm(ficlVm *vm)
610 {
611 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
612 	resolveForwardBranch(dictionary, vm, origTag);
613 }
614 
615 /*
616  * c a s e C o I m
617  * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY
618  *
619  *
620  * At FICL_VM_STATE_COMPILE-time, a CASE-SYS (see DPANS94 6.2.0873) looks
621  * like this:
622  *			i*addr i caseTag
623  * and an OF-SYS (see DPANS94 6.2.1950) looks like this:
624  *			i*addr i caseTag addr ofTag
625  * The integer under caseTag is the count of fixup addresses that branch
626  * to ENDCASE.
627  */
628 static void
629 ficlPrimitiveCaseCoIm(ficlVm *vm)
630 {
631 	FICL_STACK_CHECK(vm->dataStack, 0, 2);
632 
633 	ficlStackPushUnsigned(vm->dataStack, 0);
634 	markControlTag(vm, caseTag);
635 }
636 
637 /*
638  * e n d c a s eC o I m
639  * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY
640  */
641 static void
642 ficlPrimitiveEndcaseCoIm(ficlVm *vm)
643 {
644 	ficlUnsigned fixupCount;
645 	ficlDictionary *dictionary;
646 	ficlCell *patchAddr;
647 	ficlInteger offset;
648 
649 	/*
650 	 * if the last OF ended with FALLTHROUGH,
651 	 * just add the FALLTHROUGH fixup to the
652 	 * ENDOF fixups
653 	 */
654 	if (ficlStackGetTop(vm->dataStack).p == fallthroughTag) {
655 		matchControlTag(vm, fallthroughTag);
656 		patchAddr = ficlStackPopPointer(vm->dataStack);
657 		matchControlTag(vm, caseTag);
658 		fixupCount = ficlStackPopUnsigned(vm->dataStack);
659 		ficlStackPushPointer(vm->dataStack, patchAddr);
660 		ficlStackPushUnsigned(vm->dataStack, fixupCount + 1);
661 		markControlTag(vm, caseTag);
662 	}
663 
664 	matchControlTag(vm, caseTag);
665 
666 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
667 
668 	fixupCount = ficlStackPopUnsigned(vm->dataStack);
669 	FICL_STACK_CHECK(vm->dataStack, fixupCount, 0);
670 
671 	dictionary = ficlVmGetDictionary(vm);
672 
673 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionDrop);
674 
675 	while (fixupCount--) {
676 		patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack);
677 		offset = dictionary->here - patchAddr;
678 		(*patchAddr).i = offset;
679 	}
680 }
681 
682 /*
683  * o f C o I m
684  * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY
685  */
686 static void
687 ficlPrimitiveOfCoIm(ficlVm *vm)
688 {
689 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
690 	ficlCell *fallthroughFixup = NULL;
691 
692 	FICL_STACK_CHECK(vm->dataStack, 1, 3);
693 
694 	if (ficlStackGetTop(vm->dataStack).p == fallthroughTag) {
695 		matchControlTag(vm, fallthroughTag);
696 		fallthroughFixup = ficlStackPopPointer(vm->dataStack);
697 	}
698 
699 	matchControlTag(vm, caseTag);
700 
701 	markControlTag(vm, caseTag);
702 
703 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionOfParen);
704 	markBranch(dictionary, vm, ofTag);
705 	ficlDictionaryAppendUnsigned(dictionary, 2);
706 
707 	if (fallthroughFixup != NULL) {
708 		ficlInteger offset = dictionary->here - fallthroughFixup;
709 		(*fallthroughFixup).i = offset;
710 	}
711 }
712 
713 /*
714  * e n d o f C o I m
715  * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY
716  */
717 static void
718 ficlPrimitiveEndofCoIm(ficlVm *vm)
719 {
720 	ficlCell *patchAddr;
721 	ficlUnsigned fixupCount;
722 	ficlInteger offset;
723 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
724 
725 	FICL_STACK_CHECK(vm->dataStack, 4, 3);
726 
727 	/* ensure we're in an OF, */
728 	matchControlTag(vm, ofTag);
729 
730 	/* grab the address of the branch location after the OF */
731 	patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack);
732 	/* ensure we're also in a "case" */
733 	matchControlTag(vm, caseTag);
734 	/* grab the current number of ENDOF fixups */
735 	fixupCount = ficlStackPopUnsigned(vm->dataStack);
736 
737 	/* FICL_VM_STATE_COMPILE branch runtime */
738 	ficlDictionaryAppendUnsigned(dictionary,
739 	    ficlInstructionBranchParenWithCheck);
740 
741 	/*
742 	 * push a new ENDOF fixup, the updated count of ENDOF fixups,
743 	 * and the caseTag
744 	 */
745 	ficlStackPushPointer(vm->dataStack, dictionary->here);
746 	ficlStackPushUnsigned(vm->dataStack, fixupCount + 1);
747 	markControlTag(vm, caseTag);
748 
749 	/* reserve space for the ENDOF fixup */
750 	ficlDictionaryAppendUnsigned(dictionary, 2);
751 
752 	/* and patch the original OF */
753 	offset = dictionary->here - patchAddr;
754 	(*patchAddr).i = offset;
755 }
756 
757 /*
758  * f a l l t h r o u g h C o I m
759  * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY
760  */
761 static void
762 ficlPrimitiveFallthroughCoIm(ficlVm *vm)
763 {
764 	ficlCell *patchAddr;
765 	ficlInteger offset;
766 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
767 
768 	FICL_STACK_CHECK(vm->dataStack, 4, 3);
769 
770 	/* ensure we're in an OF, */
771 	matchControlTag(vm, ofTag);
772 	/* grab the address of the branch location after the OF */
773 	patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack);
774 	/* ensure we're also in a "case" */
775 	matchControlTag(vm, caseTag);
776 
777 	/* okay, here we go.  put the case tag back. */
778 	markControlTag(vm, caseTag);
779 
780 	/* FICL_VM_STATE_COMPILE branch runtime */
781 	ficlDictionaryAppendUnsigned(dictionary,
782 	    ficlInstructionBranchParenWithCheck);
783 
784 	/* push a new FALLTHROUGH fixup and the fallthroughTag */
785 	ficlStackPushPointer(vm->dataStack, dictionary->here);
786 	markControlTag(vm, fallthroughTag);
787 
788 	/* reserve space for the FALLTHROUGH fixup */
789 	ficlDictionaryAppendUnsigned(dictionary, 2);
790 
791 	/* and patch the original OF */
792 	offset = dictionary->here - patchAddr;
793 	(*patchAddr).i = offset;
794 }
795 
796 /*
797  * h a s h
798  * hash ( c-addr u -- code)
799  * calculates hashcode of specified string and leaves it on the stack
800  */
801 static void
802 ficlPrimitiveHash(ficlVm *vm)
803 {
804 	ficlString s;
805 
806 	FICL_STRING_SET_LENGTH(s, ficlStackPopUnsigned(vm->dataStack));
807 	FICL_STRING_SET_POINTER(s, ficlStackPopPointer(vm->dataStack));
808 	ficlStackPushUnsigned(vm->dataStack, ficlHashCode(s));
809 }
810 
811 /*
812  * i n t e r p r e t
813  * This is the "user interface" of a Forth. It does the following:
814  *   while there are words in the VM's Text Input Buffer
815  *     Copy next word into the pad (ficlVmGetWord)
816  *     Attempt to find the word in the dictionary (ficlDictionaryLookup)
817  *     If successful, execute the word.
818  *     Otherwise, attempt to convert the word to a number (isNumber)
819  *     If successful, push the number onto the parameter stack.
820  *     Otherwise, print an error message and exit loop...
821  *   End Loop
822  *
823  * From the standard, section 3.4
824  * Text interpretation (see 6.1.1360 EVALUATE and 6.1.2050 QUIT) shall
825  * repeat the following steps until either the parse area is empty or an
826  * ambiguous condition exists:
827  * a) Skip leading spaces and parse a name (see 3.4.1);
828  */
829 static void
830 ficlPrimitiveInterpret(ficlVm *vm)
831 {
832 	ficlString s;
833 	int i;
834 	ficlSystem *system;
835 
836 	FICL_VM_ASSERT(vm, vm);
837 
838 	system = vm->callback.system;
839 	s = ficlVmGetWord0(vm);
840 
841 	/*
842 	 * Get next word...if out of text, we're done.
843 	 */
844 	if (s.length == 0) {
845 		ficlVmThrow(vm, FICL_VM_STATUS_OUT_OF_TEXT);
846 	}
847 
848 	/*
849 	 * Run the parse chain against the incoming token until somebody
850 	 * eats it. Otherwise emit an error message and give up.
851 	 */
852 	for (i = 0; i < FICL_MAX_PARSE_STEPS; i++) {
853 		ficlWord *word = system->parseList[i];
854 
855 		if (word == NULL)
856 			break;
857 
858 		if (word->code == ficlPrimitiveParseStepParen) {
859 			ficlParseStep pStep;
860 			pStep = (ficlParseStep)(word->param->fn);
861 			if ((*pStep)(vm, s))
862 				return;
863 		} else {
864 			ficlStackPushPointer(vm->dataStack,
865 			    FICL_STRING_GET_POINTER(s));
866 			ficlStackPushUnsigned(vm->dataStack,
867 			    FICL_STRING_GET_LENGTH(s));
868 			ficlVmExecuteXT(vm, word);
869 			if (ficlStackPopInteger(vm->dataStack))
870 				return;
871 		}
872 	}
873 
874 	ficlVmThrowError(vm, "%.*s not found", FICL_STRING_GET_LENGTH(s),
875 	    FICL_STRING_GET_POINTER(s));
876 	/* back to inner interpreter */
877 }
878 
879 /*
880  * Surrogate precompiled parse step for ficlParseWord
881  * (this step is hard coded in FICL_VM_STATE_INTERPRET)
882  */
883 static void
884 ficlPrimitiveLookup(ficlVm *vm)
885 {
886 	ficlString name;
887 	FICL_STRING_SET_LENGTH(name, ficlStackPopUnsigned(vm->dataStack));
888 	FICL_STRING_SET_POINTER(name, ficlStackPopPointer(vm->dataStack));
889 	ficlStackPushInteger(vm->dataStack, ficlVmParseWord(vm, name));
890 }
891 
892 /*
893  * p a r e n P a r s e S t e p
894  * (parse-step)  ( c-addr u -- flag )
895  * runtime for a precompiled parse step - pop a counted string off the
896  * stack, run the parse step against it, and push the result flag (FICL_TRUE
897  * if success, FICL_FALSE otherwise).
898  */
899 void
900 ficlPrimitiveParseStepParen(ficlVm *vm)
901 {
902 	ficlString s;
903 	ficlWord *word = vm->runningWord;
904 	ficlParseStep pStep = (ficlParseStep)(word->param->fn);
905 
906 	FICL_STRING_SET_LENGTH(s, ficlStackPopInteger(vm->dataStack));
907 	FICL_STRING_SET_POINTER(s, ficlStackPopPointer(vm->dataStack));
908 
909 	ficlStackPushInteger(vm->dataStack, (*pStep)(vm, s));
910 }
911 
912 static void
913 ficlPrimitiveAddParseStep(ficlVm *vm)
914 {
915 	ficlWord *pStep;
916 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
917 
918 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
919 
920 	pStep = (ficlWord *)(ficlStackPop(vm->dataStack).p);
921 	if ((pStep != NULL) && ficlDictionaryIsAWord(dictionary, pStep))
922 		ficlSystemAddParseStep(vm->callback.system, pStep);
923 }
924 
925 /*
926  * l i t e r a l I m
927  *
928  * IMMEDIATE code for "literal". This function gets a value from the stack
929  * and compiles it into the dictionary preceded by the code for "(literal)".
930  * IMMEDIATE
931  */
932 void
933 ficlPrimitiveLiteralIm(ficlVm *vm)
934 {
935 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
936 	ficlInteger value;
937 
938 	value = ficlStackPopInteger(vm->dataStack);
939 
940 	switch (value) {
941 	case 1:
942 	case 2:
943 	case 3:
944 	case 4:
945 	case 5:
946 	case 6:
947 	case 7:
948 	case 8:
949 	case 9:
950 	case 10:
951 	case 11:
952 	case 12:
953 	case 13:
954 	case 14:
955 	case 15:
956 	case 16:
957 		ficlDictionaryAppendUnsigned(dictionary, value);
958 		break;
959 
960 	case 0:
961 	case -1:
962 	case -2:
963 	case -3:
964 	case -4:
965 	case -5:
966 	case -6:
967 	case -7:
968 	case -8:
969 	case -9:
970 	case -10:
971 	case -11:
972 	case -12:
973 	case -13:
974 	case -14:
975 	case -15:
976 	case -16:
977 		ficlDictionaryAppendUnsigned(dictionary,
978 		    ficlInstruction0 - value);
979 	break;
980 
981 	default:
982 		ficlDictionaryAppendUnsigned(dictionary,
983 		    ficlInstructionLiteralParen);
984 		ficlDictionaryAppendUnsigned(dictionary, value);
985 	break;
986 	}
987 }
988 
989 static void
990 ficlPrimitive2LiteralIm(ficlVm *vm)
991 {
992 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
993 
994 	ficlDictionaryAppendUnsigned(dictionary, ficlInstruction2LiteralParen);
995 	ficlDictionaryAppendCell(dictionary, ficlStackPop(vm->dataStack));
996 	ficlDictionaryAppendCell(dictionary, ficlStackPop(vm->dataStack));
997 }
998 
999 /*
1000  * D o  /  L o o p
1001  * do -- IMMEDIATE FICL_VM_STATE_COMPILE ONLY
1002  *    Compiles code to initialize a loop: FICL_VM_STATE_COMPILE (do),
1003  *    allot space to hold the "leave" address, push a branch
1004  *    target address for the loop.
1005  * (do) -- runtime for "do"
1006  *    pops index and limit from the p stack and moves them
1007  *    to the r stack, then skips to the loop body.
1008  * loop -- IMMEDIATE FICL_VM_STATE_COMPILE ONLY
1009  * +loop
1010  *    Compiles code for the test part of a loop:
1011  *    FICL_VM_STATE_COMPILE (loop), resolve forward branch from "do", and
1012  *    copy "here" address to the "leave" address allotted by "do"
1013  * i,j,k -- FICL_VM_STATE_COMPILE ONLY
1014  *    Runtime: Push loop indices on param stack (i is innermost loop...)
1015  *    Note: each loop has three values on the return stack:
1016  *    ( R: leave limit index )
1017  *    "leave" is the absolute address of the next ficlCell after the loop
1018  *    limit and index are the loop control variables.
1019  * leave -- FICL_VM_STATE_COMPILE ONLY
1020  *    Runtime: pop the loop control variables, then pop the
1021  *    "leave" address and jump (absolute) there.
1022  */
1023 static void
1024 ficlPrimitiveDoCoIm(ficlVm *vm)
1025 {
1026 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1027 
1028 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionDoParen);
1029 	/*
1030 	 * Allot space for a pointer to the end
1031 	 * of the loop - "leave" uses this...
1032 	 */
1033 	markBranch(dictionary, vm, leaveTag);
1034 	ficlDictionaryAppendUnsigned(dictionary, 0);
1035 	/*
1036 	 * Mark location of head of loop...
1037 	 */
1038 	markBranch(dictionary, vm, doTag);
1039 }
1040 
1041 static void
1042 ficlPrimitiveQDoCoIm(ficlVm *vm)
1043 {
1044 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1045 
1046 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionQDoParen);
1047 	/*
1048 	 * Allot space for a pointer to the end
1049 	 * of the loop - "leave" uses this...
1050 	 */
1051 	markBranch(dictionary, vm, leaveTag);
1052 	ficlDictionaryAppendUnsigned(dictionary, 0);
1053 	/*
1054 	 * Mark location of head of loop...
1055 	 */
1056 	markBranch(dictionary, vm, doTag);
1057 }
1058 
1059 
1060 static void
1061 ficlPrimitiveLoopCoIm(ficlVm *vm)
1062 {
1063 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1064 
1065 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionLoopParen);
1066 	resolveBackBranch(dictionary, vm, doTag);
1067 	resolveAbsBranch(dictionary, vm, leaveTag);
1068 }
1069 
1070 static void
1071 ficlPrimitivePlusLoopCoIm(ficlVm *vm)
1072 {
1073 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1074 
1075 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionPlusLoopParen);
1076 	resolveBackBranch(dictionary, vm, doTag);
1077 	resolveAbsBranch(dictionary, vm, leaveTag);
1078 }
1079 
1080 /*
1081  * v a r i a b l e
1082  */
1083 static void
1084 ficlPrimitiveVariable(ficlVm *vm)
1085 {
1086 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1087 	ficlString name = ficlVmGetWord(vm);
1088 
1089 	ficlDictionaryAppendWord(dictionary, name,
1090 	    (ficlPrimitive)ficlInstructionVariableParen, FICL_WORD_DEFAULT);
1091 	ficlVmDictionaryAllotCells(vm, dictionary, 1);
1092 }
1093 
1094 static void
1095 ficlPrimitive2Variable(ficlVm *vm)
1096 {
1097 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1098 	ficlString name = ficlVmGetWord(vm);
1099 
1100 	ficlDictionaryAppendWord(dictionary, name,
1101 	    (ficlPrimitive)ficlInstructionVariableParen, FICL_WORD_DEFAULT);
1102 	ficlVmDictionaryAllotCells(vm, dictionary, 2);
1103 }
1104 
1105 /*
1106  * b a s e   &   f r i e n d s
1107  */
1108 static void
1109 ficlPrimitiveBase(ficlVm *vm)
1110 {
1111 	ficlCell *pBase, c;
1112 
1113 	FICL_STACK_CHECK(vm->dataStack, 0, 1);
1114 
1115 	pBase = (ficlCell *)(&vm->base);
1116 	c.p = pBase;
1117 	ficlStackPush(vm->dataStack, c);
1118 }
1119 
1120 static void
1121 ficlPrimitiveDecimal(ficlVm *vm)
1122 {
1123 	vm->base = 10;
1124 }
1125 
1126 
1127 static void
1128 ficlPrimitiveHex(ficlVm *vm)
1129 {
1130 	vm->base = 16;
1131 }
1132 
1133 /*
1134  * a l l o t   &   f r i e n d s
1135  */
1136 static void
1137 ficlPrimitiveAllot(ficlVm *vm)
1138 {
1139 	ficlDictionary *dictionary;
1140 	ficlInteger i;
1141 
1142 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
1143 
1144 	dictionary = ficlVmGetDictionary(vm);
1145 	i = ficlStackPopInteger(vm->dataStack);
1146 
1147 	FICL_VM_DICTIONARY_CHECK(vm, dictionary, i);
1148 
1149 	ficlVmDictionaryAllot(vm, dictionary, i);
1150 }
1151 
1152 static void
1153 ficlPrimitiveHere(ficlVm *vm)
1154 {
1155 	ficlDictionary *dictionary;
1156 
1157 	FICL_STACK_CHECK(vm->dataStack, 0, 1);
1158 
1159 	dictionary = ficlVmGetDictionary(vm);
1160 	ficlStackPushPointer(vm->dataStack, dictionary->here);
1161 }
1162 
1163 /*
1164  * t i c k
1165  * tick         CORE ( "<spaces>name" -- xt )
1166  * Skip leading space delimiters. Parse name delimited by a space. Find
1167  * name and return xt, the execution token for name. An ambiguous condition
1168  * exists if name is not found.
1169  */
1170 void
1171 ficlPrimitiveTick(ficlVm *vm)
1172 {
1173 	ficlWord *word = NULL;
1174 	ficlString name = ficlVmGetWord(vm);
1175 
1176 	FICL_STACK_CHECK(vm->dataStack, 0, 1);
1177 
1178 	word = ficlDictionaryLookup(ficlVmGetDictionary(vm), name);
1179 	if (!word)
1180 		ficlVmThrowError(vm, "%.*s not found",
1181 		    FICL_STRING_GET_LENGTH(name),
1182 		    FICL_STRING_GET_POINTER(name));
1183 	ficlStackPushPointer(vm->dataStack, word);
1184 }
1185 
1186 static void
1187 ficlPrimitiveBracketTickCoIm(ficlVm *vm)
1188 {
1189 	ficlPrimitiveTick(vm);
1190 	ficlPrimitiveLiteralIm(vm);
1191 }
1192 
1193 /*
1194  * p o s t p o n e
1195  * Lookup the next word in the input stream and FICL_VM_STATE_COMPILE code to
1196  * insert it into definitions created by the resulting word
1197  * (defers compilation, even of immediate words)
1198  */
1199 static void
1200 ficlPrimitivePostponeCoIm(ficlVm *vm)
1201 {
1202 	ficlDictionary *dictionary  = ficlVmGetDictionary(vm);
1203 	ficlWord *word;
1204 	ficlWord *pComma = ficlSystemLookup(vm->callback.system, ",");
1205 	ficlCell c;
1206 
1207 	FICL_VM_ASSERT(vm, pComma);
1208 
1209 	ficlPrimitiveTick(vm);
1210 	word = ficlStackGetTop(vm->dataStack).p;
1211 	if (ficlWordIsImmediate(word)) {
1212 		ficlDictionaryAppendCell(dictionary,
1213 		    ficlStackPop(vm->dataStack));
1214 	} else {
1215 		ficlPrimitiveLiteralIm(vm);
1216 		c.p = pComma;
1217 		ficlDictionaryAppendCell(dictionary, c);
1218 	}
1219 }
1220 
1221 /*
1222  * e x e c u t e
1223  * Pop an execution token (pointer to a word) off the stack and
1224  * run it
1225  */
1226 static void
1227 ficlPrimitiveExecute(ficlVm *vm)
1228 {
1229 	ficlWord *word;
1230 
1231 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
1232 
1233 	word = ficlStackPopPointer(vm->dataStack);
1234 	ficlVmExecuteWord(vm, word);
1235 }
1236 
1237 /*
1238  * i m m e d i a t e
1239  * Make the most recently compiled word IMMEDIATE -- it executes even
1240  * in FICL_VM_STATE_COMPILE state (most often used for control compiling words
1241  * such as IF, THEN, etc)
1242  */
1243 static void
1244 ficlPrimitiveImmediate(ficlVm *vm)
1245 {
1246 	FICL_IGNORE(vm);
1247 	ficlDictionarySetImmediate(ficlVmGetDictionary(vm));
1248 }
1249 
1250 static void
1251 ficlPrimitiveCompileOnly(ficlVm *vm)
1252 {
1253 	FICL_IGNORE(vm);
1254 	ficlDictionarySetFlags(ficlVmGetDictionary(vm), FICL_WORD_COMPILE_ONLY);
1255 }
1256 
1257 static void
1258 ficlPrimitiveSetObjectFlag(ficlVm *vm)
1259 {
1260 	FICL_IGNORE(vm);
1261 	ficlDictionarySetFlags(ficlVmGetDictionary(vm), FICL_WORD_OBJECT);
1262 }
1263 
1264 static void
1265 ficlPrimitiveIsObject(ficlVm *vm)
1266 {
1267 	ficlInteger flag;
1268 	ficlWord *word = (ficlWord *)ficlStackPopPointer(vm->dataStack);
1269 
1270 	flag = ((word != NULL) && (word->flags & FICL_WORD_OBJECT))?
1271 	    FICL_TRUE : FICL_FALSE;
1272 
1273 	ficlStackPushInteger(vm->dataStack, flag);
1274 }
1275 
1276 static void
1277 ficlPrimitiveCountedStringQuoteIm(ficlVm *vm)
1278 {
1279 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1280 
1281 	if (vm->state == FICL_VM_STATE_INTERPRET) {
1282 		ficlCountedString *counted = (ficlCountedString *)
1283 		    dictionary->here;
1284 
1285 		ficlVmGetString(vm, counted, '\"');
1286 		ficlStackPushPointer(vm->dataStack, counted);
1287 
1288 		/*
1289 		 * move HERE past string so it doesn't get overwritten.  --lch
1290 		 */
1291 		ficlVmDictionaryAllot(vm, dictionary,
1292 		    counted->length + sizeof (ficlUnsigned8));
1293 	} else {	/* FICL_VM_STATE_COMPILE state */
1294 		ficlDictionaryAppendUnsigned(dictionary,
1295 		    ficlInstructionCStringLiteralParen);
1296 		dictionary->here =
1297 		    FICL_POINTER_TO_CELL(ficlVmGetString(vm,
1298 		    (ficlCountedString *)dictionary->here, '\"'));
1299 		ficlDictionaryAlign(dictionary);
1300 	}
1301 }
1302 
1303 /*
1304  * d o t Q u o t e
1305  * IMMEDIATE word that compiles a string literal for later display
1306  * FICL_VM_STATE_COMPILE fiStringLiteralParen, then copy the bytes of the
1307  * string from the
1308  * TIB to the dictionary. Backpatch the count byte and align the dictionary.
1309  */
1310 static void
1311 ficlPrimitiveDotQuoteCoIm(ficlVm *vm)
1312 {
1313 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1314 	ficlWord *pType = ficlSystemLookup(vm->callback.system, "type");
1315 	ficlCell c;
1316 
1317 	FICL_VM_ASSERT(vm, pType);
1318 
1319 	ficlDictionaryAppendUnsigned(dictionary,
1320 	    ficlInstructionStringLiteralParen);
1321 	dictionary->here =
1322 	    FICL_POINTER_TO_CELL(ficlVmGetString(vm,
1323 	    (ficlCountedString *)dictionary->here, '\"'));
1324 	ficlDictionaryAlign(dictionary);
1325 	c.p = pType;
1326 	ficlDictionaryAppendCell(dictionary, c);
1327 }
1328 
1329 static void
1330 ficlPrimitiveDotParen(ficlVm *vm)
1331 {
1332 	char *from = ficlVmGetInBuf(vm);
1333 	char *stop = ficlVmGetInBufEnd(vm);
1334 	char *to = vm->pad;
1335 	char c;
1336 
1337 	/*
1338 	 * Note: the standard does not want leading spaces skipped.
1339 	 */
1340 	for (c = *from; (from != stop) && (c != ')'); c = *++from)
1341 		*to++ = c;
1342 
1343 	*to = '\0';
1344 	if ((from != stop) && (c == ')'))
1345 		from++;
1346 
1347 	ficlVmTextOut(vm, vm->pad);
1348 	ficlVmUpdateTib(vm, from);
1349 }
1350 
1351 /*
1352  * s l i t e r a l
1353  * STRING
1354  * Interpretation: Interpretation semantics for this word are undefined.
1355  * Compilation: ( c-addr1 u -- )
1356  * Append the run-time semantics given below to the current definition.
1357  * Run-time:       ( -- c-addr2 u )
1358  * Return c-addr2 u describing a string consisting of the characters
1359  * specified by c-addr1 u during compilation. A program shall not alter
1360  * the returned string.
1361  */
1362 static void ficlPrimitiveSLiteralCoIm(ficlVm *vm)
1363 {
1364 	ficlDictionary *dictionary;
1365 	char *from;
1366 	char *to;
1367 	ficlUnsigned length;
1368 
1369 	FICL_STACK_CHECK(vm->dataStack, 2, 0);
1370 
1371 	dictionary = ficlVmGetDictionary(vm);
1372 	length  = ficlStackPopUnsigned(vm->dataStack);
1373 	from = ficlStackPopPointer(vm->dataStack);
1374 
1375 	ficlDictionaryAppendUnsigned(dictionary,
1376 	    ficlInstructionStringLiteralParen);
1377 	to = (char *)dictionary->here;
1378 	*to++ = (char)length;
1379 
1380 	for (; length > 0; --length) {
1381 		*to++ = *from++;
1382 	}
1383 
1384 	*to++ = 0;
1385 	dictionary->here = FICL_POINTER_TO_CELL(ficlAlignPointer(to));
1386 }
1387 
1388 /*
1389  * s t a t e
1390  * Return the address of the VM's state member (must be sized the
1391  * same as a ficlCell for this reason)
1392  */
1393 static void ficlPrimitiveState(ficlVm *vm)
1394 {
1395 	FICL_STACK_CHECK(vm->dataStack, 0, 1);
1396 	ficlStackPushPointer(vm->dataStack, &vm->state);
1397 }
1398 
1399 /*
1400  * c r e a t e . . . d o e s >
1401  * Make a new word in the dictionary with the run-time effect of
1402  * a variable (push my address), but with extra space allotted
1403  * for use by does> .
1404  */
1405 static void
1406 ficlPrimitiveCreate(ficlVm *vm)
1407 {
1408 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1409 	ficlString name = ficlVmGetWord(vm);
1410 
1411 	ficlDictionaryAppendWord(dictionary, name,
1412 	    (ficlPrimitive)ficlInstructionCreateParen, FICL_WORD_DEFAULT);
1413 	ficlVmDictionaryAllotCells(vm, dictionary, 1);
1414 }
1415 
1416 static void
1417 ficlPrimitiveDoesCoIm(ficlVm *vm)
1418 {
1419 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1420 #if FICL_WANT_LOCALS
1421 	if (vm->callback.system->localsCount > 0) {
1422 		ficlDictionary *locals =
1423 		    ficlSystemGetLocals(vm->callback.system);
1424 		ficlDictionaryEmpty(locals, locals->forthWordlist->size);
1425 		ficlDictionaryAppendUnsigned(dictionary,
1426 		    ficlInstructionUnlinkParen);
1427 	}
1428 
1429 	vm->callback.system->localsCount = 0;
1430 #endif
1431 	FICL_IGNORE(vm);
1432 
1433 	ficlDictionaryAppendUnsigned(dictionary, ficlInstructionDoesParen);
1434 }
1435 
1436 /*
1437  * t o   b o d y
1438  * to-body	CORE ( xt -- a-addr )
1439  * a-addr is the data-field address corresponding to xt. An ambiguous
1440  * condition exists if xt is not for a word defined via CREATE.
1441  */
1442 static void
1443 ficlPrimitiveToBody(ficlVm *vm)
1444 {
1445 	ficlWord *word;
1446 	FICL_STACK_CHECK(vm->dataStack, 1, 1);
1447 
1448 	word = ficlStackPopPointer(vm->dataStack);
1449 	ficlStackPushPointer(vm->dataStack, word->param + 1);
1450 }
1451 
1452 /*
1453  * from-body	Ficl ( a-addr -- xt )
1454  * Reverse effect of >body
1455  */
1456 static void
1457 ficlPrimitiveFromBody(ficlVm *vm)
1458 {
1459 	char *ptr;
1460 	FICL_STACK_CHECK(vm->dataStack, 1, 1);
1461 
1462 	ptr = (char *)ficlStackPopPointer(vm->dataStack) - sizeof (ficlWord);
1463 	ficlStackPushPointer(vm->dataStack, ptr);
1464 }
1465 
1466 /*
1467  * >name	Ficl ( xt -- c-addr u )
1468  * Push the address and length of a word's name given its address
1469  * xt.
1470  */
1471 static void
1472 ficlPrimitiveToName(ficlVm *vm)
1473 {
1474 	ficlWord *word;
1475 
1476 	FICL_STACK_CHECK(vm->dataStack, 1, 2);
1477 
1478 	word = ficlStackPopPointer(vm->dataStack);
1479 	ficlStackPushPointer(vm->dataStack, word->name);
1480 	ficlStackPushUnsigned(vm->dataStack, word->length);
1481 }
1482 
1483 static void
1484 ficlPrimitiveLastWord(ficlVm *vm)
1485 {
1486 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1487 	ficlWord *wp = dictionary->smudge;
1488 	ficlCell c;
1489 
1490 	FICL_VM_ASSERT(vm, wp);
1491 
1492 	c.p = wp;
1493 	ficlVmPush(vm, c);
1494 }
1495 
1496 /*
1497  * l b r a c k e t   e t c
1498  */
1499 static void
1500 ficlPrimitiveLeftBracketCoIm(ficlVm *vm)
1501 {
1502 	vm->state = FICL_VM_STATE_INTERPRET;
1503 }
1504 
1505 static void
1506 ficlPrimitiveRightBracket(ficlVm *vm)
1507 {
1508 	vm->state = FICL_VM_STATE_COMPILE;
1509 }
1510 
1511 /*
1512  * p i c t u r e d   n u m e r i c   w o r d s
1513  *
1514  * less-number-sign CORE ( -- )
1515  * Initialize the pictured numeric output conversion process.
1516  * (clear the pad)
1517  */
1518 static void
1519 ficlPrimitiveLessNumberSign(ficlVm *vm)
1520 {
1521 	ficlCountedString *counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad);
1522 	counted->length = 0;
1523 }
1524 
1525 /*
1526  * number-sign		CORE ( ud1 -- ud2 )
1527  * Divide ud1 by the number in BASE giving the quotient ud2 and the remainder
1528  * n. (n is the least-significant digit of ud1.) Convert n to external form
1529  * and add the resulting character to the beginning of the pictured numeric
1530  * output  string. An ambiguous condition exists if # executes outside of a
1531  * <# #> delimited number conversion.
1532  */
1533 static void
1534 ficlPrimitiveNumberSign(ficlVm *vm)
1535 {
1536 	ficlCountedString *counted;
1537 	ficl2Unsigned u;
1538 	ficl2UnsignedQR uqr;
1539 
1540 	FICL_STACK_CHECK(vm->dataStack, 2, 2);
1541 
1542 	counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad);
1543 	u = ficlStackPop2Unsigned(vm->dataStack);
1544 	uqr = ficl2UnsignedDivide(u, (ficlUnsigned16)(vm->base));
1545 	counted->text[counted->length++] = ficlDigitToCharacter(uqr.remainder);
1546 	ficlStackPush2Unsigned(vm->dataStack, uqr.quotient);
1547 }
1548 
1549 /*
1550  * number-sign-greater CORE ( xd -- c-addr u )
1551  * Drop xd. Make the pictured numeric output string available as a character
1552  * string. c-addr and u specify the resulting character string. A program
1553  * may replace characters within the string.
1554  */
1555 static void
1556 ficlPrimitiveNumberSignGreater(ficlVm *vm)
1557 {
1558 	ficlCountedString *counted;
1559 
1560 	FICL_STACK_CHECK(vm->dataStack, 2, 2);
1561 
1562 	counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad);
1563 	counted->text[counted->length] = 0;
1564 	ficlStringReverse(counted->text);
1565 	ficlStackDrop(vm->dataStack, 2);
1566 	ficlStackPushPointer(vm->dataStack, counted->text);
1567 	ficlStackPushUnsigned(vm->dataStack, counted->length);
1568 }
1569 
1570 /*
1571  * number-sign-s	CORE ( ud1 -- ud2 )
1572  * Convert one digit of ud1 according to the rule for #. Continue conversion
1573  * until the quotient is zero. ud2 is zero. An ambiguous condition exists if
1574  * #S executes outside of a <# #> delimited number conversion.
1575  * TO DO: presently does not use ud1 hi ficlCell - use it!
1576  */
1577 static void
1578 ficlPrimitiveNumberSignS(ficlVm *vm)
1579 {
1580 	ficlCountedString *counted;
1581 	ficl2Unsigned u;
1582 	ficl2UnsignedQR uqr;
1583 
1584 	FICL_STACK_CHECK(vm->dataStack, 2, 2);
1585 
1586 	counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad);
1587 	u = ficlStackPop2Unsigned(vm->dataStack);
1588 
1589 	do {
1590 		uqr = ficl2UnsignedDivide(u, (ficlUnsigned16)(vm->base));
1591 		counted->text[counted->length++] =
1592 		    ficlDigitToCharacter(uqr.remainder);
1593 		u = uqr.quotient;
1594 	} while (FICL_2UNSIGNED_NOT_ZERO(u));
1595 
1596 	ficlStackPush2Unsigned(vm->dataStack, u);
1597 }
1598 
1599 /*
1600  * HOLD		CORE ( char -- )
1601  * Add char to the beginning of the pictured numeric output string.
1602  * An ambiguous condition exists if HOLD executes outside of a <# #>
1603  * delimited number conversion.
1604  */
1605 static void
1606 ficlPrimitiveHold(ficlVm *vm)
1607 {
1608 	ficlCountedString *counted;
1609 	int i;
1610 
1611 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
1612 
1613 	counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad);
1614 	i = ficlStackPopInteger(vm->dataStack);
1615 	counted->text[counted->length++] = (char)i;
1616 }
1617 
1618 /*
1619  * SIGN		CORE ( n -- )
1620  * If n is negative, add a minus sign to the beginning of the pictured
1621  * numeric output string. An ambiguous condition exists if SIGN
1622  * executes outside of a <# #> delimited number conversion.
1623  */
1624 static void
1625 ficlPrimitiveSign(ficlVm *vm)
1626 {
1627 	ficlCountedString *counted;
1628 	int i;
1629 
1630 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
1631 
1632 	counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad);
1633 	i = ficlStackPopInteger(vm->dataStack);
1634 	if (i < 0)
1635 		counted->text[counted->length++] = '-';
1636 }
1637 
1638 /*
1639  * t o   N u m b e r
1640  * to-number CORE ( ud1 c-addr1 u1 -- ud2 c-addr2 u2 )
1641  * ud2 is the unsigned result of converting the characters within the
1642  * string specified by c-addr1 u1 into digits, using the number in BASE,
1643  * and adding each into ud1 after multiplying ud1 by the number in BASE.
1644  * Conversion continues left-to-right until a character that is not
1645  * convertible, including any + or -, is encountered or the string is
1646  * entirely converted. c-addr2 is the location of the first unconverted
1647  * character or the first character past the end of the string if the string
1648  * was entirely converted. u2 is the number of unconverted characters in the
1649  * string. An ambiguous condition exists if ud2 overflows during the
1650  * conversion.
1651  */
1652 static void
1653 ficlPrimitiveToNumber(ficlVm *vm)
1654 {
1655 	ficlUnsigned length;
1656 	char *trace;
1657 	ficl2Unsigned accumulator;
1658 	ficlUnsigned base = vm->base;
1659 	ficlUnsigned c;
1660 	ficlUnsigned digit;
1661 
1662 	FICL_STACK_CHECK(vm->dataStack, 4, 4);
1663 
1664 	length = ficlStackPopUnsigned(vm->dataStack);
1665 	trace = (char *)ficlStackPopPointer(vm->dataStack);
1666 	accumulator = ficlStackPop2Unsigned(vm->dataStack);
1667 
1668 	for (c = *trace; length > 0; c = *++trace, length--) {
1669 		if (c < '0')
1670 			break;
1671 
1672 		digit = c - '0';
1673 
1674 		if (digit > 9)
1675 			digit = tolower(c) - 'a' + 10;
1676 		/*
1677 		 * Note: following test also catches chars between 9 and a
1678 		 * because 'digit' is unsigned!
1679 		 */
1680 		if (digit >= base)
1681 			break;
1682 
1683 		accumulator = ficl2UnsignedMultiplyAccumulate(accumulator,
1684 		    base, digit);
1685 	}
1686 
1687 	ficlStackPush2Unsigned(vm->dataStack, accumulator);
1688 	ficlStackPushPointer(vm->dataStack, trace);
1689 	ficlStackPushUnsigned(vm->dataStack, length);
1690 }
1691 
1692 /*
1693  * q u i t   &   a b o r t
1694  * quit CORE	( -- )  ( R:  i*x -- )
1695  * Empty the return stack, store zero in SOURCE-ID if it is present, make
1696  * the user input device the input source, and enter interpretation state.
1697  * Do not display a message. Repeat the following:
1698  *
1699  *   Accept a line from the input source into the input buffer, set >IN to
1700  *   zero, and FICL_VM_STATE_INTERPRET.
1701  *   Display the implementation-defined system prompt if in
1702  *   interpretation state, all processing has been completed, and no
1703  *   ambiguous condition exists.
1704  */
1705 static void
1706 ficlPrimitiveQuit(ficlVm *vm)
1707 {
1708 	ficlVmThrow(vm, FICL_VM_STATUS_QUIT);
1709 }
1710 
1711 static void
1712 ficlPrimitiveAbort(ficlVm *vm)
1713 {
1714 	ficlVmThrow(vm, FICL_VM_STATUS_ABORT);
1715 }
1716 
1717 /*
1718  * a c c e p t
1719  * accept	CORE ( c-addr +n1 -- +n2 )
1720  * Receive a string of at most +n1 characters. An ambiguous condition
1721  * exists if +n1 is zero or greater than 32,767. Display graphic characters
1722  * as they are received. A program that depends on the presence or absence
1723  * of non-graphic characters in the string has an environmental dependency.
1724  * The editing functions, if any, that the system performs in order to
1725  * construct the string are implementation-defined.
1726  *
1727  * (Although the standard text doesn't say so, I assume that the intent
1728  * of 'accept' is to store the string at the address specified on
1729  * the stack.)
1730  *
1731  * NOTE: getchar() is used there as its present both in loader and
1732  *	userland; however, the more correct solution would be to set
1733  *	terminal to raw mode for userland.
1734  */
1735 static void
1736 ficlPrimitiveAccept(ficlVm *vm)
1737 {
1738 	ficlUnsigned size;
1739 	char *address;
1740 	int c;
1741 	ficlUnsigned length = 0;
1742 
1743 	FICL_STACK_CHECK(vm->dataStack, 2, 1);
1744 
1745 	size = ficlStackPopInteger(vm->dataStack);
1746 	address = ficlStackPopPointer(vm->dataStack);
1747 
1748 	while (size != length) {
1749 		c = getchar();
1750 		if (c == '\n' || c == '\r')
1751 			break;
1752 		address[length++] = c;
1753 	}
1754 	ficlStackPushInteger(vm->dataStack, length);
1755 }
1756 
1757 /*
1758  * a l i g n
1759  * 6.1.0705 ALIGN	CORE ( -- )
1760  * If the data-space pointer is not aligned, reserve enough space to
1761  * align it.
1762  */
1763 static void
1764 ficlPrimitiveAlign(ficlVm *vm)
1765 {
1766 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1767 	FICL_IGNORE(vm);
1768 	ficlDictionaryAlign(dictionary);
1769 }
1770 
1771 /*
1772  * a l i g n e d
1773  */
1774 static void
1775 ficlPrimitiveAligned(ficlVm *vm)
1776 {
1777 	void *addr;
1778 
1779 	FICL_STACK_CHECK(vm->dataStack, 1, 1);
1780 
1781 	addr = ficlStackPopPointer(vm->dataStack);
1782 	ficlStackPushPointer(vm->dataStack, ficlAlignPointer(addr));
1783 }
1784 
1785 /*
1786  * b e g i n   &   f r i e n d s
1787  * Indefinite loop control structures
1788  * A.6.1.0760 BEGIN
1789  * Typical use:
1790  *	: X ... BEGIN ... test UNTIL ;
1791  * or
1792  *	: X ... BEGIN ... test WHILE ... REPEAT ;
1793  */
1794 static void
1795 ficlPrimitiveBeginCoIm(ficlVm *vm)
1796 {
1797 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1798 	markBranch(dictionary, vm, destTag);
1799 }
1800 
1801 static void
1802 ficlPrimitiveUntilCoIm(ficlVm *vm)
1803 {
1804 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1805 
1806 	ficlDictionaryAppendUnsigned(dictionary,
1807 	    ficlInstructionBranch0ParenWithCheck);
1808 	resolveBackBranch(dictionary, vm, destTag);
1809 }
1810 
1811 static void
1812 ficlPrimitiveWhileCoIm(ficlVm *vm)
1813 {
1814 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1815 
1816 	FICL_STACK_CHECK(vm->dataStack, 2, 5);
1817 
1818 	ficlDictionaryAppendUnsigned(dictionary,
1819 	    ficlInstructionBranch0ParenWithCheck);
1820 	markBranch(dictionary, vm, origTag);
1821 
1822 	/* equivalent to 2swap */
1823 	ficlStackRoll(vm->dataStack, 3);
1824 	ficlStackRoll(vm->dataStack, 3);
1825 
1826 	ficlDictionaryAppendUnsigned(dictionary, 1);
1827 }
1828 
1829 static void
1830 ficlPrimitiveRepeatCoIm(ficlVm *vm)
1831 {
1832 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1833 
1834 	ficlDictionaryAppendUnsigned(dictionary,
1835 	    ficlInstructionBranchParenWithCheck);
1836 	/* expect "begin" branch marker */
1837 	resolveBackBranch(dictionary, vm, destTag);
1838 	/* expect "while" branch marker */
1839 	resolveForwardBranch(dictionary, vm, origTag);
1840 }
1841 
1842 static void
1843 ficlPrimitiveAgainCoIm(ficlVm *vm)
1844 {
1845 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1846 
1847 	ficlDictionaryAppendUnsigned(dictionary,
1848 	    ficlInstructionBranchParenWithCheck);
1849 	/* expect "begin" branch marker */
1850 	resolveBackBranch(dictionary, vm, destTag);
1851 }
1852 
1853 /*
1854  * c h a r   &   f r i e n d s
1855  * 6.1.0895 CHAR	CORE ( "<spaces>name" -- char )
1856  * Skip leading space delimiters. Parse name delimited by a space.
1857  * Put the value of its first character onto the stack.
1858  *
1859  * bracket-char		CORE
1860  * Interpretation: Interpretation semantics for this word are undefined.
1861  * Compilation: ( "<spaces>name" -- )
1862  * Skip leading space delimiters. Parse name delimited by a space.
1863  * Append the run-time semantics given below to the current definition.
1864  * Run-time: ( -- char )
1865  * Place char, the value of the first character of name, on the stack.
1866  */
1867 static void
1868 ficlPrimitiveChar(ficlVm *vm)
1869 {
1870 	ficlString s;
1871 
1872 	FICL_STACK_CHECK(vm->dataStack, 0, 1);
1873 
1874 	s = ficlVmGetWord(vm);
1875 	ficlStackPushUnsigned(vm->dataStack, (ficlUnsigned)(s.text[0]));
1876 }
1877 
1878 static void
1879 ficlPrimitiveCharCoIm(ficlVm *vm)
1880 {
1881 	ficlPrimitiveChar(vm);
1882 	ficlPrimitiveLiteralIm(vm);
1883 }
1884 
1885 /*
1886  * c h a r P l u s
1887  * char-plus	CORE ( c-addr1 -- c-addr2 )
1888  * Add the size in address units of a character to c-addr1, giving c-addr2.
1889  */
1890 static void
1891 ficlPrimitiveCharPlus(ficlVm *vm)
1892 {
1893 	char *p;
1894 
1895 	FICL_STACK_CHECK(vm->dataStack, 1, 1);
1896 
1897 	p = ficlStackPopPointer(vm->dataStack);
1898 	ficlStackPushPointer(vm->dataStack, p + 1);
1899 }
1900 
1901 /*
1902  * c h a r s
1903  * chars	CORE ( n1 -- n2 )
1904  * n2 is the size in address units of n1 characters.
1905  * For most processors, this function can be a no-op. To guarantee
1906  * portability, we'll multiply by sizeof (char).
1907  */
1908 #if defined(_M_IX86)
1909 #pragma warning(disable: 4127)
1910 #endif
1911 static void
1912 ficlPrimitiveChars(ficlVm *vm)
1913 {
1914 	if (sizeof (char) > 1) {
1915 		ficlInteger i;
1916 
1917 		FICL_STACK_CHECK(vm->dataStack, 1, 1);
1918 
1919 		i = ficlStackPopInteger(vm->dataStack);
1920 		ficlStackPushInteger(vm->dataStack, i * sizeof (char));
1921 	}
1922 	/* otherwise no-op! */
1923 }
1924 #if defined(_M_IX86)
1925 #pragma warning(default: 4127)
1926 #endif
1927 
1928 /*
1929  * c o u n t
1930  * COUNT	CORE ( c-addr1 -- c-addr2 u )
1931  * Return the character string specification for the counted string stored
1932  * at c-addr1. c-addr2 is the address of the first character after c-addr1.
1933  * u is the contents of the character at c-addr1, which is the length in
1934  * characters of the string at c-addr2.
1935  */
1936 static void
1937 ficlPrimitiveCount(ficlVm *vm)
1938 {
1939 	ficlCountedString *counted;
1940 
1941 	FICL_STACK_CHECK(vm->dataStack, 1, 2);
1942 
1943 	counted = ficlStackPopPointer(vm->dataStack);
1944 	ficlStackPushPointer(vm->dataStack, counted->text);
1945 	ficlStackPushUnsigned(vm->dataStack, counted->length);
1946 }
1947 
1948 /*
1949  * e n v i r o n m e n t ?
1950  * environment-query CORE ( c-addr u -- FICL_FALSE | i*x FICL_TRUE )
1951  * c-addr is the address of a character string and u is the string's
1952  * character count. u may have a value in the range from zero to an
1953  * implementation-defined maximum which shall not be less than 31. The
1954  * character string should contain a keyword from 3.2.6 Environmental
1955  * queries or the optional word sets to be checked for correspondence
1956  * with an attribute of the present environment. If the system treats the
1957  * attribute as unknown, the returned flag is FICL_FALSE; otherwise, the flag
1958  * is FICL_TRUE and the i*x returned is of the type specified in the table for
1959  * the attribute queried.
1960  */
1961 static void
1962 ficlPrimitiveEnvironmentQ(ficlVm *vm)
1963 {
1964 	ficlDictionary *environment;
1965 	ficlWord *word;
1966 	ficlString name;
1967 
1968 	FICL_STACK_CHECK(vm->dataStack, 2, 1);
1969 
1970 	environment = vm->callback.system->environment;
1971 	name.length = ficlStackPopUnsigned(vm->dataStack);
1972 	name.text = ficlStackPopPointer(vm->dataStack);
1973 
1974 	word = ficlDictionaryLookup(environment, name);
1975 
1976 	if (word != NULL) {
1977 		ficlVmExecuteWord(vm, word);
1978 		ficlStackPushInteger(vm->dataStack, FICL_TRUE);
1979 	} else {
1980 		ficlStackPushInteger(vm->dataStack, FICL_FALSE);
1981 	}
1982 }
1983 
1984 /*
1985  * e v a l u a t e
1986  * EVALUATE CORE ( i*x c-addr u -- j*x )
1987  * Save the current input source specification. Store minus-one (-1) in
1988  * SOURCE-ID if it is present. Make the string described by c-addr and u
1989  * both the input source and input buffer, set >IN to zero, and
1990  * FICL_VM_STATE_INTERPRET.
1991  * When the parse area is empty, restore the prior input source
1992  * specification. Other stack effects are due to the words EVALUATEd.
1993  */
1994 static void
1995 ficlPrimitiveEvaluate(ficlVm *vm)
1996 {
1997 	ficlCell id;
1998 	int result;
1999 	ficlString string;
2000 
2001 	FICL_STACK_CHECK(vm->dataStack, 2, 0);
2002 
2003 	FICL_STRING_SET_LENGTH(string, ficlStackPopUnsigned(vm->dataStack));
2004 	FICL_STRING_SET_POINTER(string, ficlStackPopPointer(vm->dataStack));
2005 
2006 	id = vm->sourceId;
2007 	vm->sourceId.i = -1;
2008 	result = ficlVmExecuteString(vm, string);
2009 	vm->sourceId = id;
2010 	if (result != FICL_VM_STATUS_OUT_OF_TEXT)
2011 		ficlVmThrow(vm, result);
2012 }
2013 
2014 /*
2015  * s t r i n g   q u o t e
2016  * Interpreting: get string delimited by a quote from the input stream,
2017  * copy to a scratch area, and put its count and address on the stack.
2018  * Compiling: FICL_VM_STATE_COMPILE code to push the address and count
2019  * of a string literal, FICL_VM_STATE_COMPILE the string from the input
2020  * stream, and align the dictionary pointer.
2021  */
2022 static void
2023 ficlPrimitiveStringQuoteIm(ficlVm *vm)
2024 {
2025 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
2026 
2027 	if (vm->state == FICL_VM_STATE_INTERPRET) {
2028 		ficlCountedString *counted;
2029 		counted = (ficlCountedString *)dictionary->here;
2030 		ficlVmGetString(vm, counted, '\"');
2031 		ficlStackPushPointer(vm->dataStack, counted->text);
2032 		ficlStackPushUnsigned(vm->dataStack, counted->length);
2033 	} else {	/* FICL_VM_STATE_COMPILE state */
2034 		ficlDictionaryAppendUnsigned(dictionary,
2035 		    ficlInstructionStringLiteralParen);
2036 		dictionary->here = FICL_POINTER_TO_CELL(
2037 		    ficlVmGetString(vm, (ficlCountedString *)dictionary->here,
2038 		    '\"'));
2039 		ficlDictionaryAlign(dictionary);
2040 	}
2041 }
2042 
2043 /*
2044  * t y p e
2045  * Pop count and char address from stack and print the designated string.
2046  */
2047 static void
2048 ficlPrimitiveType(ficlVm *vm)
2049 {
2050 	ficlUnsigned length;
2051 	char *s;
2052 
2053 	FICL_STACK_CHECK(vm->dataStack, 2, 0);
2054 
2055 	length = ficlStackPopUnsigned(vm->dataStack);
2056 	s = ficlStackPopPointer(vm->dataStack);
2057 
2058 	if ((s == NULL) || (length == 0))
2059 		return;
2060 
2061 	/*
2062 	 * Since we don't have an output primitive for a counted string
2063 	 * (oops), make sure the string is null terminated. If not, copy
2064 	 * and terminate it.
2065 	 */
2066 	if (s[length] != 0) {
2067 		char *here = (char *)ficlVmGetDictionary(vm)->here;
2068 		if (s != here)
2069 			strncpy(here, s, length);
2070 
2071 		here[length] = '\0';
2072 		s = here;
2073 	}
2074 
2075 	ficlVmTextOut(vm, s);
2076 }
2077 
2078 /*
2079  * w o r d
2080  * word CORE ( char "<chars>ccc<char>" -- c-addr )
2081  * Skip leading delimiters. Parse characters ccc delimited by char. An
2082  * ambiguous condition exists if the length of the parsed string is greater
2083  * than the implementation-defined length of a counted string.
2084  *
2085  * c-addr is the address of a transient region containing the parsed word
2086  * as a counted string. If the parse area was empty or contained no
2087  * characters other than the delimiter, the resulting string has a zero
2088  * length. A space, not included in the length, follows the string. A
2089  * program may replace characters within the string.
2090  * NOTE! Ficl also NULL-terminates the dest string.
2091  */
2092 static void
2093 ficlPrimitiveWord(ficlVm *vm)
2094 {
2095 	ficlCountedString *counted;
2096 	char delim;
2097 	ficlString name;
2098 
2099 	FICL_STACK_CHECK(vm->dataStack, 1, 1);
2100 
2101 	counted = (ficlCountedString *)vm->pad;
2102 	delim = (char)ficlStackPopInteger(vm->dataStack);
2103 	name = ficlVmParseStringEx(vm, delim, 1);
2104 
2105 	if (FICL_STRING_GET_LENGTH(name) > FICL_PAD_SIZE - 1)
2106 		FICL_STRING_SET_LENGTH(name, FICL_PAD_SIZE - 1);
2107 
2108 	counted->length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(name);
2109 	strncpy(counted->text, FICL_STRING_GET_POINTER(name),
2110 	    FICL_STRING_GET_LENGTH(name));
2111 
2112 	/*
2113 	 * store an extra space at the end of the primitive...
2114 	 * why? dunno yet.  Guy Carver did it.
2115 	 */
2116 	counted->text[counted->length] = ' ';
2117 	counted->text[counted->length + 1] = 0;
2118 
2119 	ficlStackPushPointer(vm->dataStack, counted);
2120 }
2121 
2122 /*
2123  * p a r s e - w o r d
2124  * Ficl   PARSE-WORD  ( <spaces>name -- c-addr u )
2125  * Skip leading spaces and parse name delimited by a space. c-addr is the
2126  * address within the input buffer and u is the length of the selected
2127  * string. If the parse area is empty, the resulting string has a zero length.
2128  */
2129 static void ficlPrimitiveParseNoCopy(ficlVm *vm)
2130 {
2131 	ficlString s;
2132 
2133 	FICL_STACK_CHECK(vm->dataStack, 0, 2);
2134 
2135 	s = ficlVmGetWord0(vm);
2136 	ficlStackPushPointer(vm->dataStack, FICL_STRING_GET_POINTER(s));
2137 	ficlStackPushUnsigned(vm->dataStack, FICL_STRING_GET_LENGTH(s));
2138 }
2139 
2140 /*
2141  * p a r s e
2142  * CORE EXT  ( char "ccc<char>" -- c-addr u )
2143  * Parse ccc delimited by the delimiter char.
2144  * c-addr is the address (within the input buffer) and u is the length of
2145  * the parsed string. If the parse area was empty, the resulting string has
2146  * a zero length.
2147  * NOTE! PARSE differs from WORD: it does not skip leading delimiters.
2148  */
2149 static void
2150 ficlPrimitiveParse(ficlVm *vm)
2151 {
2152 	ficlString s;
2153 	char delim;
2154 
2155 	FICL_STACK_CHECK(vm->dataStack, 1, 2);
2156 
2157 	delim = (char)ficlStackPopInteger(vm->dataStack);
2158 
2159 	s = ficlVmParseStringEx(vm, delim, 0);
2160 	ficlStackPushPointer(vm->dataStack, FICL_STRING_GET_POINTER(s));
2161 	ficlStackPushUnsigned(vm->dataStack, FICL_STRING_GET_LENGTH(s));
2162 }
2163 
2164 /*
2165  * f i n d
2166  * FIND CORE ( c-addr -- c-addr 0  |  xt 1  |  xt -1 )
2167  * Find the definition named in the counted string at c-addr. If the
2168  * definition is not found, return c-addr and zero. If the definition is
2169  * found, return its execution token xt. If the definition is immediate,
2170  * also return one (1), otherwise also return minus-one (-1). For a given
2171  * string, the values returned by FIND while compiling may differ from
2172  * those returned while not compiling.
2173  */
2174 static void
2175 do_find(ficlVm *vm, ficlString name, void *returnForFailure)
2176 {
2177 	ficlWord *word;
2178 
2179 	word = ficlDictionaryLookup(ficlVmGetDictionary(vm), name);
2180 	if (word) {
2181 		ficlStackPushPointer(vm->dataStack, word);
2182 		ficlStackPushInteger(vm->dataStack,
2183 		    (ficlWordIsImmediate(word) ? 1 : -1));
2184 	} else {
2185 		ficlStackPushPointer(vm->dataStack, returnForFailure);
2186 		ficlStackPushUnsigned(vm->dataStack, 0);
2187 	}
2188 }
2189 
2190 /*
2191  * f i n d
2192  * FIND CORE ( c-addr -- c-addr 0  |  xt 1  |  xt -1 )
2193  * Find the definition named in the counted string at c-addr. If the
2194  * definition is not found, return c-addr and zero. If the definition is
2195  * found, return its execution token xt. If the definition is immediate,
2196  * also return one (1), otherwise also return minus-one (-1). For a given
2197  * string, the values returned by FIND while compiling may differ from
2198  * those returned while not compiling.
2199  */
2200 static void
2201 ficlPrimitiveCFind(ficlVm *vm)
2202 {
2203 	ficlCountedString *counted;
2204 	ficlString name;
2205 
2206 	FICL_STACK_CHECK(vm->dataStack, 1, 2);
2207 
2208 	counted = ficlStackPopPointer(vm->dataStack);
2209 	FICL_STRING_SET_FROM_COUNTED_STRING(name, *counted);
2210 	do_find(vm, name, counted);
2211 }
2212 
2213 /*
2214  * s f i n d
2215  * Ficl   ( c-addr u -- 0 0  |  xt 1  |  xt -1 )
2216  * Like FIND, but takes "c-addr u" for the string.
2217  */
2218 static void
2219 ficlPrimitiveSFind(ficlVm *vm)
2220 {
2221 	ficlString name;
2222 
2223 	FICL_STACK_CHECK(vm->dataStack, 2, 2);
2224 
2225 	name.length = ficlStackPopInteger(vm->dataStack);
2226 	name.text = ficlStackPopPointer(vm->dataStack);
2227 
2228 	do_find(vm, name, NULL);
2229 }
2230 
2231 /*
2232  * r e c u r s e
2233  */
2234 static void
2235 ficlPrimitiveRecurseCoIm(ficlVm *vm)
2236 {
2237 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
2238 	ficlCell c;
2239 
2240 	FICL_IGNORE(vm);
2241 	c.p = dictionary->smudge;
2242 	ficlDictionaryAppendCell(dictionary, c);
2243 }
2244 
2245 /*
2246  * s o u r c e
2247  * CORE ( -- c-addr u )
2248  * c-addr is the address of, and u is the number of characters in, the
2249  * input buffer.
2250  */
2251 static void
2252 ficlPrimitiveSource(ficlVm *vm)
2253 {
2254 	FICL_STACK_CHECK(vm->dataStack, 0, 2);
2255 
2256 	ficlStackPushPointer(vm->dataStack, vm->tib.text);
2257 	ficlStackPushInteger(vm->dataStack, ficlVmGetInBufLen(vm));
2258 }
2259 
2260 /*
2261  * v e r s i o n
2262  * non-standard...
2263  */
2264 static void
2265 ficlPrimitiveVersion(ficlVm *vm)
2266 {
2267 	ficlVmTextOut(vm, "Ficl version " FICL_VERSION "\n");
2268 }
2269 
2270 /*
2271  * t o I n
2272  * to-in CORE
2273  */
2274 static void
2275 ficlPrimitiveToIn(ficlVm *vm)
2276 {
2277 	FICL_STACK_CHECK(vm->dataStack, 0, 1);
2278 
2279 	ficlStackPushPointer(vm->dataStack, &vm->tib.index);
2280 }
2281 
2282 /*
2283  * c o l o n N o N a m e
2284  * CORE EXT ( C:  -- colon-sys )  ( S:  -- xt )
2285  * Create an unnamed colon definition and push its address.
2286  * Change state to FICL_VM_STATE_COMPILE.
2287  */
2288 static void
2289 ficlPrimitiveColonNoName(ficlVm *vm)
2290 {
2291 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
2292 	ficlWord *word;
2293 	ficlString name;
2294 
2295 	FICL_STRING_SET_LENGTH(name, 0);
2296 	FICL_STRING_SET_POINTER(name, NULL);
2297 
2298 	vm->state = FICL_VM_STATE_COMPILE;
2299 	word = ficlDictionaryAppendWord(dictionary, name,
2300 	    (ficlPrimitive)ficlInstructionColonParen,
2301 	    FICL_WORD_DEFAULT | FICL_WORD_SMUDGED);
2302 
2303 	ficlStackPushPointer(vm->dataStack, word);
2304 	markControlTag(vm, colonTag);
2305 }
2306 
2307 /*
2308  * u s e r   V a r i a b l e
2309  * user  ( u -- )  "<spaces>name"
2310  * Get a name from the input stream and create a user variable
2311  * with the name and the index supplied. The run-time effect
2312  * of a user variable is to push the address of the indexed ficlCell
2313  * in the running vm's user array.
2314  *
2315  * User variables are vm local cells. Each vm has an array of
2316  * FICL_USER_CELLS of them when FICL_WANT_USER is nonzero.
2317  * Ficl's user facility is implemented with two primitives,
2318  * "user" and "(user)", a variable ("nUser") (in softcore.c) that
2319  * holds the index of the next free user ficlCell, and a redefinition
2320  * (also in softcore) of "user" that defines a user word and increments
2321  * nUser.
2322  */
2323 #if FICL_WANT_USER
2324 static void
2325 ficlPrimitiveUser(ficlVm *vm)
2326 {
2327 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
2328 	ficlString name = ficlVmGetWord(vm);
2329 	ficlCell c;
2330 
2331 	c = ficlStackPop(vm->dataStack);
2332 	if (c.i >= FICL_USER_CELLS) {
2333 		ficlVmThrowError(vm, "Error - out of user space");
2334 	}
2335 
2336 	ficlDictionaryAppendWord(dictionary, name,
2337 	    (ficlPrimitive)ficlInstructionUserParen, FICL_WORD_DEFAULT);
2338 	ficlDictionaryAppendCell(dictionary, c);
2339 }
2340 #endif
2341 
2342 #if FICL_WANT_LOCALS
2343 /*
2344  * Each local is recorded in a private locals dictionary as a
2345  * word that does doLocalIm at runtime. DoLocalIm compiles code
2346  * into the client definition to fetch the value of the
2347  * corresponding local variable from the return stack.
2348  * The private dictionary gets initialized at the end of each block
2349  * that uses locals (in ; and does> for example).
2350  */
2351 void
2352 ficlLocalParenIm(ficlVm *vm, int isDouble, int isFloat)
2353 {
2354 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
2355 	ficlInteger nLocal = vm->runningWord->param[0].i;
2356 
2357 #if !FICL_WANT_FLOAT
2358 	FICL_VM_ASSERT(vm, !isFloat);
2359 	/* get rid of unused parameter warning */
2360 	isFloat = 0;
2361 #endif /* FICL_WANT_FLOAT */
2362 
2363 	if (vm->state == FICL_VM_STATE_INTERPRET) {
2364 		ficlStack *stack;
2365 #if FICL_WANT_FLOAT
2366 		if (isFloat)
2367 			stack = vm->floatStack;
2368 		else
2369 #endif /* FICL_WANT_FLOAT */
2370 			stack = vm->dataStack;
2371 
2372 		ficlStackPush(stack, vm->returnStack->frame[nLocal]);
2373 		if (isDouble)
2374 			ficlStackPush(stack, vm->returnStack->frame[nLocal+1]);
2375 	} else {
2376 		ficlInstruction instruction;
2377 		ficlInteger appendLocalOffset;
2378 #if FICL_WANT_FLOAT
2379 		if (isFloat) {
2380 			instruction =
2381 			    (isDouble) ? ficlInstructionGetF2LocalParen :
2382 			    ficlInstructionGetFLocalParen;
2383 			appendLocalOffset = FICL_TRUE;
2384 		} else
2385 #endif /* FICL_WANT_FLOAT */
2386 		if (nLocal == 0) {
2387 			instruction = (isDouble) ? ficlInstructionGet2Local0 :
2388 			    ficlInstructionGetLocal0;
2389 			appendLocalOffset = FICL_FALSE;
2390 		} else if ((nLocal == 1) && !isDouble) {
2391 			instruction = ficlInstructionGetLocal1;
2392 			appendLocalOffset = FICL_FALSE;
2393 		} else {
2394 			instruction =
2395 			    (isDouble) ? ficlInstructionGet2LocalParen :
2396 			    ficlInstructionGetLocalParen;
2397 			appendLocalOffset = FICL_TRUE;
2398 		}
2399 
2400 		ficlDictionaryAppendUnsigned(dictionary, instruction);
2401 		if (appendLocalOffset)
2402 			ficlDictionaryAppendUnsigned(dictionary, nLocal);
2403 	}
2404 }
2405 
2406 static void
2407 ficlPrimitiveDoLocalIm(ficlVm *vm)
2408 {
2409 	ficlLocalParenIm(vm, 0, 0);
2410 }
2411 
2412 static void
2413 ficlPrimitiveDo2LocalIm(ficlVm *vm)
2414 {
2415 	ficlLocalParenIm(vm, 1, 0);
2416 }
2417 
2418 #if FICL_WANT_FLOAT
2419 static void
2420 ficlPrimitiveDoFLocalIm(ficlVm *vm)
2421 {
2422 	ficlLocalParenIm(vm, 0, 1);
2423 }
2424 
2425 static void
2426 ficlPrimitiveDoF2LocalIm(ficlVm *vm)
2427 {
2428 	ficlLocalParenIm(vm, 1, 1);
2429 }
2430 #endif /* FICL_WANT_FLOAT */
2431 
2432 /*
2433  * l o c a l P a r e n
2434  * paren-local-paren LOCAL
2435  * Interpretation: Interpretation semantics for this word are undefined.
2436  * Execution: ( c-addr u -- )
2437  * When executed during compilation, (LOCAL) passes a message to the
2438  * system that has one of two meanings. If u is non-zero,
2439  * the message identifies a new local whose definition name is given by
2440  * the string of characters identified by c-addr u. If u is zero,
2441  * the message is last local and c-addr has no significance.
2442  *
2443  * The result of executing (LOCAL) during compilation of a definition is
2444  * to create a set of named local identifiers, each of which is
2445  * a definition name, that only have execution semantics within the scope
2446  * of that definition's source.
2447  *
2448  * local Execution: ( -- x )
2449  *
2450  * Push the local's value, x, onto the stack. The local's value is
2451  * initialized as described in 13.3.3 Processing locals and may be
2452  * changed by preceding the local's name with TO. An ambiguous condition
2453  * exists when local is executed while in interpretation state.
2454  */
2455 void
2456 ficlLocalParen(ficlVm *vm, int isDouble, int isFloat)
2457 {
2458 	ficlDictionary *dictionary;
2459 	ficlString name;
2460 
2461 	FICL_STACK_CHECK(vm->dataStack, 2, 0);
2462 
2463 	dictionary = ficlVmGetDictionary(vm);
2464 	FICL_STRING_SET_LENGTH(name, ficlStackPopUnsigned(vm->dataStack));
2465 	FICL_STRING_SET_POINTER(name,
2466 	    (char *)ficlStackPopPointer(vm->dataStack));
2467 
2468 	if (FICL_STRING_GET_LENGTH(name) > 0) {
2469 		/*
2470 		 * add a local to the **locals** dictionary and
2471 		 * update localsCount
2472 		 */
2473 		ficlPrimitive code;
2474 		ficlInstruction instruction;
2475 		ficlDictionary *locals;
2476 
2477 		locals = ficlSystemGetLocals(vm->callback.system);
2478 		if (vm->callback.system->localsCount >= FICL_MAX_LOCALS) {
2479 			ficlVmThrowError(vm, "Error: out of local space");
2480 		}
2481 
2482 #if !FICL_WANT_FLOAT
2483 		FICL_VM_ASSERT(vm, !isFloat);
2484 		/* get rid of unused parameter warning */
2485 		isFloat = 0;
2486 #else /* FICL_WANT_FLOAT */
2487 		if (isFloat) {
2488 			if (isDouble) {
2489 				code = ficlPrimitiveDoF2LocalIm;
2490 				instruction = ficlInstructionToF2LocalParen;
2491 			} else {
2492 				code = ficlPrimitiveDoFLocalIm;
2493 				instruction = ficlInstructionToFLocalParen;
2494 			}
2495 		} else
2496 #endif /* FICL_WANT_FLOAT */
2497 		if (isDouble) {
2498 			code = ficlPrimitiveDo2LocalIm;
2499 			instruction = ficlInstructionTo2LocalParen;
2500 		} else {
2501 			code = ficlPrimitiveDoLocalIm;
2502 			instruction = ficlInstructionToLocalParen;
2503 		}
2504 
2505 		ficlDictionaryAppendWord(locals, name, code,
2506 		    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
2507 		ficlDictionaryAppendUnsigned(locals,
2508 		    vm->callback.system->localsCount);
2509 
2510 		if (vm->callback.system->localsCount == 0) {
2511 			/*
2512 			 * FICL_VM_STATE_COMPILE code to create a local
2513 			 * stack frame
2514 			 */
2515 			ficlDictionaryAppendUnsigned(dictionary,
2516 			    ficlInstructionLinkParen);
2517 
2518 			/* save location in dictionary for #locals */
2519 			vm->callback.system->localsFixup = dictionary->here;
2520 			ficlDictionaryAppendUnsigned(dictionary,
2521 			    vm->callback.system->localsCount);
2522 		}
2523 
2524 		ficlDictionaryAppendUnsigned(dictionary, instruction);
2525 		ficlDictionaryAppendUnsigned(dictionary,
2526 		    vm->callback.system->localsCount);
2527 
2528 		vm->callback.system->localsCount += (isDouble) ? 2 : 1;
2529 	} else if (vm->callback.system->localsCount > 0) {
2530 		/* write localsCount to (link) param area in dictionary */
2531 		*(ficlInteger *)(vm->callback.system->localsFixup) =
2532 		    vm->callback.system->localsCount;
2533 	}
2534 }
2535 
2536 static void
2537 ficlPrimitiveLocalParen(ficlVm *vm)
2538 {
2539 	ficlLocalParen(vm, 0, 0);
2540 }
2541 
2542 static void
2543 ficlPrimitive2LocalParen(ficlVm *vm)
2544 {
2545 	ficlLocalParen(vm, 1, 0);
2546 }
2547 #endif /* FICL_WANT_LOCALS */
2548 
2549 /*
2550  * t o V a l u e
2551  * CORE EXT
2552  * Interpretation: ( x "<spaces>name" -- )
2553  * Skip leading spaces and parse name delimited by a space. Store x in
2554  * name. An ambiguous condition exists if name was not defined by VALUE.
2555  * NOTE: In Ficl, VALUE is an alias of CONSTANT
2556  */
2557 static void
2558 ficlPrimitiveToValue(ficlVm *vm)
2559 {
2560 	ficlString name = ficlVmGetWord(vm);
2561 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
2562 	ficlWord *word;
2563 	ficlInstruction instruction = 0;
2564 	ficlStack *stack;
2565 	ficlInteger isDouble;
2566 #if FICL_WANT_LOCALS
2567 	ficlInteger nLocal;
2568 	ficlInteger appendLocalOffset;
2569 	ficlInteger isFloat;
2570 #endif /* FICL_WANT_LOCALS */
2571 
2572 #if FICL_WANT_LOCALS
2573 	if ((vm->callback.system->localsCount > 0) &&
2574 	    (vm->state == FICL_VM_STATE_COMPILE)) {
2575 		ficlDictionary *locals;
2576 
2577 		locals = ficlSystemGetLocals(vm->callback.system);
2578 		word = ficlDictionaryLookup(locals, name);
2579 		if (!word)
2580 			goto TO_GLOBAL;
2581 
2582 		if (word->code == ficlPrimitiveDoLocalIm) {
2583 			instruction = ficlInstructionToLocalParen;
2584 			isDouble = isFloat = FICL_FALSE;
2585 		} else if (word->code == ficlPrimitiveDo2LocalIm) {
2586 			instruction = ficlInstructionTo2LocalParen;
2587 			isDouble = FICL_TRUE;
2588 			isFloat = FICL_FALSE;
2589 		}
2590 #if FICL_WANT_FLOAT
2591 		else if (word->code == ficlPrimitiveDoFLocalIm) {
2592 			instruction = ficlInstructionToFLocalParen;
2593 			isDouble = FICL_FALSE;
2594 			isFloat = FICL_TRUE;
2595 		} else if (word->code == ficlPrimitiveDoF2LocalIm) {
2596 			instruction = ficlInstructionToF2LocalParen;
2597 			isDouble = isFloat = FICL_TRUE;
2598 		}
2599 #endif /* FICL_WANT_FLOAT */
2600 		else {
2601 			ficlVmThrowError(vm,
2602 			    "to %.*s : local is of unknown type",
2603 			    FICL_STRING_GET_LENGTH(name),
2604 			    FICL_STRING_GET_POINTER(name));
2605 			return;
2606 		}
2607 
2608 		nLocal = word->param[0].i;
2609 		appendLocalOffset = FICL_TRUE;
2610 
2611 #if FICL_WANT_FLOAT
2612 		if (!isFloat) {
2613 #endif /* FICL_WANT_FLOAT */
2614 			if (nLocal == 0) {
2615 				instruction =
2616 				    (isDouble) ? ficlInstructionTo2Local0 :
2617 				    ficlInstructionToLocal0;
2618 				appendLocalOffset = FICL_FALSE;
2619 			} else if ((nLocal == 1) && !isDouble) {
2620 				instruction = ficlInstructionToLocal1;
2621 				appendLocalOffset = FICL_FALSE;
2622 			}
2623 #if FICL_WANT_FLOAT
2624 		}
2625 #endif /* FICL_WANT_FLOAT */
2626 
2627 		ficlDictionaryAppendUnsigned(dictionary, instruction);
2628 		if (appendLocalOffset)
2629 			ficlDictionaryAppendUnsigned(dictionary, nLocal);
2630 		return;
2631 	}
2632 #endif
2633 
2634 #if FICL_WANT_LOCALS
2635 TO_GLOBAL:
2636 #endif /* FICL_WANT_LOCALS */
2637 	word = ficlDictionaryLookup(dictionary, name);
2638 	if (!word)
2639 		ficlVmThrowError(vm, "%.*s not found",
2640 		    FICL_STRING_GET_LENGTH(name),
2641 		    FICL_STRING_GET_POINTER(name));
2642 
2643 	switch ((ficlInstruction)word->code) {
2644 	case ficlInstructionConstantParen:
2645 		instruction = ficlInstructionStore;
2646 		stack = vm->dataStack;
2647 		isDouble = FICL_FALSE;
2648 	break;
2649 	case ficlInstruction2ConstantParen:
2650 		instruction = ficlInstruction2Store;
2651 		stack = vm->dataStack;
2652 		isDouble = FICL_TRUE;
2653 	break;
2654 #if FICL_WANT_FLOAT
2655 	case ficlInstructionFConstantParen:
2656 		instruction = ficlInstructionFStore;
2657 		stack = vm->floatStack;
2658 		isDouble = FICL_FALSE;
2659 	break;
2660 	case ficlInstructionF2ConstantParen:
2661 		instruction = ficlInstructionF2Store;
2662 		stack = vm->floatStack;
2663 		isDouble = FICL_TRUE;
2664 	break;
2665 #endif /* FICL_WANT_FLOAT */
2666 	default:
2667 		ficlVmThrowError(vm,
2668 		    "to %.*s : value/constant is of unknown type",
2669 		    FICL_STRING_GET_LENGTH(name),
2670 		    FICL_STRING_GET_POINTER(name));
2671 	return;
2672 	}
2673 
2674 	if (vm->state == FICL_VM_STATE_INTERPRET) {
2675 		word->param[0] = ficlStackPop(stack);
2676 		if (isDouble)
2677 			word->param[1] = ficlStackPop(stack);
2678 	} else {
2679 		/* FICL_VM_STATE_COMPILE code to store to word's param */
2680 		ficlStackPushPointer(vm->dataStack, &word->param[0]);
2681 		ficlPrimitiveLiteralIm(vm);
2682 		ficlDictionaryAppendUnsigned(dictionary, instruction);
2683 	}
2684 }
2685 
2686 /*
2687  * f m S l a s h M o d
2688  * f-m-slash-mod CORE ( d1 n1 -- n2 n3 )
2689  * Divide d1 by n1, giving the floored quotient n3 and the remainder n2.
2690  * Input and output stack arguments are signed. An ambiguous condition
2691  * exists if n1 is zero or if the quotient lies outside the range of a
2692  * single-ficlCell signed integer.
2693  */
2694 static void
2695 ficlPrimitiveFMSlashMod(ficlVm *vm)
2696 {
2697 	ficl2Integer d1;
2698 	ficlInteger n1;
2699 	ficl2IntegerQR qr;
2700 
2701 	FICL_STACK_CHECK(vm->dataStack, 3, 2);
2702 
2703 	n1 = ficlStackPopInteger(vm->dataStack);
2704 	d1 = ficlStackPop2Integer(vm->dataStack);
2705 	qr = ficl2IntegerDivideFloored(d1, n1);
2706 	ficlStackPushInteger(vm->dataStack, qr.remainder);
2707 	ficlStackPushInteger(vm->dataStack,
2708 	    FICL_2UNSIGNED_GET_LOW(qr.quotient));
2709 }
2710 
2711 /*
2712  * s m S l a s h R e m
2713  * s-m-slash-remainder CORE ( d1 n1 -- n2 n3 )
2714  * Divide d1 by n1, giving the symmetric quotient n3 and the remainder n2.
2715  * Input and output stack arguments are signed. An ambiguous condition
2716  * exists if n1 is zero or if the quotient lies outside the range of a
2717  * single-ficlCell signed integer.
2718  */
2719 static void
2720 ficlPrimitiveSMSlashRem(ficlVm *vm)
2721 {
2722 	ficl2Integer d1;
2723 	ficlInteger n1;
2724 	ficl2IntegerQR qr;
2725 
2726 	FICL_STACK_CHECK(vm->dataStack, 3, 2);
2727 
2728 	n1 = ficlStackPopInteger(vm->dataStack);
2729 	d1 = ficlStackPop2Integer(vm->dataStack);
2730 	qr = ficl2IntegerDivideSymmetric(d1, n1);
2731 	ficlStackPushInteger(vm->dataStack, qr.remainder);
2732 	ficlStackPushInteger(vm->dataStack,
2733 	    FICL_2UNSIGNED_GET_LOW(qr.quotient));
2734 }
2735 
2736 static void
2737 ficlPrimitiveMod(ficlVm *vm)
2738 {
2739 	ficl2Integer d1;
2740 	ficlInteger n1;
2741 	ficlInteger i;
2742 	ficl2IntegerQR qr;
2743 	FICL_STACK_CHECK(vm->dataStack, 2, 1);
2744 
2745 	n1 = ficlStackPopInteger(vm->dataStack);
2746 	i = ficlStackPopInteger(vm->dataStack);
2747 	FICL_INTEGER_TO_2INTEGER(i, d1);
2748 	qr = ficl2IntegerDivideSymmetric(d1, n1);
2749 	ficlStackPushInteger(vm->dataStack, qr.remainder);
2750 }
2751 
2752 /*
2753  * u m S l a s h M o d
2754  * u-m-slash-mod CORE ( ud u1 -- u2 u3 )
2755  * Divide ud by u1, giving the quotient u3 and the remainder u2.
2756  * All values and arithmetic are unsigned. An ambiguous condition
2757  * exists if u1 is zero or if the quotient lies outside the range of a
2758  * single-ficlCell unsigned integer.
2759  */
2760 static void
2761 ficlPrimitiveUMSlashMod(ficlVm *vm)
2762 {
2763 	ficl2Unsigned ud;
2764 	ficlUnsigned u1;
2765 	ficl2UnsignedQR uqr;
2766 
2767 	u1    = ficlStackPopUnsigned(vm->dataStack);
2768 	ud    = ficlStackPop2Unsigned(vm->dataStack);
2769 	uqr   = ficl2UnsignedDivide(ud, u1);
2770 	ficlStackPushUnsigned(vm->dataStack, uqr.remainder);
2771 	ficlStackPushUnsigned(vm->dataStack,
2772 	    FICL_2UNSIGNED_GET_LOW(uqr.quotient));
2773 }
2774 
2775 /*
2776  * m S t a r
2777  * m-star CORE ( n1 n2 -- d )
2778  * d is the signed product of n1 times n2.
2779  */
2780 static void
2781 ficlPrimitiveMStar(ficlVm *vm)
2782 {
2783 	ficlInteger n2;
2784 	ficlInteger n1;
2785 	ficl2Integer d;
2786 	FICL_STACK_CHECK(vm->dataStack, 2, 2);
2787 
2788 	n2 = ficlStackPopInteger(vm->dataStack);
2789 	n1 = ficlStackPopInteger(vm->dataStack);
2790 
2791 	d = ficl2IntegerMultiply(n1, n2);
2792 	ficlStackPush2Integer(vm->dataStack, d);
2793 }
2794 
2795 static void
2796 ficlPrimitiveUMStar(ficlVm *vm)
2797 {
2798 	ficlUnsigned u2;
2799 	ficlUnsigned u1;
2800 	ficl2Unsigned ud;
2801 	FICL_STACK_CHECK(vm->dataStack, 2, 2);
2802 
2803 	u2 = ficlStackPopUnsigned(vm->dataStack);
2804 	u1 = ficlStackPopUnsigned(vm->dataStack);
2805 
2806 	ud = ficl2UnsignedMultiply(u1, u2);
2807 	ficlStackPush2Unsigned(vm->dataStack, ud);
2808 }
2809 
2810 /*
2811  * 2 r o t
2812  * DOUBLE   ( d1 d2 d3 -- d2 d3 d1 )
2813  */
2814 static void
2815 ficlPrimitive2Rot(ficlVm *vm)
2816 {
2817 	ficl2Integer d1, d2, d3;
2818 	FICL_STACK_CHECK(vm->dataStack, 6, 6);
2819 
2820 	d3 = ficlStackPop2Integer(vm->dataStack);
2821 	d2 = ficlStackPop2Integer(vm->dataStack);
2822 	d1 = ficlStackPop2Integer(vm->dataStack);
2823 	ficlStackPush2Integer(vm->dataStack, d2);
2824 	ficlStackPush2Integer(vm->dataStack, d3);
2825 	ficlStackPush2Integer(vm->dataStack, d1);
2826 }
2827 
2828 /*
2829  * p a d
2830  * CORE EXT  ( -- c-addr )
2831  * c-addr is the address of a transient region that can be used to hold
2832  * data for intermediate processing.
2833  */
2834 static void
2835 ficlPrimitivePad(ficlVm *vm)
2836 {
2837 	ficlStackPushPointer(vm->dataStack, vm->pad);
2838 }
2839 
2840 /*
2841  * s o u r c e - i d
2842  * CORE EXT, FILE   ( -- 0 | -1 | fileid )
2843  *    Identifies the input source as follows:
2844  *
2845  * SOURCE-ID       Input source
2846  * ---------       ------------
2847  * fileid          Text file fileid
2848  * -1              String (via EVALUATE)
2849  * 0               User input device
2850  */
2851 static void
2852 ficlPrimitiveSourceID(ficlVm *vm)
2853 {
2854 	ficlStackPushInteger(vm->dataStack, vm->sourceId.i);
2855 }
2856 
2857 /*
2858  * r e f i l l
2859  * CORE EXT   ( -- flag )
2860  * Attempt to fill the input buffer from the input source, returning
2861  * a FICL_TRUE flag if successful.
2862  * When the input source is the user input device, attempt to receive input
2863  * into the terminal input buffer. If successful, make the result the input
2864  * buffer, set >IN to zero, and return FICL_TRUE. Receipt of a line containing
2865  * no characters is considered successful. If there is no input available from
2866  * the current input source, return FICL_FALSE.
2867  * When the input source is a string from EVALUATE, return FICL_FALSE and
2868  * perform no other action.
2869  */
2870 static void
2871 ficlPrimitiveRefill(ficlVm *vm)
2872 {
2873 	ficlInteger ret = (vm->sourceId.i == -1) ? FICL_FALSE : FICL_TRUE;
2874 	if (ret && (vm->restart == 0))
2875 		ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
2876 
2877 	ficlStackPushInteger(vm->dataStack, ret);
2878 }
2879 
2880 /*
2881  * freebsd exception handling words
2882  * Catch, from ANS Forth standard. Installs a safety net, then EXECUTE
2883  * the word in ToS. If an exception happens, restore the state to what
2884  * it was before, and pushes the exception value on the stack. If not,
2885  * push zero.
2886  *
2887  * Notice that Catch implements an inner interpreter. This is ugly,
2888  * but given how Ficl works, it cannot be helped. The problem is that
2889  * colon definitions will be executed *after* the function returns,
2890  * while "code" definitions will be executed immediately. I considered
2891  * other solutions to this problem, but all of them shared the same
2892  * basic problem (with added disadvantages): if Ficl ever changes it's
2893  * inner thread modus operandi, one would have to fix this word.
2894  *
2895  * More comments can be found throughout catch's code.
2896  *
2897  * Daniel C. Sobral Jan 09/1999
2898  * sadler may 2000 -- revised to follow ficl.c:ficlExecXT.
2899  */
2900 static void
2901 ficlPrimitiveCatch(ficlVm *vm)
2902 {
2903 	int except;
2904 	jmp_buf vmState;
2905 	ficlVm vmCopy;
2906 	ficlStack dataStackCopy;
2907 	ficlStack returnStackCopy;
2908 	ficlWord *word;
2909 
2910 	FICL_VM_ASSERT(vm, vm);
2911 	FICL_VM_ASSERT(vm, vm->callback.system->exitInnerWord);
2912 
2913 	/*
2914 	 * Get xt.
2915 	 * We need this *before* we save the stack pointer, or
2916 	 * we'll have to pop one element out of the stack after
2917 	 * an exception. I prefer to get done with it up front. :-)
2918 	 */
2919 
2920 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
2921 
2922 	word = ficlStackPopPointer(vm->dataStack);
2923 
2924 	/*
2925 	 * Save vm's state -- a catch will not back out environmental
2926 	 * changes.
2927 	 *
2928 	 * We are *not* saving dictionary state, since it is
2929 	 * global instead of per vm, and we are not saving
2930 	 * stack contents, since we are not required to (and,
2931 	 * thus, it would be useless). We save vm, and vm
2932 	 * "stacks" (a structure containing general information
2933 	 * about it, including the current stack pointer).
2934 	 */
2935 	memcpy((void*)&vmCopy, (void*)vm, sizeof (ficlVm));
2936 	memcpy((void*)&dataStackCopy, (void*)vm->dataStack, sizeof (ficlStack));
2937 	memcpy((void*)&returnStackCopy, (void*)vm->returnStack,
2938 	    sizeof (ficlStack));
2939 
2940 	/*
2941 	 * Give vm a jmp_buf
2942 	 */
2943 	vm->exceptionHandler = &vmState;
2944 
2945 	/*
2946 	 * Safety net
2947 	 */
2948 	except = setjmp(vmState);
2949 
2950 	switch (except) {
2951 	/*
2952 	 * Setup condition - push poison pill so that the VM throws
2953 	 * VM_INNEREXIT if the XT terminates normally, then execute
2954 	 * the XT
2955 	 */
2956 	case 0:
2957 		/* Open mouth, insert emetic */
2958 		ficlVmPushIP(vm, &(vm->callback.system->exitInnerWord));
2959 		ficlVmExecuteWord(vm, word);
2960 		ficlVmInnerLoop(vm, 0);
2961 	break;
2962 
2963 	/*
2964 	 * Normal exit from XT - lose the poison pill,
2965 	 * restore old setjmp vector and push a zero.
2966 	 */
2967 	case FICL_VM_STATUS_INNER_EXIT:
2968 		ficlVmPopIP(vm);	/* Gack - hurl poison pill */
2969 					/* Restore just the setjmp vector */
2970 		vm->exceptionHandler = vmCopy.exceptionHandler;
2971 					/* Push 0 -- everything is ok */
2972 		ficlStackPushInteger(vm->dataStack, 0);
2973 	break;
2974 
2975 	/*
2976 	 * Some other exception got thrown - restore pre-existing VM state
2977 	 * and push the exception code
2978 	 */
2979 	default:
2980 		/* Restore vm's state */
2981 		memcpy((void*)vm, (void*)&vmCopy, sizeof (ficlVm));
2982 		memcpy((void*)vm->dataStack, (void*)&dataStackCopy,
2983 		    sizeof (ficlStack));
2984 		memcpy((void*)vm->returnStack, (void*)&returnStackCopy,
2985 		    sizeof (ficlStack));
2986 
2987 		ficlStackPushInteger(vm->dataStack, except); /* Push error */
2988 	break;
2989 	}
2990 }
2991 
2992 /*
2993  * t h r o w
2994  * EXCEPTION
2995  * Throw --  From ANS Forth standard.
2996  *
2997  * Throw takes the ToS and, if that's different from zero,
2998  * returns to the last executed catch context. Further throws will
2999  * unstack previously executed "catches", in LIFO mode.
3000  *
3001  * Daniel C. Sobral Jan 09/1999
3002  */
3003 static void
3004 ficlPrimitiveThrow(ficlVm *vm)
3005 {
3006 	int except;
3007 
3008 	except = ficlStackPopInteger(vm->dataStack);
3009 
3010 	if (except)
3011 		ficlVmThrow(vm, except);
3012 }
3013 
3014 /*
3015  * a l l o c a t e
3016  * MEMORY
3017  */
3018 static void
3019 ficlPrimitiveAllocate(ficlVm *vm)
3020 {
3021 	size_t size;
3022 	void *p;
3023 
3024 	size = ficlStackPopInteger(vm->dataStack);
3025 	p = ficlMalloc(size);
3026 	ficlStackPushPointer(vm->dataStack, p);
3027 	if (p != NULL)
3028 		ficlStackPushInteger(vm->dataStack, 0);
3029 	else
3030 		ficlStackPushInteger(vm->dataStack, 1);
3031 }
3032 
3033 /*
3034  * f r e e
3035  * MEMORY
3036  */
3037 static void
3038 ficlPrimitiveFree(ficlVm *vm)
3039 {
3040 	void *p;
3041 
3042 	p = ficlStackPopPointer(vm->dataStack);
3043 	ficlFree(p);
3044 	ficlStackPushInteger(vm->dataStack, 0);
3045 }
3046 
3047 /*
3048  * r e s i z e
3049  * MEMORY
3050  */
3051 static void
3052 ficlPrimitiveResize(ficlVm *vm)
3053 {
3054 	size_t size;
3055 	void *new, *old;
3056 
3057 	size = ficlStackPopInteger(vm->dataStack);
3058 	old = ficlStackPopPointer(vm->dataStack);
3059 	new = ficlRealloc(old, size);
3060 
3061 	if (new) {
3062 		ficlStackPushPointer(vm->dataStack, new);
3063 		ficlStackPushInteger(vm->dataStack, 0);
3064 	} else {
3065 		ficlStackPushPointer(vm->dataStack, old);
3066 		ficlStackPushInteger(vm->dataStack, 1);
3067 	}
3068 }
3069 
3070 /*
3071  * e x i t - i n n e r
3072  * Signals execXT that an inner loop has completed
3073  */
3074 static void
3075 ficlPrimitiveExitInner(ficlVm *vm)
3076 {
3077 	ficlVmThrow(vm, FICL_VM_STATUS_INNER_EXIT);
3078 }
3079 
3080 #if 0
3081 static void
3082 ficlPrimitiveName(ficlVm *vm)
3083 {
3084 	FICL_IGNORE(vm);
3085 }
3086 #endif
3087 
3088 /*
3089  * f i c l C o m p i l e C o r e
3090  * Builds the primitive wordset and the environment-query namespace.
3091  */
3092 void
3093 ficlSystemCompileCore(ficlSystem *system)
3094 {
3095 	ficlWord *interpret;
3096 	ficlDictionary *dictionary = ficlSystemGetDictionary(system);
3097 	ficlDictionary *environment = ficlSystemGetEnvironment(system);
3098 
3099 	FICL_SYSTEM_ASSERT(system, dictionary);
3100 	FICL_SYSTEM_ASSERT(system, environment);
3101 
3102 #define	FICL_TOKEN(token, description)
3103 #define	FICL_INSTRUCTION_TOKEN(token, description, flags)	\
3104 	ficlDictionarySetInstruction(dictionary, description, token, flags);
3105 #include "ficltokens.h"
3106 #undef FICL_TOKEN
3107 #undef FICL_INSTRUCTION_TOKEN
3108 
3109 	/*
3110 	 * The Core word set
3111 	 * see softcore.c for definitions of: abs bl space spaces abort"
3112 	 */
3113 	ficlDictionarySetPrimitive(dictionary, "#", ficlPrimitiveNumberSign,
3114 	    FICL_WORD_DEFAULT);
3115 	ficlDictionarySetPrimitive(dictionary, "#>",
3116 	    ficlPrimitiveNumberSignGreater, FICL_WORD_DEFAULT);
3117 	ficlDictionarySetPrimitive(dictionary, "#s", ficlPrimitiveNumberSignS,
3118 	    FICL_WORD_DEFAULT);
3119 	ficlDictionarySetPrimitive(dictionary, "\'", ficlPrimitiveTick,
3120 	    FICL_WORD_DEFAULT);
3121 	ficlDictionarySetPrimitive(dictionary, "(", ficlPrimitiveParenthesis,
3122 	    FICL_WORD_IMMEDIATE);
3123 	ficlDictionarySetPrimitive(dictionary, "+loop",
3124 	    ficlPrimitivePlusLoopCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3125 	ficlDictionarySetPrimitive(dictionary, ".", ficlPrimitiveDot,
3126 	    FICL_WORD_DEFAULT);
3127 	ficlDictionarySetPrimitive(dictionary, ".\"",
3128 	    ficlPrimitiveDotQuoteCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3129 	ficlDictionarySetPrimitive(dictionary, ":", ficlPrimitiveColon,
3130 	    FICL_WORD_DEFAULT);
3131 	ficlDictionarySetPrimitive(dictionary, ";", ficlPrimitiveSemicolonCoIm,
3132 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3133 	ficlDictionarySetPrimitive(dictionary, "<#",
3134 	    ficlPrimitiveLessNumberSign, FICL_WORD_DEFAULT);
3135 	ficlDictionarySetPrimitive(dictionary, ">body", ficlPrimitiveToBody,
3136 	    FICL_WORD_DEFAULT);
3137 	ficlDictionarySetPrimitive(dictionary, ">in", ficlPrimitiveToIn,
3138 	    FICL_WORD_DEFAULT);
3139 	ficlDictionarySetPrimitive(dictionary, ">number", ficlPrimitiveToNumber,
3140 	    FICL_WORD_DEFAULT);
3141 	ficlDictionarySetPrimitive(dictionary, "abort", ficlPrimitiveAbort,
3142 	    FICL_WORD_DEFAULT);
3143 	ficlDictionarySetPrimitive(dictionary, "accept", ficlPrimitiveAccept,
3144 	    FICL_WORD_DEFAULT);
3145 	ficlDictionarySetPrimitive(dictionary, "align", ficlPrimitiveAlign,
3146 	    FICL_WORD_DEFAULT);
3147 	ficlDictionarySetPrimitive(dictionary, "aligned", ficlPrimitiveAligned,
3148 	    FICL_WORD_DEFAULT);
3149 	ficlDictionarySetPrimitive(dictionary, "allot", ficlPrimitiveAllot,
3150 	    FICL_WORD_DEFAULT);
3151 	ficlDictionarySetPrimitive(dictionary, "base", ficlPrimitiveBase,
3152 	    FICL_WORD_DEFAULT);
3153 	ficlDictionarySetPrimitive(dictionary, "begin", ficlPrimitiveBeginCoIm,
3154 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3155 	ficlDictionarySetPrimitive(dictionary, "case", ficlPrimitiveCaseCoIm,
3156 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3157 	ficlDictionarySetPrimitive(dictionary, "char", ficlPrimitiveChar,
3158 	    FICL_WORD_DEFAULT);
3159 	ficlDictionarySetPrimitive(dictionary, "char+", ficlPrimitiveCharPlus,
3160 	    FICL_WORD_DEFAULT);
3161 	ficlDictionarySetPrimitive(dictionary, "chars", ficlPrimitiveChars,
3162 	    FICL_WORD_DEFAULT);
3163 	ficlDictionarySetPrimitive(dictionary, "constant",
3164 	    ficlPrimitiveConstant, FICL_WORD_DEFAULT);
3165 	ficlDictionarySetPrimitive(dictionary, "count", ficlPrimitiveCount,
3166 	    FICL_WORD_DEFAULT);
3167 	ficlDictionarySetPrimitive(dictionary, "cr", ficlPrimitiveCR,
3168 	    FICL_WORD_DEFAULT);
3169 	ficlDictionarySetPrimitive(dictionary, "create", ficlPrimitiveCreate,
3170 	    FICL_WORD_DEFAULT);
3171 	ficlDictionarySetPrimitive(dictionary, "decimal", ficlPrimitiveDecimal,
3172 	    FICL_WORD_DEFAULT);
3173 	ficlDictionarySetPrimitive(dictionary, "depth", ficlPrimitiveDepth,
3174 	    FICL_WORD_DEFAULT);
3175 	ficlDictionarySetPrimitive(dictionary, "do", ficlPrimitiveDoCoIm,
3176 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3177 	ficlDictionarySetPrimitive(dictionary, "does>", ficlPrimitiveDoesCoIm,
3178 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3179 	ficlDictionarySetPrimitive(dictionary, "else", ficlPrimitiveElseCoIm,
3180 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3181 	ficlDictionarySetPrimitive(dictionary, "emit", ficlPrimitiveEmit,
3182 	    FICL_WORD_DEFAULT);
3183 	ficlDictionarySetPrimitive(dictionary, "endcase",
3184 	    ficlPrimitiveEndcaseCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3185 	ficlDictionarySetPrimitive(dictionary, "endof", ficlPrimitiveEndofCoIm,
3186 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3187 	ficlDictionarySetPrimitive(dictionary, "environment?",
3188 	    ficlPrimitiveEnvironmentQ, FICL_WORD_DEFAULT);
3189 	ficlDictionarySetPrimitive(dictionary, "evaluate",
3190 	    ficlPrimitiveEvaluate, FICL_WORD_DEFAULT);
3191 	ficlDictionarySetPrimitive(dictionary, "execute", ficlPrimitiveExecute,
3192 	    FICL_WORD_DEFAULT);
3193 	ficlDictionarySetPrimitive(dictionary, "exit", ficlPrimitiveExitCoIm,
3194 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3195 	ficlDictionarySetPrimitive(dictionary, "fallthrough",
3196 	    ficlPrimitiveFallthroughCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3197 	ficlDictionarySetPrimitive(dictionary, "find", ficlPrimitiveCFind,
3198 	    FICL_WORD_DEFAULT);
3199 	ficlDictionarySetPrimitive(dictionary, "fm/mod",
3200 	    ficlPrimitiveFMSlashMod, FICL_WORD_DEFAULT);
3201 	ficlDictionarySetPrimitive(dictionary, "here", ficlPrimitiveHere,
3202 	    FICL_WORD_DEFAULT);
3203 	ficlDictionarySetPrimitive(dictionary, "hold", ficlPrimitiveHold,
3204 	    FICL_WORD_DEFAULT);
3205 	ficlDictionarySetPrimitive(dictionary, "if", ficlPrimitiveIfCoIm,
3206 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3207 	ficlDictionarySetPrimitive(dictionary, "immediate",
3208 	    ficlPrimitiveImmediate, FICL_WORD_DEFAULT);
3209 	ficlDictionarySetPrimitive(dictionary, "literal",
3210 	    ficlPrimitiveLiteralIm, FICL_WORD_IMMEDIATE);
3211 	ficlDictionarySetPrimitive(dictionary, "loop", ficlPrimitiveLoopCoIm,
3212 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3213 	ficlDictionarySetPrimitive(dictionary, "m*", ficlPrimitiveMStar,
3214 	    FICL_WORD_DEFAULT);
3215 	ficlDictionarySetPrimitive(dictionary, "mod", ficlPrimitiveMod,
3216 	    FICL_WORD_DEFAULT);
3217 	ficlDictionarySetPrimitive(dictionary, "of", ficlPrimitiveOfCoIm,
3218 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3219 	ficlDictionarySetPrimitive(dictionary, "postpone",
3220 	    ficlPrimitivePostponeCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3221 	ficlDictionarySetPrimitive(dictionary, "quit", ficlPrimitiveQuit,
3222 	    FICL_WORD_DEFAULT);
3223 	ficlDictionarySetPrimitive(dictionary, "recurse",
3224 	    ficlPrimitiveRecurseCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3225 	ficlDictionarySetPrimitive(dictionary, "repeat",
3226 	    ficlPrimitiveRepeatCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3227 	ficlDictionarySetPrimitive(dictionary, "s\"",
3228 	    ficlPrimitiveStringQuoteIm, FICL_WORD_IMMEDIATE);
3229 	ficlDictionarySetPrimitive(dictionary, "sign", ficlPrimitiveSign,
3230 	    FICL_WORD_DEFAULT);
3231 	ficlDictionarySetPrimitive(dictionary, "sm/rem",
3232 	    ficlPrimitiveSMSlashRem, FICL_WORD_DEFAULT);
3233 	ficlDictionarySetPrimitive(dictionary, "source", ficlPrimitiveSource,
3234 	    FICL_WORD_DEFAULT);
3235 	ficlDictionarySetPrimitive(dictionary, "state", ficlPrimitiveState,
3236 	    FICL_WORD_DEFAULT);
3237 	ficlDictionarySetPrimitive(dictionary, "then", ficlPrimitiveEndifCoIm,
3238 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3239 	ficlDictionarySetPrimitive(dictionary, "type", ficlPrimitiveType,
3240 	    FICL_WORD_DEFAULT);
3241 	ficlDictionarySetPrimitive(dictionary, "u.", ficlPrimitiveUDot,
3242 	    FICL_WORD_DEFAULT);
3243 	ficlDictionarySetPrimitive(dictionary, "um*", ficlPrimitiveUMStar,
3244 	    FICL_WORD_DEFAULT);
3245 	ficlDictionarySetPrimitive(dictionary, "um/mod",
3246 	    ficlPrimitiveUMSlashMod, FICL_WORD_DEFAULT);
3247 	ficlDictionarySetPrimitive(dictionary, "until",
3248 	    ficlPrimitiveUntilCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3249 	ficlDictionarySetPrimitive(dictionary, "variable",
3250 	    ficlPrimitiveVariable, FICL_WORD_DEFAULT);
3251 	ficlDictionarySetPrimitive(dictionary, "while",
3252 	    ficlPrimitiveWhileCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3253 	ficlDictionarySetPrimitive(dictionary, "word", ficlPrimitiveWord,
3254 	    FICL_WORD_DEFAULT);
3255 	ficlDictionarySetPrimitive(dictionary, "[",
3256 	    ficlPrimitiveLeftBracketCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3257 	ficlDictionarySetPrimitive(dictionary, "[\']",
3258 	    ficlPrimitiveBracketTickCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3259 	ficlDictionarySetPrimitive(dictionary, "[char]", ficlPrimitiveCharCoIm,
3260 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3261 	ficlDictionarySetPrimitive(dictionary, "]", ficlPrimitiveRightBracket,
3262 	    FICL_WORD_DEFAULT);
3263 	/*
3264 	 * The Core Extensions word set...
3265 	 * see softcore.fr for other definitions
3266 	 */
3267 	/* "#tib" */
3268 	ficlDictionarySetPrimitive(dictionary, ".(", ficlPrimitiveDotParen,
3269 	    FICL_WORD_IMMEDIATE);
3270 	/* ".r" is in softcore */
3271 	ficlDictionarySetPrimitive(dictionary, ":noname",
3272 	    ficlPrimitiveColonNoName, FICL_WORD_DEFAULT);
3273 	ficlDictionarySetPrimitive(dictionary, "?do", ficlPrimitiveQDoCoIm,
3274 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3275 	ficlDictionarySetPrimitive(dictionary, "again", ficlPrimitiveAgainCoIm,
3276 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3277 	ficlDictionarySetPrimitive(dictionary, "c\"",
3278 	    ficlPrimitiveCountedStringQuoteIm, FICL_WORD_IMMEDIATE);
3279 	ficlDictionarySetPrimitive(dictionary, "hex", ficlPrimitiveHex,
3280 	    FICL_WORD_DEFAULT);
3281 	ficlDictionarySetPrimitive(dictionary, "pad", ficlPrimitivePad,
3282 	    FICL_WORD_DEFAULT);
3283 	ficlDictionarySetPrimitive(dictionary, "parse", ficlPrimitiveParse,
3284 	    FICL_WORD_DEFAULT);
3285 
3286 	/*
3287 	 * query restore-input save-input tib u.r u> unused
3288 	 * [FICL_VM_STATE_COMPILE]
3289 	 */
3290 	ficlDictionarySetPrimitive(dictionary, "refill", ficlPrimitiveRefill,
3291 	    FICL_WORD_DEFAULT);
3292 	ficlDictionarySetPrimitive(dictionary, "source-id",
3293 	    ficlPrimitiveSourceID, FICL_WORD_DEFAULT);
3294 	ficlDictionarySetPrimitive(dictionary, "to", ficlPrimitiveToValue,
3295 	    FICL_WORD_IMMEDIATE);
3296 	ficlDictionarySetPrimitive(dictionary, "value", ficlPrimitiveConstant,
3297 	    FICL_WORD_DEFAULT);
3298 	ficlDictionarySetPrimitive(dictionary, "\\", ficlPrimitiveBackslash,
3299 	    FICL_WORD_IMMEDIATE);
3300 
3301 	/*
3302 	 * Environment query values for the Core word set
3303 	 */
3304 	ficlDictionarySetConstant(environment, "/counted-string",
3305 	    FICL_COUNTED_STRING_MAX);
3306 	ficlDictionarySetConstant(environment, "/hold", FICL_PAD_SIZE);
3307 	ficlDictionarySetConstant(environment, "/pad", FICL_PAD_SIZE);
3308 	ficlDictionarySetConstant(environment, "address-unit-bits", 8);
3309 	ficlDictionarySetConstant(environment, "core", FICL_TRUE);
3310 	ficlDictionarySetConstant(environment, "core-ext", FICL_FALSE);
3311 	ficlDictionarySetConstant(environment, "floored", FICL_FALSE);
3312 	ficlDictionarySetConstant(environment, "max-char", UCHAR_MAX);
3313 	ficlDictionarySetConstant(environment, "max-n", LONG_MAX);
3314 	ficlDictionarySetConstant(environment, "max-u", ULONG_MAX);
3315 
3316 	{
3317 		ficl2Integer id;
3318 		ficlInteger low, high;
3319 
3320 		low = ULONG_MAX;
3321 		high = LONG_MAX;
3322 		FICL_2INTEGER_SET(high, low, id);
3323 		ficlDictionarySet2Constant(environment, "max-d", id);
3324 		high = ULONG_MAX;
3325 		FICL_2INTEGER_SET(high, low, id);
3326 		ficlDictionarySet2Constant(environment, "max-ud", id);
3327 	}
3328 
3329 	ficlDictionarySetConstant(environment, "return-stack-cells",
3330 	    FICL_DEFAULT_STACK_SIZE);
3331 	ficlDictionarySetConstant(environment, "stack-cells",
3332 	    FICL_DEFAULT_STACK_SIZE);
3333 
3334 	/*
3335 	 * The optional Double-Number word set (partial)
3336 	 */
3337 	ficlDictionarySetPrimitive(dictionary, "2constant",
3338 	    ficlPrimitive2Constant, FICL_WORD_IMMEDIATE);
3339 	ficlDictionarySetPrimitive(dictionary, "2literal",
3340 	    ficlPrimitive2LiteralIm, FICL_WORD_IMMEDIATE);
3341 	ficlDictionarySetPrimitive(dictionary, "2variable",
3342 	    ficlPrimitive2Variable, FICL_WORD_IMMEDIATE);
3343 	/*
3344 	 * D+ D- D. D.R D0< D0= D2* D2/ in softcore
3345 	 * D< D= D>S DABS DMAX DMIN DNEGATE in softcore
3346 	 * m-star-slash is TODO
3347 	 * M+ in softcore
3348 	 */
3349 
3350 	/*
3351 	 * DOUBLE EXT
3352 	 */
3353 	ficlDictionarySetPrimitive(dictionary, "2rot",
3354 	    ficlPrimitive2Rot, FICL_WORD_DEFAULT);
3355 	ficlDictionarySetPrimitive(dictionary, "2value",
3356 	    ficlPrimitive2Constant, FICL_WORD_IMMEDIATE);
3357 	/* du< in softcore */
3358 	/*
3359 	 * The optional Exception and Exception Extensions word set
3360 	 */
3361 	ficlDictionarySetPrimitive(dictionary, "catch", ficlPrimitiveCatch,
3362 	    FICL_WORD_DEFAULT);
3363 	ficlDictionarySetPrimitive(dictionary, "throw", ficlPrimitiveThrow,
3364 	    FICL_WORD_DEFAULT);
3365 
3366 	ficlDictionarySetConstant(environment, "exception", FICL_TRUE);
3367 	ficlDictionarySetConstant(environment, "exception-ext", FICL_TRUE);
3368 
3369 	/*
3370 	 * The optional Locals and Locals Extensions word set
3371 	 * see softcore.c for implementation of locals|
3372 	 */
3373 #if FICL_WANT_LOCALS
3374 	ficlDictionarySetPrimitive(dictionary, "doLocal",
3375 	    ficlPrimitiveDoLocalIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3376 	ficlDictionarySetPrimitive(dictionary, "(local)",
3377 	    ficlPrimitiveLocalParen, FICL_WORD_COMPILE_ONLY);
3378 	ficlDictionarySetPrimitive(dictionary, "(2local)",
3379 	    ficlPrimitive2LocalParen, FICL_WORD_COMPILE_ONLY);
3380 
3381 	ficlDictionarySetConstant(environment, "locals", FICL_TRUE);
3382 	ficlDictionarySetConstant(environment, "locals-ext", FICL_TRUE);
3383 	ficlDictionarySetConstant(environment, "#locals", FICL_MAX_LOCALS);
3384 #endif
3385 
3386 	/*
3387 	 * The optional Memory-Allocation word set
3388 	 */
3389 
3390 	ficlDictionarySetPrimitive(dictionary, "allocate",
3391 	    ficlPrimitiveAllocate, FICL_WORD_DEFAULT);
3392 	ficlDictionarySetPrimitive(dictionary, "free", ficlPrimitiveFree,
3393 	    FICL_WORD_DEFAULT);
3394 	ficlDictionarySetPrimitive(dictionary, "resize", ficlPrimitiveResize,
3395 	    FICL_WORD_DEFAULT);
3396 
3397 	ficlDictionarySetConstant(environment, "memory-alloc", FICL_TRUE);
3398 
3399 	/*
3400 	 * The optional Search-Order word set
3401 	 */
3402 	ficlSystemCompileSearch(system);
3403 
3404 	/*
3405 	 * The optional Programming-Tools and Programming-Tools
3406 	 * Extensions word set
3407 	 */
3408 	ficlSystemCompileTools(system);
3409 
3410 	/*
3411 	 * The optional File-Access and File-Access Extensions word set
3412 	 */
3413 #if FICL_WANT_FILE
3414 	ficlSystemCompileFile(system);
3415 #endif
3416 
3417 	/*
3418 	 * Ficl extras
3419 	 */
3420 	ficlDictionarySetPrimitive(dictionary, ".ver", ficlPrimitiveVersion,
3421 	    FICL_WORD_DEFAULT);
3422 	ficlDictionarySetPrimitive(dictionary, ">name", ficlPrimitiveToName,
3423 	    FICL_WORD_DEFAULT);
3424 	ficlDictionarySetPrimitive(dictionary, "add-parse-step",
3425 	    ficlPrimitiveAddParseStep, FICL_WORD_DEFAULT);
3426 	ficlDictionarySetPrimitive(dictionary, "body>", ficlPrimitiveFromBody,
3427 	    FICL_WORD_DEFAULT);
3428 	ficlDictionarySetPrimitive(dictionary, "compile-only",
3429 	    ficlPrimitiveCompileOnly, FICL_WORD_DEFAULT);
3430 	ficlDictionarySetPrimitive(dictionary, "endif", ficlPrimitiveEndifCoIm,
3431 	    FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3432 	ficlDictionarySetPrimitive(dictionary, "last-word",
3433 	    ficlPrimitiveLastWord, FICL_WORD_DEFAULT);
3434 	ficlDictionarySetPrimitive(dictionary, "hash", ficlPrimitiveHash,
3435 	    FICL_WORD_DEFAULT);
3436 	ficlDictionarySetPrimitive(dictionary, "objectify",
3437 	    ficlPrimitiveSetObjectFlag, FICL_WORD_DEFAULT);
3438 	ficlDictionarySetPrimitive(dictionary, "?object",
3439 	    ficlPrimitiveIsObject, FICL_WORD_DEFAULT);
3440 	ficlDictionarySetPrimitive(dictionary, "parse-word",
3441 	    ficlPrimitiveParseNoCopy, FICL_WORD_DEFAULT);
3442 	ficlDictionarySetPrimitive(dictionary, "sfind", ficlPrimitiveSFind,
3443 	    FICL_WORD_DEFAULT);
3444 	ficlDictionarySetPrimitive(dictionary, "sliteral",
3445 	    ficlPrimitiveSLiteralCoIm, FICL_WORD_COMPILE_ONLY_IMMEDIATE);
3446 	ficlDictionarySetPrimitive(dictionary, "sprintf", ficlPrimitiveSprintf,
3447 	    FICL_WORD_DEFAULT);
3448 	ficlDictionarySetPrimitive(dictionary, "strlen", ficlPrimitiveStrlen,
3449 	    FICL_WORD_DEFAULT);
3450 	ficlDictionarySetPrimitive(dictionary, "x.", ficlPrimitiveHexDot,
3451 	    FICL_WORD_DEFAULT);
3452 #if FICL_WANT_USER
3453 	ficlDictionarySetPrimitive(dictionary, "user", ficlPrimitiveUser,
3454 	    FICL_WORD_DEFAULT);
3455 #endif
3456 
3457 	/*
3458 	 * internal support words
3459 	 */
3460 	interpret = ficlDictionarySetPrimitive(dictionary, "interpret",
3461 	    ficlPrimitiveInterpret, FICL_WORD_DEFAULT);
3462 	ficlDictionarySetPrimitive(dictionary, "lookup", ficlPrimitiveLookup,
3463 	    FICL_WORD_DEFAULT);
3464 	ficlDictionarySetPrimitive(dictionary, "(parse-step)",
3465 	    ficlPrimitiveParseStepParen, FICL_WORD_DEFAULT);
3466 	system->exitInnerWord = ficlDictionarySetPrimitive(dictionary,
3467 	    "exit-inner", ficlPrimitiveExitInner, FICL_WORD_DEFAULT);
3468 
3469 	/*
3470 	 * Set constants representing the internal instruction words
3471 	 * If you want all of 'em, turn that "#if 0" to "#if 1".
3472 	 * By default you only get the numbers (fi0, fiNeg1, etc).
3473 	 */
3474 #define	FICL_TOKEN(token, description)	\
3475 	ficlDictionarySetConstant(dictionary, #token, token);
3476 #if 0
3477 #define	FICL_INSTRUCTION_TOKEN(token, description, flags)	\
3478 	ficlDictionarySetConstant(dictionary, #token, token);
3479 #else
3480 #define	FICL_INSTRUCTION_TOKEN(token, description, flags)
3481 #endif /* 0 */
3482 #include "ficltokens.h"
3483 #undef FICL_TOKEN
3484 #undef FICL_INSTRUCTION_TOKEN
3485 
3486 	/*
3487 	 * Set up system's outer interpreter loop - maybe this should
3488 	 * be in initSystem?
3489 	 */
3490 	system->interpreterLoop[0] = interpret;
3491 	system->interpreterLoop[1] = (ficlWord *)ficlInstructionBranchParen;
3492 	system->interpreterLoop[2] = (ficlWord *)(void *)(-2);
3493 
3494 	FICL_SYSTEM_ASSERT(system,
3495 	    ficlDictionaryCellsAvailable(dictionary) > 0);
3496 }
3497