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