1 /*
2 * v m . c
3 * Forth Inspired Command Language - virtual machine methods
4 * Author: John Sadler (john_sadler@alum.mit.edu)
5 * Created: 19 July 1997
6 * $Id: vm.c,v 1.17 2010/09/13 18:43:04 asau Exp $
7 */
8 /*
9 * This file implements the virtual machine of Ficl. Each virtual
10 * machine retains the state of an interpreter. A virtual machine
11 * owns a pair of stacks for parameters and return addresses, as
12 * well as a pile of state variables and the two dedicated registers
13 * of the interpreter.
14 */
15 /*
16 * Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
17 * All rights reserved.
18 *
19 * Get the latest Ficl release at http://ficl.sourceforge.net
20 *
21 * I am interested in hearing from anyone who uses Ficl. If you have
22 * a problem, a success story, a defect, an enhancement request, or
23 * if you would like to contribute to the Ficl release, please
24 * contact me by email at the address above.
25 *
26 * L I C E N S E and D I S C L A I M E R
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 */
49
50 #include "ficl.h"
51
52 #if FICL_ROBUST >= 2
53 #define FICL_VM_CHECK(vm) \
54 FICL_VM_ASSERT(vm, (*(vm->ip - 1)) == vm->runningWord)
55 #else
56 #define FICL_VM_CHECK(vm)
57 #endif
58
59 /*
60 * v m B r a n c h R e l a t i v e
61 */
62 void
ficlVmBranchRelative(ficlVm * vm,int offset)63 ficlVmBranchRelative(ficlVm *vm, int offset)
64 {
65 vm->ip += offset;
66 }
67
68 /*
69 * v m C r e a t e
70 * Creates a virtual machine either from scratch (if vm is NULL on entry)
71 * or by resizing and reinitializing an existing VM to the specified stack
72 * sizes.
73 */
74 ficlVm *
ficlVmCreate(ficlVm * vm,unsigned nPStack,unsigned nRStack)75 ficlVmCreate(ficlVm *vm, unsigned nPStack, unsigned nRStack)
76 {
77 if (vm == NULL) {
78 vm = (ficlVm *)ficlMalloc(sizeof (ficlVm));
79 FICL_ASSERT(NULL, vm);
80 memset(vm, 0, sizeof (ficlVm));
81 }
82
83 if (vm->dataStack)
84 ficlStackDestroy(vm->dataStack);
85 vm->dataStack = ficlStackCreate(vm, "data", nPStack);
86
87 if (vm->returnStack)
88 ficlStackDestroy(vm->returnStack);
89 vm->returnStack = ficlStackCreate(vm, "return", nRStack);
90
91 #if FICL_WANT_FLOAT
92 if (vm->floatStack)
93 ficlStackDestroy(vm->floatStack);
94 vm->floatStack = ficlStackCreate(vm, "float", nPStack);
95 #endif
96
97 ficlVmReset(vm);
98 return (vm);
99 }
100
101 /*
102 * v m D e l e t e
103 * Free all memory allocated to the specified VM and its subordinate
104 * structures.
105 */
106 void
ficlVmDestroy(ficlVm * vm)107 ficlVmDestroy(ficlVm *vm)
108 {
109 if (vm) {
110 ficlFree(vm->dataStack);
111 ficlFree(vm->returnStack);
112 #if FICL_WANT_FLOAT
113 ficlFree(vm->floatStack);
114 #endif
115 ficlFree(vm);
116 }
117 }
118
119 /*
120 * v m E x e c u t e
121 * Sets up the specified word to be run by the inner interpreter.
122 * Executes the word's code part immediately, but in the case of
123 * colon definition, the definition itself needs the inner interpreter
124 * to complete. This does not happen until control reaches ficlExec
125 */
126 void
ficlVmExecuteWord(ficlVm * vm,ficlWord * pWord)127 ficlVmExecuteWord(ficlVm *vm, ficlWord *pWord)
128 {
129 ficlVmInnerLoop(vm, pWord);
130 }
131
132 static void
ficlVmOptimizeJumpToJump(ficlVm * vm,ficlIp ip)133 ficlVmOptimizeJumpToJump(ficlVm *vm, ficlIp ip)
134 {
135 ficlIp destination;
136 switch ((ficlInstruction)(*ip)) {
137 case ficlInstructionBranchParenWithCheck:
138 *ip = (ficlWord *)ficlInstructionBranchParen;
139 goto RUNTIME_FIXUP;
140
141 case ficlInstructionBranch0ParenWithCheck:
142 *ip = (ficlWord *)ficlInstructionBranch0Paren;
143 RUNTIME_FIXUP:
144 ip++;
145 destination = ip + *(ficlInteger *)ip;
146 switch ((ficlInstruction)*destination) {
147 case ficlInstructionBranchParenWithCheck:
148 /* preoptimize where we're jumping to */
149 ficlVmOptimizeJumpToJump(vm, destination);
150 /* FALLTHROUGH */
151 case ficlInstructionBranchParen:
152 destination++;
153 destination += *(ficlInteger *)destination;
154 *ip = (ficlWord *)(destination - ip);
155 break;
156 }
157 }
158 }
159
160 /*
161 * v m I n n e r L o o p
162 * the mysterious inner interpreter...
163 * This loop is the address interpreter that makes colon definitions
164 * work. Upon entry, it assumes that the IP points to an entry in
165 * a definition (the body of a colon word). It runs one word at a time
166 * until something does vmThrow. The catcher for this is expected to exist
167 * in the calling code.
168 * vmThrow gets you out of this loop with a longjmp()
169 */
170
171 #if FICL_ROBUST <= 1
172 /* turn off stack checking for primitives */
173 #define _CHECK_STACK(stack, top, pop, push)
174 #else
175
176 #define _CHECK_STACK(stack, top, pop, push) \
177 ficlStackCheckNospill(stack, top, pop, push)
178
179 static FICL_PLATFORM_INLINE void
ficlStackCheckNospill(ficlStack * stack,ficlCell * top,int popCells,int pushCells)180 ficlStackCheckNospill(ficlStack *stack, ficlCell *top, int popCells,
181 int pushCells)
182 {
183 /*
184 * Why save and restore stack->top?
185 * So the simple act of stack checking doesn't force a "register" spill,
186 * which might mask bugs (places where we needed to spill but didn't).
187 * --lch
188 */
189 ficlCell *oldTop = stack->top;
190 stack->top = top;
191 ficlStackCheck(stack, popCells, pushCells);
192 stack->top = oldTop;
193 }
194
195 #endif /* FICL_ROBUST <= 1 */
196
197 #define CHECK_STACK(pop, push) \
198 _CHECK_STACK(vm->dataStack, dataTop, pop, push)
199 #define CHECK_FLOAT_STACK(pop, push) \
200 _CHECK_STACK(vm->floatStack, floatTop, pop, push)
201 #define CHECK_RETURN_STACK(pop, push) \
202 _CHECK_STACK(vm->returnStack, returnTop, pop, push)
203
204 #if FICL_WANT_FLOAT
205 #define FLOAT_LOCAL_VARIABLE_SPILL \
206 vm->floatStack->top = floatTop;
207 #define FLOAT_LOCAL_VARIABLE_REFILL \
208 floatTop = vm->floatStack->top;
209 #else
210 #define FLOAT_LOCAL_VARIABLE_SPILL
211 #define FLOAT_LOCAL_VARIABLE_REFILL
212 #endif /* FICL_WANT_FLOAT */
213
214 #if FICL_WANT_LOCALS
215 #define LOCALS_LOCAL_VARIABLE_SPILL \
216 vm->returnStack->frame = frame;
217 #define LOCALS_LOCAL_VARIABLE_REFILL \
218 frame = vm->returnStack->frame;
219 #else
220 #define LOCALS_LOCAL_VARIABLE_SPILL
221 #define LOCALS_LOCAL_VARIABLE_REFILL
222 #endif /* FICL_WANT_FLOAT */
223
224 #define LOCAL_VARIABLE_SPILL \
225 vm->ip = (ficlIp)ip; \
226 vm->dataStack->top = dataTop; \
227 vm->returnStack->top = returnTop; \
228 FLOAT_LOCAL_VARIABLE_SPILL \
229 LOCALS_LOCAL_VARIABLE_SPILL
230
231 #define LOCAL_VARIABLE_REFILL \
232 ip = (ficlInstruction *)vm->ip; \
233 dataTop = vm->dataStack->top; \
234 returnTop = vm->returnStack->top; \
235 FLOAT_LOCAL_VARIABLE_REFILL \
236 LOCALS_LOCAL_VARIABLE_REFILL
237
238 void
ficlVmInnerLoop(ficlVm * vm,ficlWord * fw)239 ficlVmInnerLoop(ficlVm *vm, ficlWord *fw)
240 {
241 register ficlInstruction *ip;
242 register ficlCell *dataTop;
243 register ficlCell *returnTop;
244 #if FICL_WANT_FLOAT
245 register ficlCell *floatTop;
246 ficlFloat f;
247 #endif /* FICL_WANT_FLOAT */
248 #if FICL_WANT_LOCALS
249 register ficlCell *frame;
250 #endif /* FICL_WANT_LOCALS */
251 jmp_buf *oldExceptionHandler;
252 jmp_buf exceptionHandler;
253 int except;
254 int once;
255 int count;
256 ficlInstruction instruction;
257 ficlInteger i;
258 ficlUnsigned u;
259 ficlCell c;
260 ficlCountedString *s;
261 ficlCell *cell;
262 char *cp;
263
264 once = (fw != NULL);
265 if (once)
266 count = 1;
267
268 oldExceptionHandler = vm->exceptionHandler;
269 /* This has to come before the setjmp! */
270 vm->exceptionHandler = &exceptionHandler;
271 except = setjmp(exceptionHandler);
272
273 LOCAL_VARIABLE_REFILL;
274
275 if (except) {
276 LOCAL_VARIABLE_SPILL;
277 vm->exceptionHandler = oldExceptionHandler;
278 ficlVmThrow(vm, except);
279 }
280
281 for (;;) {
282 if (once) {
283 if (!count--)
284 break;
285 instruction = (ficlInstruction)((void *)fw);
286 } else {
287 instruction = *ip++;
288 fw = (ficlWord *)instruction;
289 }
290
291 AGAIN:
292 switch (instruction) {
293 case ficlInstructionInvalid:
294 ficlVmThrowError(vm,
295 "Error: NULL instruction executed!");
296 return;
297
298 case ficlInstruction1:
299 case ficlInstruction2:
300 case ficlInstruction3:
301 case ficlInstruction4:
302 case ficlInstruction5:
303 case ficlInstruction6:
304 case ficlInstruction7:
305 case ficlInstruction8:
306 case ficlInstruction9:
307 case ficlInstruction10:
308 case ficlInstruction11:
309 case ficlInstruction12:
310 case ficlInstruction13:
311 case ficlInstruction14:
312 case ficlInstruction15:
313 case ficlInstruction16:
314 CHECK_STACK(0, 1);
315 (++dataTop)->i = instruction;
316 continue;
317
318 case ficlInstruction0:
319 case ficlInstructionNeg1:
320 case ficlInstructionNeg2:
321 case ficlInstructionNeg3:
322 case ficlInstructionNeg4:
323 case ficlInstructionNeg5:
324 case ficlInstructionNeg6:
325 case ficlInstructionNeg7:
326 case ficlInstructionNeg8:
327 case ficlInstructionNeg9:
328 case ficlInstructionNeg10:
329 case ficlInstructionNeg11:
330 case ficlInstructionNeg12:
331 case ficlInstructionNeg13:
332 case ficlInstructionNeg14:
333 case ficlInstructionNeg15:
334 case ficlInstructionNeg16:
335 CHECK_STACK(0, 1);
336 (++dataTop)->i = ficlInstruction0 - instruction;
337 continue;
338
339 /*
340 * stringlit: Fetch the count from the dictionary, then push
341 * the address and count on the stack. Finally, update ip to
342 * point to the first aligned address after the string text.
343 */
344 case ficlInstructionStringLiteralParen: {
345 ficlUnsigned8 length;
346 CHECK_STACK(0, 2);
347
348 s = (ficlCountedString *)(ip);
349 length = s->length;
350 cp = s->text;
351 (++dataTop)->p = cp;
352 (++dataTop)->i = length;
353
354 cp += length + 1;
355 cp = ficlAlignPointer(cp);
356 ip = (void *)cp;
357 continue;
358 }
359
360 case ficlInstructionCStringLiteralParen:
361 CHECK_STACK(0, 1);
362
363 s = (ficlCountedString *)(ip);
364 cp = s->text + s->length + 1;
365 cp = ficlAlignPointer(cp);
366 ip = (void *)cp;
367 (++dataTop)->p = s;
368 continue;
369
370 #if FICL_WANT_OPTIMIZE == FICL_OPTIMIZE_FOR_SIZE
371 #if FICL_WANT_FLOAT
372 FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC:
373 *++floatTop = cell[1];
374 /* intentional fall-through */
375 FLOAT_PUSH_CELL_POINTER_MINIPROC:
376 *++floatTop = cell[0];
377 continue;
378
379 FLOAT_POP_CELL_POINTER_MINIPROC:
380 cell[0] = *floatTop--;
381 continue;
382
383 FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC:
384 cell[0] = *floatTop--;
385 cell[1] = *floatTop--;
386 continue;
387
388 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
389 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
390 #define FLOAT_PUSH_CELL_POINTER(cp) \
391 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_MINIPROC
392 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
393 cell = (cp); goto FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
394 #define FLOAT_POP_CELL_POINTER(cp) \
395 cell = (cp); goto FLOAT_POP_CELL_POINTER_MINIPROC
396 #endif /* FICL_WANT_FLOAT */
397
398 /*
399 * Think of these as little mini-procedures.
400 * --lch
401 */
402 PUSH_CELL_POINTER_DOUBLE_MINIPROC:
403 *++dataTop = cell[1];
404 /* intentional fall-through */
405 PUSH_CELL_POINTER_MINIPROC:
406 *++dataTop = cell[0];
407 continue;
408
409 POP_CELL_POINTER_MINIPROC:
410 cell[0] = *dataTop--;
411 continue;
412 POP_CELL_POINTER_DOUBLE_MINIPROC:
413 cell[0] = *dataTop--;
414 cell[1] = *dataTop--;
415 continue;
416
417 #define PUSH_CELL_POINTER_DOUBLE(cp) \
418 cell = (cp); goto PUSH_CELL_POINTER_DOUBLE_MINIPROC
419 #define PUSH_CELL_POINTER(cp) \
420 cell = (cp); goto PUSH_CELL_POINTER_MINIPROC
421 #define POP_CELL_POINTER_DOUBLE(cp) \
422 cell = (cp); goto POP_CELL_POINTER_DOUBLE_MINIPROC
423 #define POP_CELL_POINTER(cp) \
424 cell = (cp); goto POP_CELL_POINTER_MINIPROC
425
426 BRANCH_MINIPROC:
427 ip += *(ficlInteger *)ip;
428 continue;
429
430 #define BRANCH() goto BRANCH_MINIPROC
431
432 EXIT_FUNCTION_MINIPROC:
433 ip = (ficlInstruction *)((returnTop--)->p);
434 continue;
435
436 #define EXIT_FUNCTION goto EXIT_FUNCTION_MINIPROC
437
438 #else /* FICL_WANT_SIZE */
439
440 #if FICL_WANT_FLOAT
441 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
442 cell = (cp); *++floatTop = cell[1]; *++floatTop = *cell; continue
443 #define FLOAT_PUSH_CELL_POINTER(cp) \
444 cell = (cp); *++floatTop = *cell; continue
445 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
446 cell = (cp); *cell = *floatTop--; cell[1] = *floatTop--; continue
447 #define FLOAT_POP_CELL_POINTER(cp) \
448 cell = (cp); *cell = *floatTop--; continue
449 #endif /* FICL_WANT_FLOAT */
450
451 #define PUSH_CELL_POINTER_DOUBLE(cp) \
452 cell = (cp); *++dataTop = cell[1]; *++dataTop = *cell; continue
453 #define PUSH_CELL_POINTER(cp) \
454 cell = (cp); *++dataTop = *cell; continue
455 #define POP_CELL_POINTER_DOUBLE(cp) \
456 cell = (cp); *cell = *dataTop--; cell[1] = *dataTop--; continue
457 #define POP_CELL_POINTER(cp) \
458 cell = (cp); *cell = *dataTop--; continue
459
460 #define BRANCH() ip += *(ficlInteger *)ip; continue
461 #define EXIT_FUNCTION() ip = (ficlInstruction *)((returnTop--)->p); continue
462
463 #endif /* FICL_WANT_SIZE */
464
465
466 /*
467 * This is the runtime for (literal). It assumes that it is
468 * part of a colon definition, and that the next ficlCell
469 * contains a value to be pushed on the parameter stack at
470 * runtime. This code is compiled by "literal".
471 */
472
473 case ficlInstructionLiteralParen:
474 CHECK_STACK(0, 1);
475 (++dataTop)->i = *ip++;
476 continue;
477
478 case ficlInstruction2LiteralParen:
479 CHECK_STACK(0, 2);
480 (++dataTop)->i = ip[1];
481 (++dataTop)->i = ip[0];
482 ip += 2;
483 continue;
484
485 #if FICL_WANT_LOCALS
486 /*
487 * Link a frame on the return stack, reserving nCells of space
488 * for locals - the value of nCells is the next ficlCell in
489 * the instruction stream.
490 * 1) Push frame onto returnTop
491 * 2) frame = returnTop
492 * 3) returnTop += nCells
493 */
494 case ficlInstructionLinkParen: {
495 ficlInteger nCells = *ip++;
496 (++returnTop)->p = frame;
497 frame = returnTop + 1;
498 returnTop += nCells;
499 continue;
500 }
501
502 /*
503 * Unink a stack frame previously created by stackLink
504 * 1) dataTop = frame
505 * 2) frame = pop()
506 */
507 case ficlInstructionUnlinkParen:
508 returnTop = frame - 1;
509 frame = (returnTop--)->p;
510 continue;
511
512 /*
513 * Immediate - cfa of a local while compiling - when executed,
514 * compiles code to fetch the value of a local given the
515 * local's index in the word's pfa
516 */
517 #if FICL_WANT_FLOAT
518 case ficlInstructionGetF2LocalParen:
519 FLOAT_PUSH_CELL_POINTER_DOUBLE(frame + *ip++);
520
521 case ficlInstructionGetFLocalParen:
522 FLOAT_PUSH_CELL_POINTER(frame + *ip++);
523
524 case ficlInstructionToF2LocalParen:
525 FLOAT_POP_CELL_POINTER_DOUBLE(frame + *ip++);
526
527 case ficlInstructionToFLocalParen:
528 FLOAT_POP_CELL_POINTER(frame + *ip++);
529 #endif /* FICL_WANT_FLOAT */
530
531 case ficlInstructionGet2LocalParen:
532 PUSH_CELL_POINTER_DOUBLE(frame + *ip++);
533
534 case ficlInstructionGetLocalParen:
535 PUSH_CELL_POINTER(frame + *ip++);
536
537 /*
538 * Immediate - cfa of a local while compiling - when executed,
539 * compiles code to store the value of a local given the
540 * local's index in the word's pfa
541 */
542
543 case ficlInstructionTo2LocalParen:
544 POP_CELL_POINTER_DOUBLE(frame + *ip++);
545
546 case ficlInstructionToLocalParen:
547 POP_CELL_POINTER(frame + *ip++);
548
549 /*
550 * Silly little minor optimizations.
551 * --lch
552 */
553 case ficlInstructionGetLocal0:
554 PUSH_CELL_POINTER(frame);
555
556 case ficlInstructionGetLocal1:
557 PUSH_CELL_POINTER(frame + 1);
558
559 case ficlInstructionGet2Local0:
560 PUSH_CELL_POINTER_DOUBLE(frame);
561
562 case ficlInstructionToLocal0:
563 POP_CELL_POINTER(frame);
564
565 case ficlInstructionToLocal1:
566 POP_CELL_POINTER(frame + 1);
567
568 case ficlInstructionTo2Local0:
569 POP_CELL_POINTER_DOUBLE(frame);
570
571 #endif /* FICL_WANT_LOCALS */
572
573 case ficlInstructionPlus:
574 CHECK_STACK(2, 1);
575 i = (dataTop--)->i;
576 dataTop->i += i;
577 continue;
578
579 case ficlInstructionMinus:
580 CHECK_STACK(2, 1);
581 i = (dataTop--)->i;
582 dataTop->i -= i;
583 continue;
584
585 case ficlInstruction1Plus:
586 CHECK_STACK(1, 1);
587 dataTop->i++;
588 continue;
589
590 case ficlInstruction1Minus:
591 CHECK_STACK(1, 1);
592 dataTop->i--;
593 continue;
594
595 case ficlInstruction2Plus:
596 CHECK_STACK(1, 1);
597 dataTop->i += 2;
598 continue;
599
600 case ficlInstruction2Minus:
601 CHECK_STACK(1, 1);
602 dataTop->i -= 2;
603 continue;
604
605 case ficlInstructionDup: {
606 ficlInteger i = dataTop->i;
607 CHECK_STACK(0, 1);
608 (++dataTop)->i = i;
609 continue;
610 }
611
612 case ficlInstructionQuestionDup:
613 CHECK_STACK(1, 2);
614
615 if (dataTop->i != 0) {
616 dataTop[1] = dataTop[0];
617 dataTop++;
618 }
619
620 continue;
621
622 case ficlInstructionSwap: {
623 ficlCell swap;
624 CHECK_STACK(2, 2);
625 swap = dataTop[0];
626 dataTop[0] = dataTop[-1];
627 dataTop[-1] = swap;
628 }
629 continue;
630
631 case ficlInstructionDrop:
632 CHECK_STACK(1, 0);
633 dataTop--;
634 continue;
635
636 case ficlInstruction2Drop:
637 CHECK_STACK(2, 0);
638 dataTop -= 2;
639 continue;
640
641 case ficlInstruction2Dup:
642 CHECK_STACK(2, 4);
643 dataTop[1] = dataTop[-1];
644 dataTop[2] = *dataTop;
645 dataTop += 2;
646 continue;
647
648 case ficlInstructionOver:
649 CHECK_STACK(2, 3);
650 dataTop[1] = dataTop[-1];
651 dataTop++;
652 continue;
653
654 case ficlInstruction2Over:
655 CHECK_STACK(4, 6);
656 dataTop[1] = dataTop[-3];
657 dataTop[2] = dataTop[-2];
658 dataTop += 2;
659 continue;
660
661 case ficlInstructionPick:
662 CHECK_STACK(1, 0);
663 i = dataTop->i;
664 if (i < 0)
665 continue;
666 CHECK_STACK(i + 2, i + 3);
667 *dataTop = dataTop[-i - 1];
668 continue;
669
670 /*
671 * Do stack rot.
672 * rot ( 1 2 3 -- 2 3 1 )
673 */
674 case ficlInstructionRot:
675 i = 2;
676 goto ROLL;
677
678 /*
679 * Do stack roll.
680 * roll ( n -- )
681 */
682 case ficlInstructionRoll:
683 CHECK_STACK(1, 0);
684 i = (dataTop--)->i;
685
686 if (i < 1)
687 continue;
688
689 ROLL:
690 CHECK_STACK(i+1, i+2);
691 c = dataTop[-i];
692 memmove(dataTop - i, dataTop - (i - 1),
693 i * sizeof (ficlCell));
694 *dataTop = c;
695 continue;
696
697 /*
698 * Do stack -rot.
699 * -rot ( 1 2 3 -- 3 1 2 )
700 */
701 case ficlInstructionMinusRot:
702 i = 2;
703 goto MINUSROLL;
704
705 /*
706 * Do stack -roll.
707 * -roll ( n -- )
708 */
709 case ficlInstructionMinusRoll:
710 CHECK_STACK(1, 0);
711 i = (dataTop--)->i;
712
713 if (i < 1)
714 continue;
715
716 MINUSROLL:
717 CHECK_STACK(i+1, i+2);
718 c = *dataTop;
719 memmove(dataTop - (i - 1), dataTop - i,
720 i * sizeof (ficlCell));
721 dataTop[-i] = c;
722
723 continue;
724
725 /*
726 * Do stack 2swap
727 * 2swap ( 1 2 3 4 -- 3 4 1 2 )
728 */
729 case ficlInstruction2Swap: {
730 ficlCell c2;
731 CHECK_STACK(4, 4);
732
733 c = *dataTop;
734 c2 = dataTop[-1];
735
736 *dataTop = dataTop[-2];
737 dataTop[-1] = dataTop[-3];
738
739 dataTop[-2] = c;
740 dataTop[-3] = c2;
741 continue;
742 }
743
744 case ficlInstructionPlusStore: {
745 ficlCell *cell;
746 CHECK_STACK(2, 0);
747 cell = (ficlCell *)(dataTop--)->p;
748 cell->i += (dataTop--)->i;
749 continue;
750 }
751
752 case ficlInstructionQuadFetch: {
753 ficlUnsigned32 *integer32;
754 CHECK_STACK(1, 1);
755 integer32 = (ficlUnsigned32 *)dataTop->i;
756 dataTop->u = (ficlUnsigned)*integer32;
757 continue;
758 }
759
760 case ficlInstructionQuadStore: {
761 ficlUnsigned32 *integer32;
762 CHECK_STACK(2, 0);
763 integer32 = (ficlUnsigned32 *)(dataTop--)->p;
764 *integer32 = (ficlUnsigned32)((dataTop--)->u);
765 continue;
766 }
767
768 case ficlInstructionWFetch: {
769 ficlUnsigned16 *integer16;
770 CHECK_STACK(1, 1);
771 integer16 = (ficlUnsigned16 *)dataTop->p;
772 dataTop->u = ((ficlUnsigned)*integer16);
773 continue;
774 }
775
776 case ficlInstructionWStore: {
777 ficlUnsigned16 *integer16;
778 CHECK_STACK(2, 0);
779 integer16 = (ficlUnsigned16 *)(dataTop--)->p;
780 *integer16 = (ficlUnsigned16)((dataTop--)->u);
781 continue;
782 }
783
784 case ficlInstructionCFetch: {
785 ficlUnsigned8 *integer8;
786 CHECK_STACK(1, 1);
787 integer8 = (ficlUnsigned8 *)dataTop->p;
788 dataTop->u = ((ficlUnsigned)*integer8);
789 continue;
790 }
791
792 case ficlInstructionCStore: {
793 ficlUnsigned8 *integer8;
794 CHECK_STACK(2, 0);
795 integer8 = (ficlUnsigned8 *)(dataTop--)->p;
796 *integer8 = (ficlUnsigned8)((dataTop--)->u);
797 continue;
798 }
799
800
801 /*
802 * l o g i c a n d c o m p a r i s o n s
803 */
804
805 case ficlInstruction0Equals:
806 CHECK_STACK(1, 1);
807 dataTop->i = FICL_BOOL(dataTop->i == 0);
808 continue;
809
810 case ficlInstruction0Less:
811 CHECK_STACK(1, 1);
812 dataTop->i = FICL_BOOL(dataTop->i < 0);
813 continue;
814
815 case ficlInstruction0Greater:
816 CHECK_STACK(1, 1);
817 dataTop->i = FICL_BOOL(dataTop->i > 0);
818 continue;
819
820 case ficlInstructionEquals:
821 CHECK_STACK(2, 1);
822 i = (dataTop--)->i;
823 dataTop->i = FICL_BOOL(dataTop->i == i);
824 continue;
825
826 case ficlInstructionLess:
827 CHECK_STACK(2, 1);
828 i = (dataTop--)->i;
829 dataTop->i = FICL_BOOL(dataTop->i < i);
830 continue;
831
832 case ficlInstructionULess:
833 CHECK_STACK(2, 1);
834 u = (dataTop--)->u;
835 dataTop->i = FICL_BOOL(dataTop->u < u);
836 continue;
837
838 case ficlInstructionAnd:
839 CHECK_STACK(2, 1);
840 i = (dataTop--)->i;
841 dataTop->i = dataTop->i & i;
842 continue;
843
844 case ficlInstructionOr:
845 CHECK_STACK(2, 1);
846 i = (dataTop--)->i;
847 dataTop->i = dataTop->i | i;
848 continue;
849
850 case ficlInstructionXor:
851 CHECK_STACK(2, 1);
852 i = (dataTop--)->i;
853 dataTop->i = dataTop->i ^ i;
854 continue;
855
856 case ficlInstructionInvert:
857 CHECK_STACK(1, 1);
858 dataTop->i = ~dataTop->i;
859 continue;
860
861 /*
862 * r e t u r n s t a c k
863 */
864 case ficlInstructionToRStack:
865 CHECK_STACK(1, 0);
866 CHECK_RETURN_STACK(0, 1);
867 *++returnTop = *dataTop--;
868 continue;
869
870 case ficlInstructionFromRStack:
871 CHECK_STACK(0, 1);
872 CHECK_RETURN_STACK(1, 0);
873 *++dataTop = *returnTop--;
874 continue;
875
876 case ficlInstructionFetchRStack:
877 CHECK_STACK(0, 1);
878 CHECK_RETURN_STACK(1, 1);
879 *++dataTop = *returnTop;
880 continue;
881
882 case ficlInstruction2ToR:
883 CHECK_STACK(2, 0);
884 CHECK_RETURN_STACK(0, 2);
885 *++returnTop = dataTop[-1];
886 *++returnTop = dataTop[0];
887 dataTop -= 2;
888 continue;
889
890 case ficlInstruction2RFrom:
891 CHECK_STACK(0, 2);
892 CHECK_RETURN_STACK(2, 0);
893 *++dataTop = returnTop[-1];
894 *++dataTop = returnTop[0];
895 returnTop -= 2;
896 continue;
897
898 case ficlInstruction2RFetch:
899 CHECK_STACK(0, 2);
900 CHECK_RETURN_STACK(2, 2);
901 *++dataTop = returnTop[-1];
902 *++dataTop = returnTop[0];
903 continue;
904
905 /*
906 * f i l l
907 * CORE ( c-addr u char -- )
908 * If u is greater than zero, store char in each of u
909 * consecutive characters of memory beginning at c-addr.
910 */
911 case ficlInstructionFill: {
912 char c;
913 char *memory;
914 CHECK_STACK(3, 0);
915 c = (char)(dataTop--)->i;
916 u = (dataTop--)->u;
917 memory = (char *)(dataTop--)->p;
918
919 /*
920 * memset() is faster than the previous hand-rolled
921 * solution. --lch
922 */
923 memset(memory, c, u);
924 continue;
925 }
926
927 /*
928 * l s h i f t
929 * l-shift CORE ( x1 u -- x2 )
930 * Perform a logical left shift of u bit-places on x1,
931 * giving x2. Put zeroes into the least significant bits
932 * vacated by the shift. An ambiguous condition exists if
933 * u is greater than or equal to the number of bits in a
934 * ficlCell.
935 *
936 * r-shift CORE ( x1 u -- x2 )
937 * Perform a logical right shift of u bit-places on x1,
938 * giving x2. Put zeroes into the most significant bits
939 * vacated by the shift. An ambiguous condition exists
940 * if u is greater than or equal to the number of bits
941 * in a ficlCell.
942 */
943 case ficlInstructionLShift: {
944 ficlUnsigned nBits;
945 ficlUnsigned x1;
946 CHECK_STACK(2, 1);
947
948 nBits = (dataTop--)->u;
949 x1 = dataTop->u;
950 dataTop->u = x1 << nBits;
951 continue;
952 }
953
954 case ficlInstructionRShift: {
955 ficlUnsigned nBits;
956 ficlUnsigned x1;
957 CHECK_STACK(2, 1);
958
959 nBits = (dataTop--)->u;
960 x1 = dataTop->u;
961 dataTop->u = x1 >> nBits;
962 continue;
963 }
964
965 /*
966 * m a x & m i n
967 */
968 case ficlInstructionMax: {
969 ficlInteger n2;
970 ficlInteger n1;
971 CHECK_STACK(2, 1);
972
973 n2 = (dataTop--)->i;
974 n1 = dataTop->i;
975
976 dataTop->i = ((n1 > n2) ? n1 : n2);
977 continue;
978 }
979
980 case ficlInstructionMin: {
981 ficlInteger n2;
982 ficlInteger n1;
983 CHECK_STACK(2, 1);
984
985 n2 = (dataTop--)->i;
986 n1 = dataTop->i;
987
988 dataTop->i = ((n1 < n2) ? n1 : n2);
989 continue;
990 }
991
992 /*
993 * m o v e
994 * CORE ( addr1 addr2 u -- )
995 * If u is greater than zero, copy the contents of u
996 * consecutive address units at addr1 to the u consecutive
997 * address units at addr2. After MOVE completes, the u
998 * consecutive address units at addr2 contain exactly
999 * what the u consecutive address units at addr1 contained
1000 * before the move.
1001 * NOTE! This implementation assumes that a char is the same
1002 * size as an address unit.
1003 */
1004 case ficlInstructionMove: {
1005 ficlUnsigned u;
1006 char *addr2;
1007 char *addr1;
1008 CHECK_STACK(3, 0);
1009
1010 u = (dataTop--)->u;
1011 addr2 = (dataTop--)->p;
1012 addr1 = (dataTop--)->p;
1013
1014 if (u == 0)
1015 continue;
1016 /*
1017 * Do the copy carefully, so as to be
1018 * correct even if the two ranges overlap
1019 */
1020 /* Which ANSI C's memmove() does for you! Yay! --lch */
1021 memmove(addr2, addr1, u);
1022 continue;
1023 }
1024
1025 /*
1026 * s t o d
1027 * s-to-d CORE ( n -- d )
1028 * Convert the number n to the double-ficlCell number d with
1029 * the same numerical value.
1030 */
1031 case ficlInstructionSToD: {
1032 ficlInteger s;
1033 CHECK_STACK(1, 2);
1034
1035 s = dataTop->i;
1036
1037 /* sign extend to 64 bits.. */
1038 (++dataTop)->i = (s < 0) ? -1 : 0;
1039 continue;
1040 }
1041
1042 /*
1043 * c o m p a r e
1044 * STRING ( c-addr1 u1 c-addr2 u2 -- n )
1045 * Compare the string specified by c-addr1 u1 to the string
1046 * specified by c-addr2 u2. The strings are compared, beginning
1047 * at the given addresses, character by character, up to the
1048 * length of the shorter string or until a difference is found.
1049 * If the two strings are identical, n is zero. If the two
1050 * strings are identical up to the length of the shorter string,
1051 * n is minus-one (-1) if u1 is less than u2 and one (1)
1052 * otherwise. If the two strings are not identical up to the
1053 * length of the shorter string, n is minus-one (-1) if the
1054 * first non-matching character in the string specified by
1055 * c-addr1 u1 has a lesser numeric value than the corresponding
1056 * character in the string specified by c-addr2 u2 and
1057 * one (1) otherwise.
1058 */
1059 case ficlInstructionCompare:
1060 i = FICL_FALSE;
1061 goto COMPARE;
1062
1063
1064 case ficlInstructionCompareInsensitive:
1065 i = FICL_TRUE;
1066 goto COMPARE;
1067
1068 COMPARE:
1069 {
1070 char *cp1, *cp2;
1071 ficlUnsigned u1, u2, uMin;
1072 int n = 0;
1073
1074 CHECK_STACK(4, 1);
1075 u2 = (dataTop--)->u;
1076 cp2 = (char *)(dataTop--)->p;
1077 u1 = (dataTop--)->u;
1078 cp1 = (char *)(dataTop--)->p;
1079
1080 uMin = (u1 < u2)? u1 : u2;
1081 for (; (uMin > 0) && (n == 0); uMin--) {
1082 int c1 = (unsigned char)*cp1++;
1083 int c2 = (unsigned char)*cp2++;
1084
1085 if (i) {
1086 c1 = tolower(c1);
1087 c2 = tolower(c2);
1088 }
1089 n = (c1 - c2);
1090 }
1091
1092 if (n == 0)
1093 n = (int)(u1 - u2);
1094
1095 if (n < 0)
1096 n = -1;
1097 else if (n > 0)
1098 n = 1;
1099
1100 (++dataTop)->i = n;
1101 continue;
1102 }
1103
1104 /*
1105 * r a n d o m
1106 * Ficl-specific
1107 */
1108 case ficlInstructionRandom:
1109 (++dataTop)->i = random();
1110 continue;
1111
1112 /*
1113 * s e e d - r a n d o m
1114 * Ficl-specific
1115 */
1116 case ficlInstructionSeedRandom:
1117 srandom((dataTop--)->i);
1118 continue;
1119
1120 case ficlInstructionGreaterThan: {
1121 ficlInteger x, y;
1122 CHECK_STACK(2, 1);
1123 y = (dataTop--)->i;
1124 x = dataTop->i;
1125 dataTop->i = FICL_BOOL(x > y);
1126 continue;
1127
1128 case ficlInstructionUGreaterThan:
1129 CHECK_STACK(2, 1);
1130 u = (dataTop--)->u;
1131 dataTop->i = FICL_BOOL(dataTop->u > u);
1132 continue;
1133
1134 }
1135
1136 /*
1137 * This function simply pops the previous instruction
1138 * pointer and returns to the "next" loop. Used for exiting
1139 * from within a definition. Note that exitParen is identical
1140 * to semiParen - they are in two different functions so that
1141 * "see" can correctly identify the end of a colon definition,
1142 * even if it uses "exit".
1143 */
1144 case ficlInstructionExitParen:
1145 case ficlInstructionSemiParen:
1146 EXIT_FUNCTION();
1147
1148 /*
1149 * The first time we run "(branch)", perform a "peephole
1150 * optimization" to see if we're jumping to another
1151 * unconditional jump. If so, just jump directly there.
1152 */
1153 case ficlInstructionBranchParenWithCheck:
1154 LOCAL_VARIABLE_SPILL;
1155 ficlVmOptimizeJumpToJump(vm, vm->ip - 1);
1156 LOCAL_VARIABLE_REFILL;
1157 goto BRANCH_PAREN;
1158
1159 /*
1160 * Same deal with branch0.
1161 */
1162 case ficlInstructionBranch0ParenWithCheck:
1163 LOCAL_VARIABLE_SPILL;
1164 ficlVmOptimizeJumpToJump(vm, vm->ip - 1);
1165 LOCAL_VARIABLE_REFILL;
1166 /* intentional fall-through */
1167
1168 /*
1169 * Runtime code for "(branch0)"; pop a flag from the stack,
1170 * branch if 0. fall through otherwise.
1171 * The heart of "if" and "until".
1172 */
1173 case ficlInstructionBranch0Paren:
1174 CHECK_STACK(1, 0);
1175
1176 if ((dataTop--)->i) {
1177 /*
1178 * don't branch, but skip over branch
1179 * relative address
1180 */
1181 ip += 1;
1182 continue;
1183 }
1184 /* otherwise, take branch (to else/endif/begin) */
1185 /* intentional fall-through! */
1186
1187 /*
1188 * Runtime for "(branch)" -- expects a literal offset in the
1189 * next compilation address, and branches to that location.
1190 */
1191 case ficlInstructionBranchParen:
1192 BRANCH_PAREN:
1193 BRANCH();
1194
1195 case ficlInstructionOfParen: {
1196 ficlUnsigned a, b;
1197
1198 CHECK_STACK(2, 1);
1199
1200 a = (dataTop--)->u;
1201 b = dataTop->u;
1202
1203 if (a == b) {
1204 /* fall through */
1205 ip++;
1206 /* remove CASE argument */
1207 dataTop--;
1208 } else {
1209 /* take branch to next of or endcase */
1210 BRANCH();
1211 }
1212
1213 continue;
1214 }
1215
1216 case ficlInstructionDoParen: {
1217 ficlCell index, limit;
1218
1219 CHECK_STACK(2, 0);
1220
1221 index = *dataTop--;
1222 limit = *dataTop--;
1223
1224 /* copy "leave" target addr to stack */
1225 (++returnTop)->i = *(ip++);
1226 *++returnTop = limit;
1227 *++returnTop = index;
1228
1229 continue;
1230 }
1231
1232 case ficlInstructionQDoParen: {
1233 ficlCell index, limit, leave;
1234
1235 CHECK_STACK(2, 0);
1236
1237 index = *dataTop--;
1238 limit = *dataTop--;
1239
1240 leave.i = *ip;
1241
1242 if (limit.u == index.u) {
1243 ip = leave.p;
1244 } else {
1245 ip++;
1246 *++returnTop = leave;
1247 *++returnTop = limit;
1248 *++returnTop = index;
1249 }
1250
1251 continue;
1252 }
1253
1254 case ficlInstructionLoopParen:
1255 case ficlInstructionPlusLoopParen: {
1256 ficlInteger index;
1257 ficlInteger limit;
1258 int direction = 0;
1259
1260 index = returnTop->i;
1261 limit = returnTop[-1].i;
1262
1263 if (instruction == ficlInstructionLoopParen)
1264 index++;
1265 else {
1266 ficlInteger increment;
1267 CHECK_STACK(1, 0);
1268 increment = (dataTop--)->i;
1269 index += increment;
1270 direction = (increment < 0);
1271 }
1272
1273 if (direction ^ (index >= limit)) {
1274 /* nuke the loop indices & "leave" addr */
1275 returnTop -= 3;
1276 ip++; /* fall through the loop */
1277 } else { /* update index, branch to loop head */
1278 returnTop->i = index;
1279 BRANCH();
1280 }
1281
1282 continue;
1283 }
1284
1285
1286 /*
1287 * Runtime code to break out of a do..loop construct
1288 * Drop the loop control variables; the branch address
1289 * past "loop" is next on the return stack.
1290 */
1291 case ficlInstructionLeave:
1292 /* almost unloop */
1293 returnTop -= 2;
1294 /* exit */
1295 EXIT_FUNCTION();
1296
1297 case ficlInstructionUnloop:
1298 returnTop -= 3;
1299 continue;
1300
1301 case ficlInstructionI:
1302 *++dataTop = *returnTop;
1303 continue;
1304
1305 case ficlInstructionJ:
1306 *++dataTop = returnTop[-3];
1307 continue;
1308
1309 case ficlInstructionK:
1310 *++dataTop = returnTop[-6];
1311 continue;
1312
1313 case ficlInstructionDoesParen: {
1314 ficlDictionary *dictionary = ficlVmGetDictionary(vm);
1315 dictionary->smudge->code =
1316 (ficlPrimitive)ficlInstructionDoDoes;
1317 dictionary->smudge->param[0].p = ip;
1318 ip = (ficlInstruction *)((returnTop--)->p);
1319 continue;
1320 }
1321
1322 case ficlInstructionDoDoes: {
1323 ficlCell *cell;
1324 ficlIp tempIP;
1325
1326 CHECK_STACK(0, 1);
1327
1328 cell = fw->param;
1329 tempIP = (ficlIp)((*cell).p);
1330 (++dataTop)->p = (cell + 1);
1331 (++returnTop)->p = (void *)ip;
1332 ip = (ficlInstruction *)tempIP;
1333 continue;
1334 }
1335
1336 #if FICL_WANT_FLOAT
1337 case ficlInstructionF2Fetch:
1338 CHECK_FLOAT_STACK(0, 2);
1339 CHECK_STACK(1, 0);
1340 FLOAT_PUSH_CELL_POINTER_DOUBLE((dataTop--)->p);
1341
1342 case ficlInstructionFFetch:
1343 CHECK_FLOAT_STACK(0, 1);
1344 CHECK_STACK(1, 0);
1345 FLOAT_PUSH_CELL_POINTER((dataTop--)->p);
1346
1347 case ficlInstructionF2Store:
1348 CHECK_FLOAT_STACK(2, 0);
1349 CHECK_STACK(1, 0);
1350 FLOAT_POP_CELL_POINTER_DOUBLE((dataTop--)->p);
1351
1352 case ficlInstructionFStore:
1353 CHECK_FLOAT_STACK(1, 0);
1354 CHECK_STACK(1, 0);
1355 FLOAT_POP_CELL_POINTER((dataTop--)->p);
1356 #endif /* FICL_WANT_FLOAT */
1357
1358 /*
1359 * two-fetch CORE ( a-addr -- x1 x2 )
1360 *
1361 * Fetch the ficlCell pair x1 x2 stored at a-addr.
1362 * x2 is stored at a-addr and x1 at the next consecutive
1363 * ficlCell. It is equivalent to the sequence
1364 * DUP ficlCell+ @ SWAP @ .
1365 */
1366 case ficlInstruction2Fetch:
1367 CHECK_STACK(1, 2);
1368 PUSH_CELL_POINTER_DOUBLE((dataTop--)->p);
1369
1370 /*
1371 * fetch CORE ( a-addr -- x )
1372 *
1373 * x is the value stored at a-addr.
1374 */
1375 case ficlInstructionFetch:
1376 CHECK_STACK(1, 1);
1377 PUSH_CELL_POINTER((dataTop--)->p);
1378
1379 /*
1380 * two-store CORE ( x1 x2 a-addr -- )
1381 * Store the ficlCell pair x1 x2 at a-addr, with x2 at a-addr
1382 * and x1 at the next consecutive ficlCell. It is equivalent
1383 * to the sequence SWAP OVER ! ficlCell+ !
1384 */
1385 case ficlInstruction2Store:
1386 CHECK_STACK(3, 0);
1387 POP_CELL_POINTER_DOUBLE((dataTop--)->p);
1388
1389 /*
1390 * store CORE ( x a-addr -- )
1391 * Store x at a-addr.
1392 */
1393 case ficlInstructionStore:
1394 CHECK_STACK(2, 0);
1395 POP_CELL_POINTER((dataTop--)->p);
1396
1397 case ficlInstructionComma: {
1398 ficlDictionary *dictionary;
1399 CHECK_STACK(1, 0);
1400
1401 dictionary = ficlVmGetDictionary(vm);
1402 ficlDictionaryAppendCell(dictionary, *dataTop--);
1403 continue;
1404 }
1405
1406 case ficlInstructionCComma: {
1407 ficlDictionary *dictionary;
1408 char c;
1409 CHECK_STACK(1, 0);
1410
1411 dictionary = ficlVmGetDictionary(vm);
1412 c = (char)(dataTop--)->i;
1413 ficlDictionaryAppendCharacter(dictionary, c);
1414 continue;
1415 }
1416
1417 case ficlInstructionCells:
1418 CHECK_STACK(1, 1);
1419 dataTop->i *= sizeof (ficlCell);
1420 continue;
1421
1422 case ficlInstructionCellPlus:
1423 CHECK_STACK(1, 1);
1424 dataTop->i += sizeof (ficlCell);
1425 continue;
1426
1427 case ficlInstructionStar:
1428 CHECK_STACK(2, 1);
1429 i = (dataTop--)->i;
1430 dataTop->i *= i;
1431 continue;
1432
1433 case ficlInstructionNegate:
1434 CHECK_STACK(1, 1);
1435 dataTop->i = - dataTop->i;
1436 continue;
1437
1438 case ficlInstructionSlash:
1439 CHECK_STACK(2, 1);
1440 i = (dataTop--)->i;
1441 dataTop->i /= i;
1442 continue;
1443
1444 /*
1445 * slash-mod CORE ( n1 n2 -- n3 n4 )
1446 * Divide n1 by n2, giving the single-ficlCell remainder n3
1447 * and the single-ficlCell quotient n4. An ambiguous condition
1448 * exists if n2 is zero. If n1 and n2 differ in sign, the
1449 * implementation-defined result returned will be the
1450 * same as that returned by either the phrase
1451 * >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM.
1452 * NOTE: Ficl complies with the second phrase
1453 * (symmetric division)
1454 */
1455 case ficlInstructionSlashMod: {
1456 ficl2Integer n1;
1457 ficlInteger n2;
1458 ficl2IntegerQR qr;
1459
1460 CHECK_STACK(2, 2);
1461 n2 = dataTop[0].i;
1462 FICL_INTEGER_TO_2INTEGER(dataTop[-1].i, n1);
1463
1464 qr = ficl2IntegerDivideSymmetric(n1, n2);
1465 dataTop[-1].i = qr.remainder;
1466 dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient);
1467 continue;
1468 }
1469
1470 case ficlInstruction2Star:
1471 CHECK_STACK(1, 1);
1472 dataTop->i <<= 1;
1473 continue;
1474
1475 case ficlInstruction2Slash:
1476 CHECK_STACK(1, 1);
1477 dataTop->i >>= 1;
1478 continue;
1479
1480 case ficlInstructionStarSlash: {
1481 ficlInteger x, y, z;
1482 ficl2Integer prod;
1483 CHECK_STACK(3, 1);
1484
1485 z = (dataTop--)->i;
1486 y = (dataTop--)->i;
1487 x = dataTop->i;
1488
1489 prod = ficl2IntegerMultiply(x, y);
1490 dataTop->i = FICL_2UNSIGNED_GET_LOW(
1491 ficl2IntegerDivideSymmetric(prod, z).quotient);
1492 continue;
1493 }
1494
1495 case ficlInstructionStarSlashMod: {
1496 ficlInteger x, y, z;
1497 ficl2Integer prod;
1498 ficl2IntegerQR qr;
1499
1500 CHECK_STACK(3, 2);
1501
1502 z = (dataTop--)->i;
1503 y = dataTop[0].i;
1504 x = dataTop[-1].i;
1505
1506 prod = ficl2IntegerMultiply(x, y);
1507 qr = ficl2IntegerDivideSymmetric(prod, z);
1508
1509 dataTop[-1].i = qr.remainder;
1510 dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient);
1511 continue;
1512 }
1513
1514 #if FICL_WANT_FLOAT
1515 case ficlInstructionF0:
1516 CHECK_FLOAT_STACK(0, 1);
1517 (++floatTop)->f = 0.0f;
1518 continue;
1519
1520 case ficlInstructionF1:
1521 CHECK_FLOAT_STACK(0, 1);
1522 (++floatTop)->f = 1.0f;
1523 continue;
1524
1525 case ficlInstructionFNeg1:
1526 CHECK_FLOAT_STACK(0, 1);
1527 (++floatTop)->f = -1.0f;
1528 continue;
1529
1530 /*
1531 * Floating point literal execution word.
1532 */
1533 case ficlInstructionFLiteralParen:
1534 CHECK_FLOAT_STACK(0, 1);
1535
1536 /*
1537 * Yes, I'm using ->i here,
1538 * but it's really a float. --lch
1539 */
1540 (++floatTop)->i = *ip++;
1541 continue;
1542
1543 /*
1544 * Do float addition r1 + r2.
1545 * f+ ( r1 r2 -- r )
1546 */
1547 case ficlInstructionFPlus:
1548 CHECK_FLOAT_STACK(2, 1);
1549
1550 f = (floatTop--)->f;
1551 floatTop->f += f;
1552 continue;
1553
1554 /*
1555 * Do float subtraction r1 - r2.
1556 * f- ( r1 r2 -- r )
1557 */
1558 case ficlInstructionFMinus:
1559 CHECK_FLOAT_STACK(2, 1);
1560
1561 f = (floatTop--)->f;
1562 floatTop->f -= f;
1563 continue;
1564
1565 /*
1566 * Do float multiplication r1 * r2.
1567 * f* ( r1 r2 -- r )
1568 */
1569 case ficlInstructionFStar:
1570 CHECK_FLOAT_STACK(2, 1);
1571
1572 f = (floatTop--)->f;
1573 floatTop->f *= f;
1574 continue;
1575
1576 /*
1577 * Do float negation.
1578 * fnegate ( r -- r )
1579 */
1580 case ficlInstructionFNegate:
1581 CHECK_FLOAT_STACK(1, 1);
1582
1583 floatTop->f = -(floatTop->f);
1584 continue;
1585
1586 /*
1587 * Do float division r1 / r2.
1588 * f/ ( r1 r2 -- r )
1589 */
1590 case ficlInstructionFSlash:
1591 CHECK_FLOAT_STACK(2, 1);
1592
1593 f = (floatTop--)->f;
1594 floatTop->f /= f;
1595 continue;
1596
1597 /*
1598 * Do float + integer r + n.
1599 * f+i ( r n -- r )
1600 */
1601 case ficlInstructionFPlusI:
1602 CHECK_FLOAT_STACK(1, 1);
1603 CHECK_STACK(1, 0);
1604
1605 f = (ficlFloat)(dataTop--)->f;
1606 floatTop->f += f;
1607 continue;
1608
1609 /*
1610 * Do float - integer r - n.
1611 * f-i ( r n -- r )
1612 */
1613 case ficlInstructionFMinusI:
1614 CHECK_FLOAT_STACK(1, 1);
1615 CHECK_STACK(1, 0);
1616
1617 f = (ficlFloat)(dataTop--)->f;
1618 floatTop->f -= f;
1619 continue;
1620
1621 /*
1622 * Do float * integer r * n.
1623 * f*i ( r n -- r )
1624 */
1625 case ficlInstructionFStarI:
1626 CHECK_FLOAT_STACK(1, 1);
1627 CHECK_STACK(1, 0);
1628
1629 f = (ficlFloat)(dataTop--)->f;
1630 floatTop->f *= f;
1631 continue;
1632
1633 /*
1634 * Do float / integer r / n.
1635 * f/i ( r n -- r )
1636 */
1637 case ficlInstructionFSlashI:
1638 CHECK_FLOAT_STACK(1, 1);
1639 CHECK_STACK(1, 0);
1640
1641 f = (ficlFloat)(dataTop--)->f;
1642 floatTop->f /= f;
1643 continue;
1644
1645 /*
1646 * Do integer - float n - r.
1647 * i-f ( n r -- r )
1648 */
1649 case ficlInstructionIMinusF:
1650 CHECK_FLOAT_STACK(1, 1);
1651 CHECK_STACK(1, 0);
1652
1653 f = (ficlFloat)(dataTop--)->f;
1654 floatTop->f = f - floatTop->f;
1655 continue;
1656
1657 /*
1658 * Do integer / float n / r.
1659 * i/f ( n r -- r )
1660 */
1661 case ficlInstructionISlashF:
1662 CHECK_FLOAT_STACK(1, 1);
1663 CHECK_STACK(1, 0);
1664
1665 f = (ficlFloat)(dataTop--)->f;
1666 floatTop->f = f / floatTop->f;
1667 continue;
1668
1669 /*
1670 * Do integer to float conversion.
1671 * int>float ( n -- r )
1672 */
1673 case ficlInstructionIntToFloat:
1674 CHECK_STACK(1, 0);
1675 CHECK_FLOAT_STACK(0, 1);
1676
1677 (++floatTop)->f = ((dataTop--)->f);
1678 continue;
1679
1680 /*
1681 * Do float to integer conversion.
1682 * float>int ( r -- n )
1683 */
1684 case ficlInstructionFloatToInt:
1685 CHECK_STACK(0, 1);
1686 CHECK_FLOAT_STACK(1, 0);
1687
1688 (++dataTop)->i = ((floatTop--)->i);
1689 continue;
1690
1691 /*
1692 * Add a floating point number to contents of a variable.
1693 * f+! ( r n -- )
1694 */
1695 case ficlInstructionFPlusStore: {
1696 ficlCell *cell;
1697
1698 CHECK_STACK(1, 0);
1699 CHECK_FLOAT_STACK(1, 0);
1700
1701 cell = (ficlCell *)(dataTop--)->p;
1702 cell->f += (floatTop--)->f;
1703 continue;
1704 }
1705
1706 /*
1707 * Do float stack drop.
1708 * fdrop ( r -- )
1709 */
1710 case ficlInstructionFDrop:
1711 CHECK_FLOAT_STACK(1, 0);
1712 floatTop--;
1713 continue;
1714
1715 /*
1716 * Do float stack ?dup.
1717 * f?dup ( r -- r )
1718 */
1719 case ficlInstructionFQuestionDup:
1720 CHECK_FLOAT_STACK(1, 2);
1721
1722 if (floatTop->f != 0)
1723 goto FDUP;
1724
1725 continue;
1726
1727 /*
1728 * Do float stack dup.
1729 * fdup ( r -- r r )
1730 */
1731 case ficlInstructionFDup:
1732 CHECK_FLOAT_STACK(1, 2);
1733
1734 FDUP:
1735 floatTop[1] = floatTop[0];
1736 floatTop++;
1737 continue;
1738
1739 /*
1740 * Do float stack swap.
1741 * fswap ( r1 r2 -- r2 r1 )
1742 */
1743 case ficlInstructionFSwap:
1744 CHECK_FLOAT_STACK(2, 2);
1745
1746 c = floatTop[0];
1747 floatTop[0] = floatTop[-1];
1748 floatTop[-1] = c;
1749 continue;
1750
1751 /*
1752 * Do float stack 2drop.
1753 * f2drop ( r r -- )
1754 */
1755 case ficlInstructionF2Drop:
1756 CHECK_FLOAT_STACK(2, 0);
1757
1758 floatTop -= 2;
1759 continue;
1760
1761 /*
1762 * Do float stack 2dup.
1763 * f2dup ( r1 r2 -- r1 r2 r1 r2 )
1764 */
1765 case ficlInstructionF2Dup:
1766 CHECK_FLOAT_STACK(2, 4);
1767
1768 floatTop[1] = floatTop[-1];
1769 floatTop[2] = *floatTop;
1770 floatTop += 2;
1771 continue;
1772
1773 /*
1774 * Do float stack over.
1775 * fover ( r1 r2 -- r1 r2 r1 )
1776 */
1777 case ficlInstructionFOver:
1778 CHECK_FLOAT_STACK(2, 3);
1779
1780 floatTop[1] = floatTop[-1];
1781 floatTop++;
1782 continue;
1783
1784 /*
1785 * Do float stack 2over.
1786 * f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
1787 */
1788 case ficlInstructionF2Over:
1789 CHECK_FLOAT_STACK(4, 6);
1790
1791 floatTop[1] = floatTop[-2];
1792 floatTop[2] = floatTop[-1];
1793 floatTop += 2;
1794 continue;
1795
1796 /*
1797 * Do float stack pick.
1798 * fpick ( n -- r )
1799 */
1800 case ficlInstructionFPick:
1801 CHECK_STACK(1, 0);
1802 c = *dataTop--;
1803 CHECK_FLOAT_STACK(c.i+2, c.i+3);
1804
1805 floatTop[1] = floatTop[- c.i - 1];
1806 continue;
1807
1808 /*
1809 * Do float stack rot.
1810 * frot ( r1 r2 r3 -- r2 r3 r1 )
1811 */
1812 case ficlInstructionFRot:
1813 i = 2;
1814 goto FROLL;
1815
1816 /*
1817 * Do float stack roll.
1818 * froll ( n -- )
1819 */
1820 case ficlInstructionFRoll:
1821 CHECK_STACK(1, 0);
1822 i = (dataTop--)->i;
1823
1824 if (i < 1)
1825 continue;
1826
1827 FROLL:
1828 CHECK_FLOAT_STACK(i+1, i+2);
1829 c = floatTop[-i];
1830 memmove(floatTop - i, floatTop - (i - 1),
1831 i * sizeof (ficlCell));
1832 *floatTop = c;
1833
1834 continue;
1835
1836 /*
1837 * Do float stack -rot.
1838 * f-rot ( r1 r2 r3 -- r3 r1 r2 )
1839 */
1840 case ficlInstructionFMinusRot:
1841 i = 2;
1842 goto FMINUSROLL;
1843
1844
1845 /*
1846 * Do float stack -roll.
1847 * f-roll ( n -- )
1848 */
1849 case ficlInstructionFMinusRoll:
1850 CHECK_STACK(1, 0);
1851 i = (dataTop--)->i;
1852
1853 if (i < 1)
1854 continue;
1855
1856 FMINUSROLL:
1857 CHECK_FLOAT_STACK(i+1, i+2);
1858 c = *floatTop;
1859 memmove(floatTop - (i - 1), floatTop - i,
1860 i * sizeof (ficlCell));
1861 floatTop[-i] = c;
1862
1863 continue;
1864
1865 /*
1866 * Do float stack 2swap
1867 * f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 )
1868 */
1869 case ficlInstructionF2Swap: {
1870 ficlCell c2;
1871 CHECK_FLOAT_STACK(4, 4);
1872
1873 c = *floatTop;
1874 c2 = floatTop[-1];
1875
1876 *floatTop = floatTop[-2];
1877 floatTop[-1] = floatTop[-3];
1878
1879 floatTop[-2] = c;
1880 floatTop[-3] = c2;
1881 continue;
1882 }
1883
1884 /*
1885 * Do float 0= comparison r = 0.0.
1886 * f0= ( r -- T/F )
1887 */
1888 case ficlInstructionF0Equals:
1889 CHECK_FLOAT_STACK(1, 0);
1890 CHECK_STACK(0, 1);
1891
1892 (++dataTop)->i = FICL_BOOL((floatTop--)->f != 0.0f);
1893 continue;
1894
1895 /*
1896 * Do float 0< comparison r < 0.0.
1897 * f0< ( r -- T/F )
1898 */
1899 case ficlInstructionF0Less:
1900 CHECK_FLOAT_STACK(1, 0);
1901 CHECK_STACK(0, 1);
1902
1903 (++dataTop)->i = FICL_BOOL((floatTop--)->f < 0.0f);
1904 continue;
1905
1906 /*
1907 * Do float 0> comparison r > 0.0.
1908 * f0> ( r -- T/F )
1909 */
1910 case ficlInstructionF0Greater:
1911 CHECK_FLOAT_STACK(1, 0);
1912 CHECK_STACK(0, 1);
1913
1914 (++dataTop)->i = FICL_BOOL((floatTop--)->f > 0.0f);
1915 continue;
1916
1917 /*
1918 * Do float = comparison r1 = r2.
1919 * f= ( r1 r2 -- T/F )
1920 */
1921 case ficlInstructionFEquals:
1922 CHECK_FLOAT_STACK(2, 0);
1923 CHECK_STACK(0, 1);
1924
1925 f = (floatTop--)->f;
1926 (++dataTop)->i = FICL_BOOL((floatTop--)->f == f);
1927 continue;
1928
1929 /*
1930 * Do float < comparison r1 < r2.
1931 * f< ( r1 r2 -- T/F )
1932 */
1933 case ficlInstructionFLess:
1934 CHECK_FLOAT_STACK(2, 0);
1935 CHECK_STACK(0, 1);
1936
1937 f = (floatTop--)->f;
1938 (++dataTop)->i = FICL_BOOL((floatTop--)->f < f);
1939 continue;
1940
1941 /*
1942 * Do float > comparison r1 > r2.
1943 * f> ( r1 r2 -- T/F )
1944 */
1945 case ficlInstructionFGreater:
1946 CHECK_FLOAT_STACK(2, 0);
1947 CHECK_STACK(0, 1);
1948
1949 f = (floatTop--)->f;
1950 (++dataTop)->i = FICL_BOOL((floatTop--)->f > f);
1951 continue;
1952
1953
1954 /*
1955 * Move float to param stack (assumes they both fit in a
1956 * single ficlCell) f>s
1957 */
1958 case ficlInstructionFFrom:
1959 CHECK_FLOAT_STACK(1, 0);
1960 CHECK_STACK(0, 1);
1961
1962 *++dataTop = *floatTop--;
1963 continue;
1964
1965 case ficlInstructionToF:
1966 CHECK_FLOAT_STACK(0, 1);
1967 CHECK_STACK(1, 0);
1968
1969 *++floatTop = *dataTop--;
1970 continue;
1971
1972 #endif /* FICL_WANT_FLOAT */
1973
1974 /*
1975 * c o l o n P a r e n
1976 * This is the code that executes a colon definition. It
1977 * assumes that the virtual machine is running a "next" loop
1978 * (See the vm.c for its implementation of member function
1979 * vmExecute()). The colon code simply copies the address of
1980 * the first word in the list of words to interpret into IP
1981 * after saving its old value. When we return to the "next"
1982 * loop, the virtual machine will call the code for each
1983 * word in turn.
1984 */
1985 case ficlInstructionColonParen:
1986 (++returnTop)->p = (void *)ip;
1987 ip = (ficlInstruction *)(fw->param);
1988 continue;
1989
1990 case ficlInstructionCreateParen:
1991 CHECK_STACK(0, 1);
1992 (++dataTop)->p = (fw->param + 1);
1993 continue;
1994
1995 case ficlInstructionVariableParen:
1996 CHECK_STACK(0, 1);
1997 (++dataTop)->p = fw->param;
1998 continue;
1999
2000 /*
2001 * c o n s t a n t P a r e n
2002 * This is the run-time code for "constant". It simply returns
2003 * the contents of its word's first data ficlCell.
2004 */
2005
2006 #if FICL_WANT_FLOAT
2007 case ficlInstructionF2ConstantParen:
2008 CHECK_FLOAT_STACK(0, 2);
2009 FLOAT_PUSH_CELL_POINTER_DOUBLE(fw->param);
2010
2011 case ficlInstructionFConstantParen:
2012 CHECK_FLOAT_STACK(0, 1);
2013 FLOAT_PUSH_CELL_POINTER(fw->param);
2014 #endif /* FICL_WANT_FLOAT */
2015
2016 case ficlInstruction2ConstantParen:
2017 CHECK_STACK(0, 2);
2018 PUSH_CELL_POINTER_DOUBLE(fw->param);
2019
2020 case ficlInstructionConstantParen:
2021 CHECK_STACK(0, 1);
2022 PUSH_CELL_POINTER(fw->param);
2023
2024 #if FICL_WANT_USER
2025 case ficlInstructionUserParen: {
2026 ficlInteger i = fw->param[0].i;
2027 (++dataTop)->p = &vm->user[i];
2028 continue;
2029 }
2030 #endif
2031
2032 default:
2033 /*
2034 * Clever hack, or evil coding? You be the judge.
2035 *
2036 * If the word we've been asked to execute is in fact
2037 * an *instruction*, we grab the instruction, stow it
2038 * in "i" (our local cache of *ip), and *jump* to the
2039 * top of the switch statement. --lch
2040 */
2041 if (((ficlInstruction)fw->code >
2042 ficlInstructionInvalid) &&
2043 ((ficlInstruction)fw->code < ficlInstructionLast)) {
2044 instruction = (ficlInstruction)fw->code;
2045 goto AGAIN;
2046 }
2047
2048 LOCAL_VARIABLE_SPILL;
2049 (vm)->runningWord = fw;
2050 fw->code(vm);
2051 LOCAL_VARIABLE_REFILL;
2052 continue;
2053 }
2054 }
2055
2056 LOCAL_VARIABLE_SPILL;
2057 vm->exceptionHandler = oldExceptionHandler;
2058 }
2059
2060 /*
2061 * v m G e t D i c t
2062 * Returns the address dictionary for this VM's system
2063 */
2064 ficlDictionary *
ficlVmGetDictionary(ficlVm * vm)2065 ficlVmGetDictionary(ficlVm *vm)
2066 {
2067 FICL_VM_ASSERT(vm, vm);
2068 return (vm->callback.system->dictionary);
2069 }
2070
2071 /*
2072 * v m G e t S t r i n g
2073 * Parses a string out of the VM input buffer and copies up to the first
2074 * FICL_COUNTED_STRING_MAX characters to the supplied destination buffer, a
2075 * ficlCountedString. The destination string is NULL terminated.
2076 *
2077 * Returns the address of the first unused character in the dest buffer.
2078 */
2079 char *
ficlVmGetString(ficlVm * vm,ficlCountedString * counted,char delimiter)2080 ficlVmGetString(ficlVm *vm, ficlCountedString *counted, char delimiter)
2081 {
2082 ficlString s = ficlVmParseStringEx(vm, delimiter, 0);
2083
2084 if (FICL_STRING_GET_LENGTH(s) > FICL_COUNTED_STRING_MAX) {
2085 FICL_STRING_SET_LENGTH(s, FICL_COUNTED_STRING_MAX);
2086 }
2087
2088 strncpy(counted->text, FICL_STRING_GET_POINTER(s),
2089 FICL_STRING_GET_LENGTH(s));
2090 counted->text[FICL_STRING_GET_LENGTH(s)] = '\0';
2091 counted->length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s);
2092
2093 return (counted->text + FICL_STRING_GET_LENGTH(s) + 1);
2094 }
2095
2096 /*
2097 * v m G e t W o r d
2098 * vmGetWord calls vmGetWord0 repeatedly until it gets a string with
2099 * non-zero length.
2100 */
2101 ficlString
ficlVmGetWord(ficlVm * vm)2102 ficlVmGetWord(ficlVm *vm)
2103 {
2104 ficlString s = ficlVmGetWord0(vm);
2105
2106 if (FICL_STRING_GET_LENGTH(s) == 0) {
2107 ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
2108 }
2109
2110 return (s);
2111 }
2112
2113 /*
2114 * v m G e t W o r d 0
2115 * Skip leading whitespace and parse a space delimited word from the tib.
2116 * Returns the start address and length of the word. Updates the tib
2117 * to reflect characters consumed, including the trailing delimiter.
2118 * If there's nothing of interest in the tib, returns zero. This function
2119 * does not use vmParseString because it uses isspace() rather than a
2120 * single delimiter character.
2121 */
2122 ficlString
ficlVmGetWord0(ficlVm * vm)2123 ficlVmGetWord0(ficlVm *vm)
2124 {
2125 char *trace = ficlVmGetInBuf(vm);
2126 char *stop = ficlVmGetInBufEnd(vm);
2127 ficlString s;
2128 ficlUnsigned length = 0;
2129 char c = 0;
2130
2131 trace = ficlStringSkipSpace(trace, stop);
2132 FICL_STRING_SET_POINTER(s, trace);
2133
2134 /* Please leave this loop this way; it makes Purify happier. --lch */
2135 for (;;) {
2136 if (trace == stop)
2137 break;
2138 c = *trace;
2139 if (isspace((unsigned char)c))
2140 break;
2141 length++;
2142 trace++;
2143 }
2144
2145 FICL_STRING_SET_LENGTH(s, length);
2146
2147 /* skip one trailing delimiter */
2148 if ((trace != stop) && isspace((unsigned char)c))
2149 trace++;
2150
2151 ficlVmUpdateTib(vm, trace);
2152
2153 return (s);
2154 }
2155
2156 /*
2157 * v m G e t W o r d T o P a d
2158 * Does vmGetWord and copies the result to the pad as a NULL terminated
2159 * string. Returns the length of the string. If the string is too long
2160 * to fit in the pad, it is truncated.
2161 */
2162 int
ficlVmGetWordToPad(ficlVm * vm)2163 ficlVmGetWordToPad(ficlVm *vm)
2164 {
2165 ficlString s;
2166 char *pad = (char *)vm->pad;
2167 s = ficlVmGetWord(vm);
2168
2169 if (FICL_STRING_GET_LENGTH(s) > FICL_PAD_SIZE)
2170 FICL_STRING_SET_LENGTH(s, FICL_PAD_SIZE);
2171
2172 strncpy(pad, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s));
2173 pad[FICL_STRING_GET_LENGTH(s)] = '\0';
2174 return ((int)(FICL_STRING_GET_LENGTH(s)));
2175 }
2176
2177 /*
2178 * v m P a r s e S t r i n g
2179 * Parses a string out of the input buffer using the delimiter
2180 * specified. Skips leading delimiters, marks the start of the string,
2181 * and counts characters to the next delimiter it encounters. It then
2182 * updates the vm input buffer to consume all these chars, including the
2183 * trailing delimiter.
2184 * Returns the address and length of the parsed string, not including the
2185 * trailing delimiter.
2186 */
2187 ficlString
ficlVmParseString(ficlVm * vm,char delimiter)2188 ficlVmParseString(ficlVm *vm, char delimiter)
2189 {
2190 return (ficlVmParseStringEx(vm, delimiter, 1));
2191 }
2192
2193 ficlString
ficlVmParseStringEx(ficlVm * vm,char delimiter,char skipLeadingDelimiters)2194 ficlVmParseStringEx(ficlVm *vm, char delimiter, char skipLeadingDelimiters)
2195 {
2196 ficlString s;
2197 char *trace = ficlVmGetInBuf(vm);
2198 char *stop = ficlVmGetInBufEnd(vm);
2199 char c;
2200
2201 if (skipLeadingDelimiters) {
2202 while ((trace != stop) && (*trace == delimiter))
2203 trace++;
2204 }
2205
2206 FICL_STRING_SET_POINTER(s, trace); /* mark start of text */
2207
2208 /* find next delimiter or end of line */
2209 for (c = *trace;
2210 (trace != stop) && (c != delimiter) && (c != '\r') && (c != '\n');
2211 c = *++trace) {
2212 ;
2213 }
2214
2215 /* set length of result */
2216 FICL_STRING_SET_LENGTH(s, trace - FICL_STRING_GET_POINTER(s));
2217
2218 /* gobble trailing delimiter */
2219 if ((trace != stop) && (*trace == delimiter))
2220 trace++;
2221
2222 ficlVmUpdateTib(vm, trace);
2223 return (s);
2224 }
2225
2226
2227 /*
2228 * v m P o p
2229 */
2230 ficlCell
ficlVmPop(ficlVm * vm)2231 ficlVmPop(ficlVm *vm)
2232 {
2233 return (ficlStackPop(vm->dataStack));
2234 }
2235
2236 /*
2237 * v m P u s h
2238 */
2239 void
ficlVmPush(ficlVm * vm,ficlCell c)2240 ficlVmPush(ficlVm *vm, ficlCell c)
2241 {
2242 ficlStackPush(vm->dataStack, c);
2243 }
2244
2245 /*
2246 * v m P o p I P
2247 */
2248 void
ficlVmPopIP(ficlVm * vm)2249 ficlVmPopIP(ficlVm *vm)
2250 {
2251 vm->ip = (ficlIp)(ficlStackPopPointer(vm->returnStack));
2252 }
2253
2254 /*
2255 * v m P u s h I P
2256 */
2257 void
ficlVmPushIP(ficlVm * vm,ficlIp newIP)2258 ficlVmPushIP(ficlVm *vm, ficlIp newIP)
2259 {
2260 ficlStackPushPointer(vm->returnStack, (void *)vm->ip);
2261 vm->ip = newIP;
2262 }
2263
2264 /*
2265 * v m P u s h T i b
2266 * Binds the specified input string to the VM and clears >IN (the index)
2267 */
2268 void
ficlVmPushTib(ficlVm * vm,char * text,ficlInteger nChars,ficlTIB * pSaveTib)2269 ficlVmPushTib(ficlVm *vm, char *text, ficlInteger nChars, ficlTIB *pSaveTib)
2270 {
2271 if (pSaveTib) {
2272 *pSaveTib = vm->tib;
2273 }
2274 vm->tib.text = text;
2275 vm->tib.end = text + nChars;
2276 vm->tib.index = 0;
2277 }
2278
2279 void
ficlVmPopTib(ficlVm * vm,ficlTIB * pTib)2280 ficlVmPopTib(ficlVm *vm, ficlTIB *pTib)
2281 {
2282 if (pTib) {
2283 vm->tib = *pTib;
2284 }
2285 }
2286
2287 /*
2288 * v m Q u i t
2289 */
2290 void
ficlVmQuit(ficlVm * vm)2291 ficlVmQuit(ficlVm *vm)
2292 {
2293 ficlStackReset(vm->returnStack);
2294 vm->restart = 0;
2295 vm->ip = NULL;
2296 vm->runningWord = NULL;
2297 vm->state = FICL_VM_STATE_INTERPRET;
2298 vm->tib.text = NULL;
2299 vm->tib.end = NULL;
2300 vm->tib.index = 0;
2301 vm->pad[0] = '\0';
2302 vm->sourceId.i = 0;
2303 }
2304
2305 /*
2306 * v m R e s e t
2307 */
2308 void
ficlVmReset(ficlVm * vm)2309 ficlVmReset(ficlVm *vm)
2310 {
2311 ficlVmQuit(vm);
2312 ficlStackReset(vm->dataStack);
2313 #if FICL_WANT_FLOAT
2314 ficlStackReset(vm->floatStack);
2315 #endif
2316 vm->base = 10;
2317 }
2318
2319 /*
2320 * v m S e t T e x t O u t
2321 * Binds the specified output callback to the vm. If you pass NULL,
2322 * binds the default output function (ficlTextOut)
2323 */
2324 void
ficlVmSetTextOut(ficlVm * vm,ficlOutputFunction textOut)2325 ficlVmSetTextOut(ficlVm *vm, ficlOutputFunction textOut)
2326 {
2327 vm->callback.textOut = textOut;
2328 }
2329
2330 void
ficlVmTextOut(ficlVm * vm,char * text)2331 ficlVmTextOut(ficlVm *vm, char *text)
2332 {
2333 ficlCallbackTextOut((ficlCallback *)vm, text);
2334 }
2335
2336
2337 void
ficlVmErrorOut(ficlVm * vm,char * text)2338 ficlVmErrorOut(ficlVm *vm, char *text)
2339 {
2340 ficlCallbackErrorOut((ficlCallback *)vm, text);
2341 }
2342
2343
2344 /*
2345 * v m T h r o w
2346 */
2347 void
ficlVmThrow(ficlVm * vm,int except)2348 ficlVmThrow(ficlVm *vm, int except)
2349 {
2350 if (vm->exceptionHandler)
2351 longjmp(*(vm->exceptionHandler), except);
2352 }
2353
2354 void
ficlVmThrowError(ficlVm * vm,char * fmt,...)2355 ficlVmThrowError(ficlVm *vm, char *fmt, ...)
2356 {
2357 va_list list;
2358
2359 va_start(list, fmt);
2360 vsprintf(vm->pad, fmt, list);
2361 va_end(list);
2362 strcat(vm->pad, "\n");
2363
2364 ficlVmErrorOut(vm, vm->pad);
2365 longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT);
2366 }
2367
2368 void
ficlVmThrowErrorVararg(ficlVm * vm,char * fmt,va_list list)2369 ficlVmThrowErrorVararg(ficlVm *vm, char *fmt, va_list list)
2370 {
2371 vsprintf(vm->pad, fmt, list);
2372 /*
2373 * well, we can try anyway, we're certainly not
2374 * returning to our caller!
2375 */
2376 va_end(list);
2377 strcat(vm->pad, "\n");
2378
2379 ficlVmErrorOut(vm, vm->pad);
2380 longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT);
2381 }
2382
2383 /*
2384 * f i c l E v a l u a t e
2385 * Wrapper for ficlExec() which sets SOURCE-ID to -1.
2386 */
2387 int
ficlVmEvaluate(ficlVm * vm,char * s)2388 ficlVmEvaluate(ficlVm *vm, char *s)
2389 {
2390 int returnValue;
2391 ficlCell id = vm->sourceId;
2392 ficlString string;
2393 vm->sourceId.i = -1;
2394 FICL_STRING_SET_FROM_CSTRING(string, s);
2395 returnValue = ficlVmExecuteString(vm, string);
2396 vm->sourceId = id;
2397 return (returnValue);
2398 }
2399
2400 /*
2401 * f i c l E x e c
2402 * Evaluates a block of input text in the context of the
2403 * specified interpreter. Emits any requested output to the
2404 * interpreter's output function.
2405 *
2406 * Contains the "inner interpreter" code in a tight loop
2407 *
2408 * Returns one of the VM_XXXX codes defined in ficl.h:
2409 * VM_OUTOFTEXT is the normal exit condition
2410 * VM_ERREXIT means that the interpreter encountered a syntax error
2411 * and the vm has been reset to recover (some or all
2412 * of the text block got ignored
2413 * VM_USEREXIT means that the user executed the "bye" command
2414 * to shut down the interpreter. This would be a good
2415 * time to delete the vm, etc -- or you can ignore this
2416 * signal.
2417 */
2418 int
ficlVmExecuteString(ficlVm * vm,ficlString s)2419 ficlVmExecuteString(ficlVm *vm, ficlString s)
2420 {
2421 ficlSystem *system = vm->callback.system;
2422 ficlDictionary *dictionary = system->dictionary;
2423
2424 int except;
2425 jmp_buf vmState;
2426 jmp_buf *oldState;
2427 ficlTIB saveficlTIB;
2428
2429 FICL_VM_ASSERT(vm, vm);
2430 FICL_VM_ASSERT(vm, system->interpreterLoop[0]);
2431
2432 ficlVmPushTib(vm, FICL_STRING_GET_POINTER(s),
2433 FICL_STRING_GET_LENGTH(s), &saveficlTIB);
2434
2435 /*
2436 * Save and restore VM's jmp_buf to enable nested calls to ficlExec
2437 */
2438 oldState = vm->exceptionHandler;
2439
2440 /* This has to come before the setjmp! */
2441 vm->exceptionHandler = &vmState;
2442 except = setjmp(vmState);
2443
2444 switch (except) {
2445 case 0:
2446 if (vm->restart) {
2447 vm->runningWord->code(vm);
2448 vm->restart = 0;
2449 } else { /* set VM up to interpret text */
2450 ficlVmPushIP(vm, &(system->interpreterLoop[0]));
2451 }
2452
2453 ficlVmInnerLoop(vm, 0);
2454 break;
2455
2456 case FICL_VM_STATUS_RESTART:
2457 vm->restart = 1;
2458 except = FICL_VM_STATUS_OUT_OF_TEXT;
2459 break;
2460
2461 case FICL_VM_STATUS_OUT_OF_TEXT:
2462 ficlVmPopIP(vm);
2463 #if 0 /* we dont output prompt in loader */
2464 if ((vm->state != FICL_VM_STATE_COMPILE) &&
2465 (vm->sourceId.i == 0))
2466 ficlVmTextOut(vm, FICL_PROMPT);
2467 #endif
2468 break;
2469
2470 case FICL_VM_STATUS_USER_EXIT:
2471 case FICL_VM_STATUS_INNER_EXIT:
2472 case FICL_VM_STATUS_BREAK:
2473 break;
2474
2475 case FICL_VM_STATUS_QUIT:
2476 if (vm->state == FICL_VM_STATE_COMPILE) {
2477 ficlDictionaryAbortDefinition(dictionary);
2478 #if FICL_WANT_LOCALS
2479 ficlDictionaryEmpty(system->locals,
2480 system->locals->forthWordlist->size);
2481 #endif
2482 }
2483 ficlVmQuit(vm);
2484 break;
2485
2486 case FICL_VM_STATUS_ERROR_EXIT:
2487 case FICL_VM_STATUS_ABORT:
2488 case FICL_VM_STATUS_ABORTQ:
2489 default: /* user defined exit code?? */
2490 if (vm->state == FICL_VM_STATE_COMPILE) {
2491 ficlDictionaryAbortDefinition(dictionary);
2492 #if FICL_WANT_LOCALS
2493 ficlDictionaryEmpty(system->locals,
2494 system->locals->forthWordlist->size);
2495 #endif
2496 }
2497 ficlDictionaryResetSearchOrder(dictionary);
2498 ficlVmReset(vm);
2499 break;
2500 }
2501
2502 vm->exceptionHandler = oldState;
2503 ficlVmPopTib(vm, &saveficlTIB);
2504 return (except);
2505 }
2506
2507 /*
2508 * f i c l E x e c X T
2509 * Given a pointer to a ficlWord, push an inner interpreter and
2510 * execute the word to completion. This is in contrast with vmExecute,
2511 * which does not guarantee that the word will have completed when
2512 * the function returns (ie in the case of colon definitions, which
2513 * need an inner interpreter to finish)
2514 *
2515 * Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
2516 * exit condition is VM_INNEREXIT, Ficl's private signal to exit the
2517 * inner loop under normal circumstances. If another code is thrown to
2518 * exit the loop, this function will re-throw it if it's nested under
2519 * itself or ficlExec.
2520 *
2521 * NOTE: this function is intended so that C code can execute ficlWords
2522 * given their address in the dictionary (xt).
2523 */
2524 int
ficlVmExecuteXT(ficlVm * vm,ficlWord * pWord)2525 ficlVmExecuteXT(ficlVm *vm, ficlWord *pWord)
2526 {
2527 int except;
2528 jmp_buf vmState;
2529 jmp_buf *oldState;
2530 ficlWord *oldRunningWord;
2531
2532 FICL_VM_ASSERT(vm, vm);
2533 FICL_VM_ASSERT(vm, vm->callback.system->exitInnerWord);
2534
2535 /*
2536 * Save the runningword so that RESTART behaves correctly
2537 * over nested calls.
2538 */
2539 oldRunningWord = vm->runningWord;
2540 /*
2541 * Save and restore VM's jmp_buf to enable nested calls
2542 */
2543 oldState = vm->exceptionHandler;
2544 /* This has to come before the setjmp! */
2545 vm->exceptionHandler = &vmState;
2546 except = setjmp(vmState);
2547
2548 if (except)
2549 ficlVmPopIP(vm);
2550 else
2551 ficlVmPushIP(vm, &(vm->callback.system->exitInnerWord));
2552
2553 switch (except) {
2554 case 0:
2555 ficlVmExecuteWord(vm, pWord);
2556 ficlVmInnerLoop(vm, 0);
2557 break;
2558
2559 case FICL_VM_STATUS_INNER_EXIT:
2560 case FICL_VM_STATUS_BREAK:
2561 break;
2562
2563 case FICL_VM_STATUS_RESTART:
2564 case FICL_VM_STATUS_OUT_OF_TEXT:
2565 case FICL_VM_STATUS_USER_EXIT:
2566 case FICL_VM_STATUS_QUIT:
2567 case FICL_VM_STATUS_ERROR_EXIT:
2568 case FICL_VM_STATUS_ABORT:
2569 case FICL_VM_STATUS_ABORTQ:
2570 default: /* user defined exit code?? */
2571 if (oldState) {
2572 vm->exceptionHandler = oldState;
2573 ficlVmThrow(vm, except);
2574 }
2575 break;
2576 }
2577
2578 vm->exceptionHandler = oldState;
2579 vm->runningWord = oldRunningWord;
2580 return (except);
2581 }
2582
2583 /*
2584 * f i c l P a r s e N u m b e r
2585 * Attempts to convert the NULL terminated string in the VM's pad to
2586 * a number using the VM's current base. If successful, pushes the number
2587 * onto the param stack and returns FICL_TRUE. Otherwise, returns FICL_FALSE.
2588 * (jws 8/01) Trailing decimal point causes a zero ficlCell to be pushed. (See
2589 * the standard for DOUBLE wordset.
2590 */
2591 int
ficlVmParseNumber(ficlVm * vm,ficlString s)2592 ficlVmParseNumber(ficlVm *vm, ficlString s)
2593 {
2594 ficlInteger accumulator = 0;
2595 char isNegative = 0;
2596 char isDouble = 0;
2597 unsigned base = vm->base;
2598 char *trace = FICL_STRING_GET_POINTER(s);
2599 ficlUnsigned8 length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s);
2600 unsigned c;
2601 unsigned digit;
2602
2603 if (length > 1) {
2604 switch (*trace) {
2605 case '-':
2606 trace++;
2607 length--;
2608 isNegative = 1;
2609 break;
2610 case '+':
2611 trace++;
2612 length--;
2613 isNegative = 0;
2614 break;
2615 default:
2616 break;
2617 }
2618 }
2619
2620 /* detect & remove trailing decimal */
2621 if ((length > 0) && (trace[length - 1] == '.')) {
2622 isDouble = 1;
2623 length--;
2624 }
2625
2626 if (length == 0) /* detect "+", "-", ".", "+." etc */
2627 return (0); /* false */
2628
2629 while ((length--) && ((c = *trace++) != '\0')) {
2630 if (!isalnum(c))
2631 return (0); /* false */
2632
2633 digit = c - '0';
2634
2635 if (digit > 9)
2636 digit = tolower(c) - 'a' + 10;
2637
2638 if (digit >= base)
2639 return (0); /* false */
2640
2641 accumulator = accumulator * base + digit;
2642 }
2643
2644 if (isNegative)
2645 accumulator = -accumulator;
2646
2647 ficlStackPushInteger(vm->dataStack, accumulator);
2648 if (vm->state == FICL_VM_STATE_COMPILE)
2649 ficlPrimitiveLiteralIm(vm);
2650
2651 if (isDouble) { /* simple (required) DOUBLE support */
2652 if (isNegative)
2653 ficlStackPushInteger(vm->dataStack, -1);
2654 else
2655 ficlStackPushInteger(vm->dataStack, 0);
2656 if (vm->state == FICL_VM_STATE_COMPILE)
2657 ficlPrimitiveLiteralIm(vm);
2658 }
2659
2660 return (1); /* true */
2661 }
2662
2663 /*
2664 * d i c t C h e c k
2665 * Checks the dictionary for corruption and throws appropriate
2666 * errors.
2667 * Input: +n number of ADDRESS UNITS (not ficlCells) proposed to allot
2668 * -n number of ADDRESS UNITS proposed to de-allot
2669 * 0 just do a consistency check
2670 */
2671 void
ficlVmDictionarySimpleCheck(ficlVm * vm,ficlDictionary * dictionary,int cells)2672 ficlVmDictionarySimpleCheck(ficlVm *vm, ficlDictionary *dictionary, int cells)
2673 {
2674 #if FICL_ROBUST >= 1
2675 if ((cells >= 0) &&
2676 (ficlDictionaryCellsAvailable(dictionary) *
2677 (int)sizeof (ficlCell) < cells)) {
2678 ficlVmThrowError(vm, "Error: dictionary full");
2679 }
2680
2681 if ((cells <= 0) &&
2682 (ficlDictionaryCellsUsed(dictionary) *
2683 (int)sizeof (ficlCell) < -cells)) {
2684 ficlVmThrowError(vm, "Error: dictionary underflow");
2685 }
2686 #else /* FICL_ROBUST >= 1 */
2687 FICL_IGNORE(vm);
2688 FICL_IGNORE(dictionary);
2689 FICL_IGNORE(cells);
2690 #endif /* FICL_ROBUST >= 1 */
2691 }
2692
2693 void
ficlVmDictionaryCheck(ficlVm * vm,ficlDictionary * dictionary,int cells)2694 ficlVmDictionaryCheck(ficlVm *vm, ficlDictionary *dictionary, int cells)
2695 {
2696 #if FICL_ROBUST >= 1
2697 ficlVmDictionarySimpleCheck(vm, dictionary, cells);
2698
2699 if (dictionary->wordlistCount > FICL_MAX_WORDLISTS) {
2700 ficlDictionaryResetSearchOrder(dictionary);
2701 ficlVmThrowError(vm, "Error: search order overflow");
2702 } else if (dictionary->wordlistCount < 0) {
2703 ficlDictionaryResetSearchOrder(dictionary);
2704 ficlVmThrowError(vm, "Error: search order underflow");
2705 }
2706 #else /* FICL_ROBUST >= 1 */
2707 FICL_IGNORE(vm);
2708 FICL_IGNORE(dictionary);
2709 FICL_IGNORE(cells);
2710 #endif /* FICL_ROBUST >= 1 */
2711 }
2712
2713 void
ficlVmDictionaryAllot(ficlVm * vm,ficlDictionary * dictionary,int n)2714 ficlVmDictionaryAllot(ficlVm *vm, ficlDictionary *dictionary, int n)
2715 {
2716 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, n);
2717 FICL_IGNORE(vm);
2718 ficlDictionaryAllot(dictionary, n);
2719 }
2720
2721 void
ficlVmDictionaryAllotCells(ficlVm * vm,ficlDictionary * dictionary,int cells)2722 ficlVmDictionaryAllotCells(ficlVm *vm, ficlDictionary *dictionary, int cells)
2723 {
2724 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, cells);
2725 FICL_IGNORE(vm);
2726 ficlDictionaryAllotCells(dictionary, cells);
2727 }
2728
2729 /*
2730 * f i c l P a r s e W o r d
2731 * From the standard, section 3.4
2732 * b) Search the dictionary name space (see 3.4.2). If a definition name
2733 * matching the string is found:
2734 * 1.if interpreting, perform the interpretation semantics of the definition
2735 * (see 3.4.3.2), and continue at a);
2736 * 2.if compiling, perform the compilation semantics of the definition
2737 * (see 3.4.3.3), and continue at a).
2738 *
2739 * c) If a definition name matching the string is not found, attempt to
2740 * convert the string to a number (see 3.4.1.3). If successful:
2741 * 1.if interpreting, place the number on the data stack, and continue at a);
2742 * 2.if compiling, FICL_VM_STATE_COMPILE code that when executed will place
2743 * the number on the stack (see 6.1.1780 LITERAL), and continue at a);
2744 *
2745 * d) If unsuccessful, an ambiguous condition exists (see 3.4.4).
2746 *
2747 * (jws 4/01) Modified to be a ficlParseStep
2748 */
2749 int
ficlVmParseWord(ficlVm * vm,ficlString name)2750 ficlVmParseWord(ficlVm *vm, ficlString name)
2751 {
2752 ficlDictionary *dictionary = ficlVmGetDictionary(vm);
2753 ficlWord *tempFW;
2754
2755 FICL_VM_DICTIONARY_CHECK(vm, dictionary, 0);
2756 FICL_STACK_CHECK(vm->dataStack, 0, 0);
2757
2758 #if FICL_WANT_LOCALS
2759 if (vm->callback.system->localsCount > 0) {
2760 tempFW = ficlSystemLookupLocal(vm->callback.system, name);
2761 } else
2762 #endif
2763 tempFW = ficlDictionaryLookup(dictionary, name);
2764
2765 if (vm->state == FICL_VM_STATE_INTERPRET) {
2766 if (tempFW != NULL) {
2767 if (ficlWordIsCompileOnly(tempFW)) {
2768 ficlVmThrowError(vm,
2769 "Error: FICL_VM_STATE_COMPILE only!");
2770 }
2771
2772 ficlVmExecuteWord(vm, tempFW);
2773 return (1); /* true */
2774 }
2775 } else { /* (vm->state == FICL_VM_STATE_COMPILE) */
2776 if (tempFW != NULL) {
2777 if (ficlWordIsImmediate(tempFW)) {
2778 ficlVmExecuteWord(vm, tempFW);
2779 } else {
2780 ficlCell c;
2781 c.p = tempFW;
2782 if (tempFW->flags & FICL_WORD_INSTRUCTION)
2783 ficlDictionaryAppendUnsigned(dictionary,
2784 (ficlInteger)tempFW->code);
2785 else
2786 ficlDictionaryAppendCell(dictionary, c);
2787 }
2788 return (1); /* true */
2789 }
2790 }
2791
2792 return (0); /* false */
2793 }
2794