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 $
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
16 * Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
17 * All rights reserved.
19 * Get the latest Ficl release at http://ficl.sourceforge.net
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.
26 * L I C E N S E and D I S C L A I M E R
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
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.
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
51 * Copyright 2019 Joyent, Inc.
57 #define FICL_VM_CHECK(vm) \
58 FICL_VM_ASSERT(vm, (*(vm->ip - 1)) == vm->runningWord)
60 #define FICL_VM_CHECK(vm)
64 * v m B r a n c h R e l a t i v e
67 ficlVmBranchRelative(ficlVm
*vm
, int offset
)
74 * Creates a virtual machine either from scratch (if vm is NULL on entry)
75 * or by resizing and reinitializing an existing VM to the specified stack
79 ficlVmCreate(ficlVm
*vm
, unsigned nPStack
, unsigned nRStack
)
82 vm
= (ficlVm
*)ficlMalloc(sizeof (ficlVm
));
83 FICL_ASSERT(NULL
, vm
);
84 memset(vm
, 0, sizeof (ficlVm
));
88 ficlStackDestroy(vm
->dataStack
);
89 vm
->dataStack
= ficlStackCreate(vm
, "data", nPStack
);
92 ficlStackDestroy(vm
->returnStack
);
93 vm
->returnStack
= ficlStackCreate(vm
, "return", nRStack
);
97 ficlStackDestroy(vm
->floatStack
);
98 vm
->floatStack
= ficlStackCreate(vm
, "float", nPStack
);
107 * Free all memory allocated to the specified VM and its subordinate
111 ficlVmDestroy(ficlVm
*vm
)
114 ficlFree(vm
->dataStack
);
115 ficlFree(vm
->returnStack
);
117 ficlFree(vm
->floatStack
);
125 * Sets up the specified word to be run by the inner interpreter.
126 * Executes the word's code part immediately, but in the case of
127 * colon definition, the definition itself needs the inner interpreter
128 * to complete. This does not happen until control reaches ficlExec
131 ficlVmExecuteWord(ficlVm
*vm
, ficlWord
*pWord
)
133 ficlVmInnerLoop(vm
, pWord
);
137 ficlVmOptimizeJumpToJump(ficlVm
*vm
, ficlIp ip
)
140 switch ((ficlInstruction
)(*ip
)) {
141 case ficlInstructionBranchParenWithCheck
:
142 *ip
= (ficlWord
*)ficlInstructionBranchParen
;
145 case ficlInstructionBranch0ParenWithCheck
:
146 *ip
= (ficlWord
*)ficlInstructionBranch0Paren
;
149 destination
= ip
+ *(ficlInteger
*)ip
;
150 switch ((ficlInstruction
)*destination
) {
151 case ficlInstructionBranchParenWithCheck
:
152 /* preoptimize where we're jumping to */
153 ficlVmOptimizeJumpToJump(vm
, destination
);
155 case ficlInstructionBranchParen
:
157 destination
+= *(ficlInteger
*)destination
;
158 *ip
= (ficlWord
*)(destination
- ip
);
165 * v m I n n e r L o o p
166 * the mysterious inner interpreter...
167 * This loop is the address interpreter that makes colon definitions
168 * work. Upon entry, it assumes that the IP points to an entry in
169 * a definition (the body of a colon word). It runs one word at a time
170 * until something does vmThrow. The catcher for this is expected to exist
171 * in the calling code.
172 * vmThrow gets you out of this loop with a longjmp()
176 /* turn off stack checking for primitives */
177 #define _CHECK_STACK(stack, top, pop, push)
180 #define _CHECK_STACK(stack, top, pop, push) \
181 ficlStackCheckNospill(stack, top, pop, push)
183 static FICL_PLATFORM_INLINE
void
184 ficlStackCheckNospill(ficlStack
*stack
, ficlCell
*top
, int popCells
,
188 * Why save and restore stack->top?
189 * So the simple act of stack checking doesn't force a "register" spill,
190 * which might mask bugs (places where we needed to spill but didn't).
193 ficlCell
*oldTop
= stack
->top
;
195 ficlStackCheck(stack
, popCells
, pushCells
);
199 #endif /* FICL_ROBUST <= 1 */
201 #define CHECK_STACK(pop, push) \
202 _CHECK_STACK(vm->dataStack, dataTop, pop, push)
203 #define CHECK_FLOAT_STACK(pop, push) \
204 _CHECK_STACK(vm->floatStack, floatTop, pop, push)
205 #define CHECK_RETURN_STACK(pop, push) \
206 _CHECK_STACK(vm->returnStack, returnTop, pop, push)
209 #define FLOAT_LOCAL_VARIABLE_SPILL \
210 vm->floatStack->top = floatTop;
211 #define FLOAT_LOCAL_VARIABLE_REFILL \
212 floatTop = vm->floatStack->top;
214 #define FLOAT_LOCAL_VARIABLE_SPILL
215 #define FLOAT_LOCAL_VARIABLE_REFILL
216 #endif /* FICL_WANT_FLOAT */
219 #define LOCALS_LOCAL_VARIABLE_SPILL \
220 vm->returnStack->frame = frame;
221 #define LOCALS_LOCAL_VARIABLE_REFILL \
222 frame = vm->returnStack->frame;
224 #define LOCALS_LOCAL_VARIABLE_SPILL
225 #define LOCALS_LOCAL_VARIABLE_REFILL
226 #endif /* FICL_WANT_FLOAT */
228 #define LOCAL_VARIABLE_SPILL \
229 vm->ip = (ficlIp)ip; \
230 vm->dataStack->top = dataTop; \
231 vm->returnStack->top = returnTop; \
232 FLOAT_LOCAL_VARIABLE_SPILL \
233 LOCALS_LOCAL_VARIABLE_SPILL
235 #define LOCAL_VARIABLE_REFILL \
236 ip = (ficlInstruction *)vm->ip; \
237 dataTop = vm->dataStack->top; \
238 returnTop = vm->returnStack->top; \
239 FLOAT_LOCAL_VARIABLE_REFILL \
240 LOCALS_LOCAL_VARIABLE_REFILL
243 ficlVmInnerLoop(ficlVm
*vm
, ficlWord
*fw
)
245 register ficlInstruction
*ip
;
246 register ficlCell
*dataTop
;
247 register ficlCell
*returnTop
;
249 register ficlCell
*floatTop
;
251 #endif /* FICL_WANT_FLOAT */
253 register ficlCell
*frame
;
254 #endif /* FICL_WANT_LOCALS */
255 jmp_buf *oldExceptionHandler
;
256 jmp_buf exceptionHandler
;
259 volatile int count
; /* volatile because of longjmp */
260 ficlInstruction instruction
;
264 ficlCountedString
*s
;
272 oldExceptionHandler
= vm
->exceptionHandler
;
273 /* This has to come before the setjmp! */
274 vm
->exceptionHandler
= &exceptionHandler
;
275 except
= setjmp(exceptionHandler
);
277 LOCAL_VARIABLE_REFILL
;
280 LOCAL_VARIABLE_SPILL
;
281 vm
->exceptionHandler
= oldExceptionHandler
;
282 ficlVmThrow(vm
, except
);
289 instruction
= (ficlInstruction
)((void *)fw
);
292 fw
= (ficlWord
*)instruction
;
296 switch (instruction
) {
297 case ficlInstructionInvalid
:
299 "Error: NULL instruction executed!");
302 case ficlInstruction1
:
303 case ficlInstruction2
:
304 case ficlInstruction3
:
305 case ficlInstruction4
:
306 case ficlInstruction5
:
307 case ficlInstruction6
:
308 case ficlInstruction7
:
309 case ficlInstruction8
:
310 case ficlInstruction9
:
311 case ficlInstruction10
:
312 case ficlInstruction11
:
313 case ficlInstruction12
:
314 case ficlInstruction13
:
315 case ficlInstruction14
:
316 case ficlInstruction15
:
317 case ficlInstruction16
:
319 (++dataTop
)->i
= instruction
;
322 case ficlInstruction0
:
323 case ficlInstructionNeg1
:
324 case ficlInstructionNeg2
:
325 case ficlInstructionNeg3
:
326 case ficlInstructionNeg4
:
327 case ficlInstructionNeg5
:
328 case ficlInstructionNeg6
:
329 case ficlInstructionNeg7
:
330 case ficlInstructionNeg8
:
331 case ficlInstructionNeg9
:
332 case ficlInstructionNeg10
:
333 case ficlInstructionNeg11
:
334 case ficlInstructionNeg12
:
335 case ficlInstructionNeg13
:
336 case ficlInstructionNeg14
:
337 case ficlInstructionNeg15
:
338 case ficlInstructionNeg16
:
340 (++dataTop
)->i
= ficlInstruction0
- instruction
;
344 * stringlit: Fetch the count from the dictionary, then push
345 * the address and count on the stack. Finally, update ip to
346 * point to the first aligned address after the string text.
348 case ficlInstructionStringLiteralParen
: {
349 ficlUnsigned8 length
;
352 s
= (ficlCountedString
*)(ip
);
356 (++dataTop
)->i
= length
;
359 cp
= ficlAlignPointer(cp
);
364 case ficlInstructionCStringLiteralParen
:
367 s
= (ficlCountedString
*)(ip
);
368 cp
= s
->text
+ s
->length
+ 1;
369 cp
= ficlAlignPointer(cp
);
374 #if FICL_WANT_OPTIMIZE == FICL_OPTIMIZE_FOR_SIZE
376 FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
:
377 *++floatTop
= cell
[1];
378 /* intentional fall-through */
379 FLOAT_PUSH_CELL_POINTER_MINIPROC
:
380 *++floatTop
= cell
[0];
383 FLOAT_POP_CELL_POINTER_MINIPROC
:
384 cell
[0] = *floatTop
--;
387 FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
:
388 cell
[0] = *floatTop
--;
389 cell
[1] = *floatTop
--;
392 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
393 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
394 #define FLOAT_PUSH_CELL_POINTER(cp) \
395 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_MINIPROC
396 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
397 cell = (cp); goto FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
398 #define FLOAT_POP_CELL_POINTER(cp) \
399 cell = (cp); goto FLOAT_POP_CELL_POINTER_MINIPROC
400 #endif /* FICL_WANT_FLOAT */
403 * Think of these as little mini-procedures.
406 PUSH_CELL_POINTER_DOUBLE_MINIPROC
:
407 *++dataTop
= cell
[1];
408 /* intentional fall-through */
409 PUSH_CELL_POINTER_MINIPROC
:
410 *++dataTop
= cell
[0];
413 POP_CELL_POINTER_MINIPROC
:
414 cell
[0] = *dataTop
--;
416 POP_CELL_POINTER_DOUBLE_MINIPROC
:
417 cell
[0] = *dataTop
--;
418 cell
[1] = *dataTop
--;
421 #define PUSH_CELL_POINTER_DOUBLE(cp) \
422 cell = (cp); goto PUSH_CELL_POINTER_DOUBLE_MINIPROC
423 #define PUSH_CELL_POINTER(cp) \
424 cell = (cp); goto PUSH_CELL_POINTER_MINIPROC
425 #define POP_CELL_POINTER_DOUBLE(cp) \
426 cell = (cp); goto POP_CELL_POINTER_DOUBLE_MINIPROC
427 #define POP_CELL_POINTER(cp) \
428 cell = (cp); goto POP_CELL_POINTER_MINIPROC
431 ip
+= *(ficlInteger
*)ip
;
434 #define BRANCH() goto BRANCH_MINIPROC
436 EXIT_FUNCTION_MINIPROC
:
437 ip
= (ficlInstruction
*)((returnTop
--)->p
);
440 #define EXIT_FUNCTION goto EXIT_FUNCTION_MINIPROC
442 #else /* FICL_WANT_SIZE */
445 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
446 cell = (cp); *++floatTop = cell[1]; *++floatTop = *cell; continue
447 #define FLOAT_PUSH_CELL_POINTER(cp) \
448 cell = (cp); *++floatTop = *cell; continue
449 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
450 cell = (cp); *cell = *floatTop--; cell[1] = *floatTop--; continue
451 #define FLOAT_POP_CELL_POINTER(cp) \
452 cell = (cp); *cell = *floatTop--; continue
453 #endif /* FICL_WANT_FLOAT */
455 #define PUSH_CELL_POINTER_DOUBLE(cp) \
456 cell = (cp); *++dataTop = cell[1]; *++dataTop = *cell; continue
457 #define PUSH_CELL_POINTER(cp) \
458 cell = (cp); *++dataTop = *cell; continue
459 #define POP_CELL_POINTER_DOUBLE(cp) \
460 cell = (cp); *cell = *dataTop--; cell[1] = *dataTop--; continue
461 #define POP_CELL_POINTER(cp) \
462 cell = (cp); *cell = *dataTop--; continue
464 #define BRANCH() ip += *(ficlInteger *)ip; continue
465 #define EXIT_FUNCTION() ip = (ficlInstruction *)((returnTop--)->p); continue
467 #endif /* FICL_WANT_SIZE */
471 * This is the runtime for (literal). It assumes that it is
472 * part of a colon definition, and that the next ficlCell
473 * contains a value to be pushed on the parameter stack at
474 * runtime. This code is compiled by "literal".
477 case ficlInstructionLiteralParen
:
479 (++dataTop
)->i
= *ip
++;
482 case ficlInstruction2LiteralParen
:
484 (++dataTop
)->i
= ip
[1];
485 (++dataTop
)->i
= ip
[0];
491 * Link a frame on the return stack, reserving nCells of space
492 * for locals - the value of nCells is the next ficlCell in
493 * the instruction stream.
494 * 1) Push frame onto returnTop
495 * 2) frame = returnTop
496 * 3) returnTop += nCells
498 case ficlInstructionLinkParen
: {
499 ficlInteger nCells
= *ip
++;
500 (++returnTop
)->p
= frame
;
501 frame
= returnTop
+ 1;
507 * Unink a stack frame previously created by stackLink
511 case ficlInstructionUnlinkParen
:
512 returnTop
= frame
- 1;
513 frame
= (returnTop
--)->p
;
517 * Immediate - cfa of a local while compiling - when executed,
518 * compiles code to fetch the value of a local given the
519 * local's index in the word's pfa
522 case ficlInstructionGetF2LocalParen
:
523 FLOAT_PUSH_CELL_POINTER_DOUBLE(frame
+ *ip
++);
525 case ficlInstructionGetFLocalParen
:
526 FLOAT_PUSH_CELL_POINTER(frame
+ *ip
++);
528 case ficlInstructionToF2LocalParen
:
529 FLOAT_POP_CELL_POINTER_DOUBLE(frame
+ *ip
++);
531 case ficlInstructionToFLocalParen
:
532 FLOAT_POP_CELL_POINTER(frame
+ *ip
++);
533 #endif /* FICL_WANT_FLOAT */
535 case ficlInstructionGet2LocalParen
:
536 PUSH_CELL_POINTER_DOUBLE(frame
+ *ip
++);
538 case ficlInstructionGetLocalParen
:
539 PUSH_CELL_POINTER(frame
+ *ip
++);
542 * Immediate - cfa of a local while compiling - when executed,
543 * compiles code to store the value of a local given the
544 * local's index in the word's pfa
547 case ficlInstructionTo2LocalParen
:
548 POP_CELL_POINTER_DOUBLE(frame
+ *ip
++);
550 case ficlInstructionToLocalParen
:
551 POP_CELL_POINTER(frame
+ *ip
++);
554 * Silly little minor optimizations.
557 case ficlInstructionGetLocal0
:
558 PUSH_CELL_POINTER(frame
);
560 case ficlInstructionGetLocal1
:
561 PUSH_CELL_POINTER(frame
+ 1);
563 case ficlInstructionGet2Local0
:
564 PUSH_CELL_POINTER_DOUBLE(frame
);
566 case ficlInstructionToLocal0
:
567 POP_CELL_POINTER(frame
);
569 case ficlInstructionToLocal1
:
570 POP_CELL_POINTER(frame
+ 1);
572 case ficlInstructionTo2Local0
:
573 POP_CELL_POINTER_DOUBLE(frame
);
575 #endif /* FICL_WANT_LOCALS */
577 case ficlInstructionPlus
:
583 case ficlInstructionMinus
:
589 case ficlInstruction1Plus
:
594 case ficlInstruction1Minus
:
599 case ficlInstruction2Plus
:
604 case ficlInstruction2Minus
:
609 case ficlInstructionDup
: {
610 ficlInteger i
= dataTop
->i
;
616 case ficlInstructionQuestionDup
:
619 if (dataTop
->i
!= 0) {
620 dataTop
[1] = dataTop
[0];
626 case ficlInstructionSwap
: {
630 dataTop
[0] = dataTop
[-1];
635 case ficlInstructionDrop
:
640 case ficlInstruction2Drop
:
645 case ficlInstruction2Dup
:
647 dataTop
[1] = dataTop
[-1];
648 dataTop
[2] = *dataTop
;
652 case ficlInstructionOver
:
654 dataTop
[1] = dataTop
[-1];
658 case ficlInstruction2Over
:
660 dataTop
[1] = dataTop
[-3];
661 dataTop
[2] = dataTop
[-2];
665 case ficlInstructionPick
:
670 CHECK_STACK(i
+ 2, i
+ 3);
671 *dataTop
= dataTop
[-i
- 1];
676 * rot ( 1 2 3 -- 2 3 1 )
678 case ficlInstructionRot
:
686 case ficlInstructionRoll
:
694 CHECK_STACK(i
+1, i
+2);
696 memmove(dataTop
- i
, dataTop
- (i
- 1),
697 i
* sizeof (ficlCell
));
703 * -rot ( 1 2 3 -- 3 1 2 )
705 case ficlInstructionMinusRot
:
713 case ficlInstructionMinusRoll
:
721 CHECK_STACK(i
+1, i
+2);
723 memmove(dataTop
- (i
- 1), dataTop
- i
,
724 i
* sizeof (ficlCell
));
731 * 2swap ( 1 2 3 4 -- 3 4 1 2 )
733 case ficlInstruction2Swap
: {
740 *dataTop
= dataTop
[-2];
741 dataTop
[-1] = dataTop
[-3];
748 case ficlInstructionPlusStore
: {
751 cell
= (ficlCell
*)(dataTop
--)->p
;
752 cell
->i
+= (dataTop
--)->i
;
756 case ficlInstructionQuadFetch
: {
757 ficlUnsigned32
*integer32
;
759 integer32
= (ficlUnsigned32
*)dataTop
->i
;
760 dataTop
->u
= (ficlUnsigned
)*integer32
;
764 case ficlInstructionQuadStore
: {
765 ficlUnsigned32
*integer32
;
767 integer32
= (ficlUnsigned32
*)(dataTop
--)->p
;
768 *integer32
= (ficlUnsigned32
)((dataTop
--)->u
);
772 case ficlInstructionWFetch
: {
773 ficlUnsigned16
*integer16
;
775 integer16
= (ficlUnsigned16
*)dataTop
->p
;
776 dataTop
->u
= ((ficlUnsigned
)*integer16
);
780 case ficlInstructionWStore
: {
781 ficlUnsigned16
*integer16
;
783 integer16
= (ficlUnsigned16
*)(dataTop
--)->p
;
784 *integer16
= (ficlUnsigned16
)((dataTop
--)->u
);
788 case ficlInstructionCFetch
: {
789 ficlUnsigned8
*integer8
;
791 integer8
= (ficlUnsigned8
*)dataTop
->p
;
792 dataTop
->u
= ((ficlUnsigned
)*integer8
);
796 case ficlInstructionCStore
: {
797 ficlUnsigned8
*integer8
;
799 integer8
= (ficlUnsigned8
*)(dataTop
--)->p
;
800 *integer8
= (ficlUnsigned8
)((dataTop
--)->u
);
806 * l o g i c a n d c o m p a r i s o n s
809 case ficlInstruction0Equals
:
811 dataTop
->i
= FICL_BOOL(dataTop
->i
== 0);
814 case ficlInstruction0Less
:
816 dataTop
->i
= FICL_BOOL(dataTop
->i
< 0);
819 case ficlInstruction0Greater
:
821 dataTop
->i
= FICL_BOOL(dataTop
->i
> 0);
824 case ficlInstructionEquals
:
827 dataTop
->i
= FICL_BOOL(dataTop
->i
== i
);
830 case ficlInstructionLess
:
833 dataTop
->i
= FICL_BOOL(dataTop
->i
< i
);
836 case ficlInstructionULess
:
839 dataTop
->i
= FICL_BOOL(dataTop
->u
< u
);
842 case ficlInstructionAnd
:
845 dataTop
->i
= dataTop
->i
& i
;
848 case ficlInstructionOr
:
851 dataTop
->i
= dataTop
->i
| i
;
854 case ficlInstructionXor
:
857 dataTop
->i
= dataTop
->i
^ i
;
860 case ficlInstructionInvert
:
862 dataTop
->i
= ~dataTop
->i
;
866 * r e t u r n s t a c k
868 case ficlInstructionToRStack
:
870 CHECK_RETURN_STACK(0, 1);
871 *++returnTop
= *dataTop
--;
874 case ficlInstructionFromRStack
:
876 CHECK_RETURN_STACK(1, 0);
877 *++dataTop
= *returnTop
--;
880 case ficlInstructionFetchRStack
:
882 CHECK_RETURN_STACK(1, 1);
883 *++dataTop
= *returnTop
;
886 case ficlInstruction2ToR
:
888 CHECK_RETURN_STACK(0, 2);
889 *++returnTop
= dataTop
[-1];
890 *++returnTop
= dataTop
[0];
894 case ficlInstruction2RFrom
:
896 CHECK_RETURN_STACK(2, 0);
897 *++dataTop
= returnTop
[-1];
898 *++dataTop
= returnTop
[0];
902 case ficlInstruction2RFetch
:
904 CHECK_RETURN_STACK(2, 2);
905 *++dataTop
= returnTop
[-1];
906 *++dataTop
= returnTop
[0];
911 * CORE ( c-addr u char -- )
912 * If u is greater than zero, store char in each of u
913 * consecutive characters of memory beginning at c-addr.
915 case ficlInstructionFill
: {
919 c
= (char)(dataTop
--)->i
;
921 memory
= (char *)(dataTop
--)->p
;
924 * memset() is faster than the previous hand-rolled
927 memset(memory
, c
, u
);
933 * l-shift CORE ( x1 u -- x2 )
934 * Perform a logical left shift of u bit-places on x1,
935 * giving x2. Put zeroes into the least significant bits
936 * vacated by the shift. An ambiguous condition exists if
937 * u is greater than or equal to the number of bits in a
940 * r-shift CORE ( x1 u -- x2 )
941 * Perform a logical right shift of u bit-places on x1,
942 * giving x2. Put zeroes into the most significant bits
943 * vacated by the shift. An ambiguous condition exists
944 * if u is greater than or equal to the number of bits
947 case ficlInstructionLShift
: {
952 nBits
= (dataTop
--)->u
;
954 dataTop
->u
= x1
<< nBits
;
958 case ficlInstructionRShift
: {
963 nBits
= (dataTop
--)->u
;
965 dataTop
->u
= x1
>> nBits
;
972 case ficlInstructionMax
: {
980 dataTop
->i
= ((n1
> n2
) ? n1
: n2
);
984 case ficlInstructionMin
: {
992 dataTop
->i
= ((n1
< n2
) ? n1
: n2
);
998 * CORE ( addr1 addr2 u -- )
999 * If u is greater than zero, copy the contents of u
1000 * consecutive address units at addr1 to the u consecutive
1001 * address units at addr2. After MOVE completes, the u
1002 * consecutive address units at addr2 contain exactly
1003 * what the u consecutive address units at addr1 contained
1005 * NOTE! This implementation assumes that a char is the same
1006 * size as an address unit.
1008 case ficlInstructionMove
: {
1015 addr2
= (dataTop
--)->p
;
1016 addr1
= (dataTop
--)->p
;
1021 * Do the copy carefully, so as to be
1022 * correct even if the two ranges overlap
1024 /* Which ANSI C's memmove() does for you! Yay! --lch */
1025 memmove(addr2
, addr1
, u
);
1031 * s-to-d CORE ( n -- d )
1032 * Convert the number n to the double-ficlCell number d with
1033 * the same numerical value.
1035 case ficlInstructionSToD
: {
1041 /* sign extend to 64 bits.. */
1042 (++dataTop
)->i
= (s
< 0) ? -1 : 0;
1048 * STRING ( c-addr1 u1 c-addr2 u2 -- n )
1049 * Compare the string specified by c-addr1 u1 to the string
1050 * specified by c-addr2 u2. The strings are compared, beginning
1051 * at the given addresses, character by character, up to the
1052 * length of the shorter string or until a difference is found.
1053 * If the two strings are identical, n is zero. If the two
1054 * strings are identical up to the length of the shorter string,
1055 * n is minus-one (-1) if u1 is less than u2 and one (1)
1056 * otherwise. If the two strings are not identical up to the
1057 * length of the shorter string, n is minus-one (-1) if the
1058 * first non-matching character in the string specified by
1059 * c-addr1 u1 has a lesser numeric value than the corresponding
1060 * character in the string specified by c-addr2 u2 and
1061 * one (1) otherwise.
1063 case ficlInstructionCompare
:
1068 case ficlInstructionCompareInsensitive
:
1075 ficlUnsigned u1
, u2
, uMin
;
1079 u2
= (dataTop
--)->u
;
1080 cp2
= (char *)(dataTop
--)->p
;
1081 u1
= (dataTop
--)->u
;
1082 cp1
= (char *)(dataTop
--)->p
;
1084 uMin
= (u1
< u2
)? u1
: u2
;
1085 for (; (uMin
> 0) && (n
== 0); uMin
--) {
1086 int c1
= (unsigned char)*cp1
++;
1087 int c2
= (unsigned char)*cp2
++;
1112 case ficlInstructionRandom
:
1113 (++dataTop
)->i
= random();
1117 * s e e d - r a n d o m
1120 case ficlInstructionSeedRandom
:
1121 srandom((dataTop
--)->i
);
1124 case ficlInstructionGreaterThan
: {
1129 dataTop
->i
= FICL_BOOL(x
> y
);
1133 case ficlInstructionUGreaterThan
:
1136 dataTop
->i
= FICL_BOOL(dataTop
->u
> u
);
1140 * This function simply pops the previous instruction
1141 * pointer and returns to the "next" loop. Used for exiting
1142 * from within a definition. Note that exitParen is identical
1143 * to semiParen - they are in two different functions so that
1144 * "see" can correctly identify the end of a colon definition,
1145 * even if it uses "exit".
1147 case ficlInstructionExitParen
:
1148 case ficlInstructionSemiParen
:
1152 * The first time we run "(branch)", perform a "peephole
1153 * optimization" to see if we're jumping to another
1154 * unconditional jump. If so, just jump directly there.
1156 case ficlInstructionBranchParenWithCheck
:
1157 LOCAL_VARIABLE_SPILL
;
1158 ficlVmOptimizeJumpToJump(vm
, vm
->ip
- 1);
1159 LOCAL_VARIABLE_REFILL
;
1163 * Same deal with branch0.
1165 case ficlInstructionBranch0ParenWithCheck
:
1166 LOCAL_VARIABLE_SPILL
;
1167 ficlVmOptimizeJumpToJump(vm
, vm
->ip
- 1);
1168 LOCAL_VARIABLE_REFILL
;
1169 /* intentional fall-through */
1172 * Runtime code for "(branch0)"; pop a flag from the stack,
1173 * branch if 0. fall through otherwise.
1174 * The heart of "if" and "until".
1176 case ficlInstructionBranch0Paren
:
1179 if ((dataTop
--)->i
) {
1181 * don't branch, but skip over branch
1187 /* otherwise, take branch (to else/endif/begin) */
1188 /* intentional fall-through! */
1191 * Runtime for "(branch)" -- expects a literal offset in the
1192 * next compilation address, and branches to that location.
1194 case ficlInstructionBranchParen
:
1198 case ficlInstructionOfParen
: {
1209 /* remove CASE argument */
1212 /* take branch to next of or endcase */
1219 case ficlInstructionDoParen
: {
1220 ficlCell index
, limit
;
1227 /* copy "leave" target addr to stack */
1228 (++returnTop
)->i
= *(ip
++);
1229 *++returnTop
= limit
;
1230 *++returnTop
= index
;
1235 case ficlInstructionQDoParen
: {
1236 ficlCell index
, limit
, leave
;
1245 if (limit
.u
== index
.u
) {
1249 *++returnTop
= leave
;
1250 *++returnTop
= limit
;
1251 *++returnTop
= index
;
1257 case ficlInstructionLoopParen
:
1258 case ficlInstructionPlusLoopParen
: {
1263 index
= returnTop
->i
;
1264 limit
= returnTop
[-1].i
;
1266 if (instruction
== ficlInstructionLoopParen
)
1269 ficlInteger increment
;
1271 increment
= (dataTop
--)->i
;
1273 direction
= (increment
< 0);
1276 if (direction
^ (index
>= limit
)) {
1277 /* nuke the loop indices & "leave" addr */
1279 ip
++; /* fall through the loop */
1280 } else { /* update index, branch to loop head */
1281 returnTop
->i
= index
;
1290 * Runtime code to break out of a do..loop construct
1291 * Drop the loop control variables; the branch address
1292 * past "loop" is next on the return stack.
1294 case ficlInstructionLeave
:
1300 case ficlInstructionUnloop
:
1304 case ficlInstructionI
:
1305 *++dataTop
= *returnTop
;
1308 case ficlInstructionJ
:
1309 *++dataTop
= returnTop
[-3];
1312 case ficlInstructionK
:
1313 *++dataTop
= returnTop
[-6];
1316 case ficlInstructionDoesParen
: {
1317 ficlDictionary
*dictionary
= ficlVmGetDictionary(vm
);
1318 dictionary
->smudge
->code
=
1319 (ficlPrimitive
)ficlInstructionDoDoes
;
1320 dictionary
->smudge
->param
[0].p
= ip
;
1321 ip
= (ficlInstruction
*)((returnTop
--)->p
);
1325 case ficlInstructionDoDoes
: {
1332 tempIP
= (ficlIp
)((*cell
).p
);
1333 (++dataTop
)->p
= (cell
+ 1);
1334 (++returnTop
)->p
= (void *)ip
;
1335 ip
= (ficlInstruction
*)tempIP
;
1340 case ficlInstructionF2Fetch
:
1341 CHECK_FLOAT_STACK(0, 2);
1343 FLOAT_PUSH_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1345 case ficlInstructionFFetch
:
1346 CHECK_FLOAT_STACK(0, 1);
1348 FLOAT_PUSH_CELL_POINTER((dataTop
--)->p
);
1350 case ficlInstructionF2Store
:
1351 CHECK_FLOAT_STACK(2, 0);
1353 FLOAT_POP_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1355 case ficlInstructionFStore
:
1356 CHECK_FLOAT_STACK(1, 0);
1358 FLOAT_POP_CELL_POINTER((dataTop
--)->p
);
1359 #endif /* FICL_WANT_FLOAT */
1362 * two-fetch CORE ( a-addr -- x1 x2 )
1364 * Fetch the ficlCell pair x1 x2 stored at a-addr.
1365 * x2 is stored at a-addr and x1 at the next consecutive
1366 * ficlCell. It is equivalent to the sequence
1367 * DUP ficlCell+ @ SWAP @ .
1369 case ficlInstruction2Fetch
:
1371 PUSH_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1374 * fetch CORE ( a-addr -- x )
1376 * x is the value stored at a-addr.
1378 case ficlInstructionFetch
:
1380 PUSH_CELL_POINTER((dataTop
--)->p
);
1383 * two-store CORE ( x1 x2 a-addr -- )
1384 * Store the ficlCell pair x1 x2 at a-addr, with x2 at a-addr
1385 * and x1 at the next consecutive ficlCell. It is equivalent
1386 * to the sequence SWAP OVER ! ficlCell+ !
1388 case ficlInstruction2Store
:
1390 POP_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1393 * store CORE ( x a-addr -- )
1394 * Store x at a-addr.
1396 case ficlInstructionStore
:
1398 POP_CELL_POINTER((dataTop
--)->p
);
1400 case ficlInstructionComma
: {
1401 ficlDictionary
*dictionary
;
1404 dictionary
= ficlVmGetDictionary(vm
);
1405 ficlDictionaryAppendCell(dictionary
, *dataTop
--);
1409 case ficlInstructionCComma
: {
1410 ficlDictionary
*dictionary
;
1414 dictionary
= ficlVmGetDictionary(vm
);
1415 c
= (char)(dataTop
--)->i
;
1416 ficlDictionaryAppendCharacter(dictionary
, c
);
1420 case ficlInstructionCells
:
1422 dataTop
->i
*= sizeof (ficlCell
);
1425 case ficlInstructionCellPlus
:
1427 dataTop
->i
+= sizeof (ficlCell
);
1430 case ficlInstructionStar
:
1436 case ficlInstructionNegate
:
1438 dataTop
->i
= - dataTop
->i
;
1441 case ficlInstructionSlash
:
1448 * slash-mod CORE ( n1 n2 -- n3 n4 )
1449 * Divide n1 by n2, giving the single-ficlCell remainder n3
1450 * and the single-ficlCell quotient n4. An ambiguous condition
1451 * exists if n2 is zero. If n1 and n2 differ in sign, the
1452 * implementation-defined result returned will be the
1453 * same as that returned by either the phrase
1454 * >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM.
1455 * NOTE: Ficl complies with the second phrase
1456 * (symmetric division)
1458 case ficlInstructionSlashMod
: {
1465 FICL_INTEGER_TO_2INTEGER(dataTop
[-1].i
, n1
);
1467 qr
= ficl2IntegerDivideSymmetric(n1
, n2
);
1468 dataTop
[-1].i
= qr
.remainder
;
1469 dataTop
[0].i
= FICL_2UNSIGNED_GET_LOW(qr
.quotient
);
1473 case ficlInstruction2Star
:
1478 case ficlInstruction2Slash
:
1483 case ficlInstructionStarSlash
: {
1484 ficlInteger x
, y
, z
;
1492 prod
= ficl2IntegerMultiply(x
, y
);
1493 dataTop
->i
= FICL_2UNSIGNED_GET_LOW(
1494 ficl2IntegerDivideSymmetric(prod
, z
).quotient
);
1498 case ficlInstructionStarSlashMod
: {
1499 ficlInteger x
, y
, z
;
1509 prod
= ficl2IntegerMultiply(x
, y
);
1510 qr
= ficl2IntegerDivideSymmetric(prod
, z
);
1512 dataTop
[-1].i
= qr
.remainder
;
1513 dataTop
[0].i
= FICL_2UNSIGNED_GET_LOW(qr
.quotient
);
1518 case ficlInstructionF0
:
1519 CHECK_FLOAT_STACK(0, 1);
1520 (++floatTop
)->f
= 0.0f
;
1523 case ficlInstructionF1
:
1524 CHECK_FLOAT_STACK(0, 1);
1525 (++floatTop
)->f
= 1.0f
;
1528 case ficlInstructionFNeg1
:
1529 CHECK_FLOAT_STACK(0, 1);
1530 (++floatTop
)->f
= -1.0f
;
1534 * Floating point literal execution word.
1536 case ficlInstructionFLiteralParen
:
1537 CHECK_FLOAT_STACK(0, 1);
1540 * Yes, I'm using ->i here,
1541 * but it's really a float. --lch
1543 (++floatTop
)->i
= *ip
++;
1547 * Do float addition r1 + r2.
1550 case ficlInstructionFPlus
:
1551 CHECK_FLOAT_STACK(2, 1);
1553 f
= (floatTop
--)->f
;
1558 * Do float subtraction r1 - r2.
1561 case ficlInstructionFMinus
:
1562 CHECK_FLOAT_STACK(2, 1);
1564 f
= (floatTop
--)->f
;
1569 * Do float multiplication r1 * r2.
1572 case ficlInstructionFStar
:
1573 CHECK_FLOAT_STACK(2, 1);
1575 f
= (floatTop
--)->f
;
1580 * Do float negation.
1581 * fnegate ( r -- r )
1583 case ficlInstructionFNegate
:
1584 CHECK_FLOAT_STACK(1, 1);
1586 floatTop
->f
= -(floatTop
->f
);
1590 * Do float division r1 / r2.
1593 case ficlInstructionFSlash
:
1594 CHECK_FLOAT_STACK(2, 1);
1596 f
= (floatTop
--)->f
;
1601 * Do float + integer r + n.
1604 case ficlInstructionFPlusI
:
1605 CHECK_FLOAT_STACK(1, 1);
1608 f
= (ficlFloat
)(dataTop
--)->f
;
1613 * Do float - integer r - n.
1616 case ficlInstructionFMinusI
:
1617 CHECK_FLOAT_STACK(1, 1);
1620 f
= (ficlFloat
)(dataTop
--)->f
;
1625 * Do float * integer r * n.
1628 case ficlInstructionFStarI
:
1629 CHECK_FLOAT_STACK(1, 1);
1632 f
= (ficlFloat
)(dataTop
--)->f
;
1637 * Do float / integer r / n.
1640 case ficlInstructionFSlashI
:
1641 CHECK_FLOAT_STACK(1, 1);
1644 f
= (ficlFloat
)(dataTop
--)->f
;
1649 * Do integer - float n - r.
1652 case ficlInstructionIMinusF
:
1653 CHECK_FLOAT_STACK(1, 1);
1656 f
= (ficlFloat
)(dataTop
--)->f
;
1657 floatTop
->f
= f
- floatTop
->f
;
1661 * Do integer / float n / r.
1664 case ficlInstructionISlashF
:
1665 CHECK_FLOAT_STACK(1, 1);
1668 f
= (ficlFloat
)(dataTop
--)->f
;
1669 floatTop
->f
= f
/ floatTop
->f
;
1673 * Do integer to float conversion.
1674 * int>float ( n -- r )
1676 case ficlInstructionIntToFloat
:
1678 CHECK_FLOAT_STACK(0, 1);
1680 (++floatTop
)->f
= ((dataTop
--)->f
);
1684 * Do float to integer conversion.
1685 * float>int ( r -- n )
1687 case ficlInstructionFloatToInt
:
1689 CHECK_FLOAT_STACK(1, 0);
1691 (++dataTop
)->i
= ((floatTop
--)->i
);
1695 * Add a floating point number to contents of a variable.
1698 case ficlInstructionFPlusStore
: {
1702 CHECK_FLOAT_STACK(1, 0);
1704 cell
= (ficlCell
*)(dataTop
--)->p
;
1705 cell
->f
+= (floatTop
--)->f
;
1710 * Do float stack drop.
1713 case ficlInstructionFDrop
:
1714 CHECK_FLOAT_STACK(1, 0);
1719 * Do float stack ?dup.
1722 case ficlInstructionFQuestionDup
:
1723 CHECK_FLOAT_STACK(1, 2);
1725 if (floatTop
->f
!= 0)
1731 * Do float stack dup.
1734 case ficlInstructionFDup
:
1735 CHECK_FLOAT_STACK(1, 2);
1738 floatTop
[1] = floatTop
[0];
1743 * Do float stack swap.
1744 * fswap ( r1 r2 -- r2 r1 )
1746 case ficlInstructionFSwap
:
1747 CHECK_FLOAT_STACK(2, 2);
1750 floatTop
[0] = floatTop
[-1];
1755 * Do float stack 2drop.
1758 case ficlInstructionF2Drop
:
1759 CHECK_FLOAT_STACK(2, 0);
1765 * Do float stack 2dup.
1766 * f2dup ( r1 r2 -- r1 r2 r1 r2 )
1768 case ficlInstructionF2Dup
:
1769 CHECK_FLOAT_STACK(2, 4);
1771 floatTop
[1] = floatTop
[-1];
1772 floatTop
[2] = *floatTop
;
1777 * Do float stack over.
1778 * fover ( r1 r2 -- r1 r2 r1 )
1780 case ficlInstructionFOver
:
1781 CHECK_FLOAT_STACK(2, 3);
1783 floatTop
[1] = floatTop
[-1];
1788 * Do float stack 2over.
1789 * f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
1791 case ficlInstructionF2Over
:
1792 CHECK_FLOAT_STACK(4, 6);
1794 floatTop
[1] = floatTop
[-2];
1795 floatTop
[2] = floatTop
[-1];
1800 * Do float stack pick.
1803 case ficlInstructionFPick
:
1806 CHECK_FLOAT_STACK(c
.i
+2, c
.i
+3);
1808 floatTop
[1] = floatTop
[- c
.i
- 1];
1812 * Do float stack rot.
1813 * frot ( r1 r2 r3 -- r2 r3 r1 )
1815 case ficlInstructionFRot
:
1820 * Do float stack roll.
1823 case ficlInstructionFRoll
:
1831 CHECK_FLOAT_STACK(i
+1, i
+2);
1833 memmove(floatTop
- i
, floatTop
- (i
- 1),
1834 i
* sizeof (ficlCell
));
1840 * Do float stack -rot.
1841 * f-rot ( r1 r2 r3 -- r3 r1 r2 )
1843 case ficlInstructionFMinusRot
:
1849 * Do float stack -roll.
1852 case ficlInstructionFMinusRoll
:
1860 CHECK_FLOAT_STACK(i
+1, i
+2);
1862 memmove(floatTop
- (i
- 1), floatTop
- i
,
1863 i
* sizeof (ficlCell
));
1869 * Do float stack 2swap
1870 * f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 )
1872 case ficlInstructionF2Swap
: {
1874 CHECK_FLOAT_STACK(4, 4);
1879 *floatTop
= floatTop
[-2];
1880 floatTop
[-1] = floatTop
[-3];
1888 * Do float 0= comparison r = 0.0.
1891 case ficlInstructionF0Equals
:
1892 CHECK_FLOAT_STACK(1, 0);
1895 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
!= 0.0f
);
1899 * Do float 0< comparison r < 0.0.
1902 case ficlInstructionF0Less
:
1903 CHECK_FLOAT_STACK(1, 0);
1906 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
< 0.0f
);
1910 * Do float 0> comparison r > 0.0.
1913 case ficlInstructionF0Greater
:
1914 CHECK_FLOAT_STACK(1, 0);
1917 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
> 0.0f
);
1921 * Do float = comparison r1 = r2.
1922 * f= ( r1 r2 -- T/F )
1924 case ficlInstructionFEquals
:
1925 CHECK_FLOAT_STACK(2, 0);
1928 f
= (floatTop
--)->f
;
1929 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
== f
);
1933 * Do float < comparison r1 < r2.
1934 * f< ( r1 r2 -- T/F )
1936 case ficlInstructionFLess
:
1937 CHECK_FLOAT_STACK(2, 0);
1940 f
= (floatTop
--)->f
;
1941 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
< f
);
1945 * Do float > comparison r1 > r2.
1946 * f> ( r1 r2 -- T/F )
1948 case ficlInstructionFGreater
:
1949 CHECK_FLOAT_STACK(2, 0);
1952 f
= (floatTop
--)->f
;
1953 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
> f
);
1958 * Move float to param stack (assumes they both fit in a
1959 * single ficlCell) f>s
1961 case ficlInstructionFFrom
:
1962 CHECK_FLOAT_STACK(1, 0);
1965 *++dataTop
= *floatTop
--;
1968 case ficlInstructionToF
:
1969 CHECK_FLOAT_STACK(0, 1);
1972 *++floatTop
= *dataTop
--;
1975 #endif /* FICL_WANT_FLOAT */
1978 * c o l o n P a r e n
1979 * This is the code that executes a colon definition. It
1980 * assumes that the virtual machine is running a "next" loop
1981 * (See the vm.c for its implementation of member function
1982 * vmExecute()). The colon code simply copies the address of
1983 * the first word in the list of words to interpret into IP
1984 * after saving its old value. When we return to the "next"
1985 * loop, the virtual machine will call the code for each
1988 case ficlInstructionColonParen
:
1989 (++returnTop
)->p
= (void *)ip
;
1990 ip
= (ficlInstruction
*)(fw
->param
);
1993 case ficlInstructionCreateParen
:
1995 (++dataTop
)->p
= (fw
->param
+ 1);
1998 case ficlInstructionVariableParen
:
2000 (++dataTop
)->p
= fw
->param
;
2004 * c o n s t a n t P a r e n
2005 * This is the run-time code for "constant". It simply returns
2006 * the contents of its word's first data ficlCell.
2010 case ficlInstructionF2ConstantParen
:
2011 CHECK_FLOAT_STACK(0, 2);
2012 FLOAT_PUSH_CELL_POINTER_DOUBLE(fw
->param
);
2014 case ficlInstructionFConstantParen
:
2015 CHECK_FLOAT_STACK(0, 1);
2016 FLOAT_PUSH_CELL_POINTER(fw
->param
);
2017 #endif /* FICL_WANT_FLOAT */
2019 case ficlInstruction2ConstantParen
:
2021 PUSH_CELL_POINTER_DOUBLE(fw
->param
);
2023 case ficlInstructionConstantParen
:
2025 PUSH_CELL_POINTER(fw
->param
);
2028 case ficlInstructionUserParen
: {
2029 ficlInteger i
= fw
->param
[0].i
;
2030 (++dataTop
)->p
= &vm
->user
[i
];
2037 * Clever hack, or evil coding? You be the judge.
2039 * If the word we've been asked to execute is in fact
2040 * an *instruction*, we grab the instruction, stow it
2041 * in "i" (our local cache of *ip), and *jump* to the
2042 * top of the switch statement. --lch
2044 if (((ficlInstruction
)fw
->code
>
2045 ficlInstructionInvalid
) &&
2046 ((ficlInstruction
)fw
->code
< ficlInstructionLast
)) {
2047 instruction
= (ficlInstruction
)fw
->code
;
2051 LOCAL_VARIABLE_SPILL
;
2052 (vm
)->runningWord
= fw
;
2054 LOCAL_VARIABLE_REFILL
;
2059 LOCAL_VARIABLE_SPILL
;
2060 vm
->exceptionHandler
= oldExceptionHandler
;
2065 * Returns the address dictionary for this VM's system
2068 ficlVmGetDictionary(ficlVm
*vm
)
2070 FICL_VM_ASSERT(vm
, vm
);
2071 return (vm
->callback
.system
->dictionary
);
2075 * v m G e t S t r i n g
2076 * Parses a string out of the VM input buffer and copies up to the first
2077 * FICL_COUNTED_STRING_MAX characters to the supplied destination buffer, a
2078 * ficlCountedString. The destination string is NULL terminated.
2080 * Returns the address of the first unused character in the dest buffer.
2083 ficlVmGetString(ficlVm
*vm
, ficlCountedString
*counted
, char delimiter
)
2085 ficlString s
= ficlVmParseStringEx(vm
, delimiter
, 0);
2087 if (FICL_STRING_GET_LENGTH(s
) > FICL_COUNTED_STRING_MAX
) {
2088 FICL_STRING_SET_LENGTH(s
, FICL_COUNTED_STRING_MAX
);
2091 (void) strncpy(counted
->text
, FICL_STRING_GET_POINTER(s
),
2092 FICL_STRING_GET_LENGTH(s
));
2093 counted
->text
[FICL_STRING_GET_LENGTH(s
)] = '\0';
2094 counted
->length
= (ficlUnsigned8
)FICL_STRING_GET_LENGTH(s
);
2096 return (counted
->text
+ FICL_STRING_GET_LENGTH(s
) + 1);
2101 * vmGetWord calls vmGetWord0 repeatedly until it gets a string with
2105 ficlVmGetWord(ficlVm
*vm
)
2107 ficlString s
= ficlVmGetWord0(vm
);
2109 if (FICL_STRING_GET_LENGTH(s
) == 0) {
2110 ficlVmThrow(vm
, FICL_VM_STATUS_RESTART
);
2117 * v m G e t W o r d 0
2118 * Skip leading whitespace and parse a space delimited word from the tib.
2119 * Returns the start address and length of the word. Updates the tib
2120 * to reflect characters consumed, including the trailing delimiter.
2121 * If there's nothing of interest in the tib, returns zero. This function
2122 * does not use vmParseString because it uses isspace() rather than a
2123 * single delimiter character.
2126 ficlVmGetWord0(ficlVm
*vm
)
2128 char *trace
= ficlVmGetInBuf(vm
);
2129 char *stop
= ficlVmGetInBufEnd(vm
);
2131 ficlUnsigned length
= 0;
2134 trace
= ficlStringSkipSpace(trace
, stop
);
2135 FICL_STRING_SET_POINTER(s
, trace
);
2137 /* Please leave this loop this way; it makes Purify happier. --lch */
2142 if (isspace((unsigned char)c
))
2148 FICL_STRING_SET_LENGTH(s
, length
);
2150 /* skip one trailing delimiter */
2151 if ((trace
!= stop
) && isspace((unsigned char)c
))
2154 ficlVmUpdateTib(vm
, trace
);
2160 * v m G e t W o r d T o P a d
2161 * Does vmGetWord and copies the result to the pad as a NULL terminated
2162 * string. Returns the length of the string. If the string is too long
2163 * to fit in the pad, it is truncated.
2166 ficlVmGetWordToPad(ficlVm
*vm
)
2169 char *pad
= (char *)vm
->pad
;
2170 s
= ficlVmGetWord(vm
);
2172 if (FICL_STRING_GET_LENGTH(s
) >= FICL_PAD_SIZE
)
2173 FICL_STRING_SET_LENGTH(s
, FICL_PAD_SIZE
- 1);
2175 (void) strncpy(pad
, FICL_STRING_GET_POINTER(s
),
2176 FICL_STRING_GET_LENGTH(s
));
2177 pad
[FICL_STRING_GET_LENGTH(s
)] = '\0';
2178 return ((int)(FICL_STRING_GET_LENGTH(s
)));
2182 * v m P a r s e S t r i n g
2183 * Parses a string out of the input buffer using the delimiter
2184 * specified. Skips leading delimiters, marks the start of the string,
2185 * and counts characters to the next delimiter it encounters. It then
2186 * updates the vm input buffer to consume all these chars, including the
2187 * trailing delimiter.
2188 * Returns the address and length of the parsed string, not including the
2189 * trailing delimiter.
2192 ficlVmParseString(ficlVm
*vm
, char delimiter
)
2194 return (ficlVmParseStringEx(vm
, delimiter
, 1));
2198 ficlVmParseStringEx(ficlVm
*vm
, char delimiter
, char skipLeadingDelimiters
)
2201 char *trace
= ficlVmGetInBuf(vm
);
2202 char *stop
= ficlVmGetInBufEnd(vm
);
2205 if (skipLeadingDelimiters
) {
2206 while ((trace
!= stop
) && (*trace
== delimiter
))
2210 FICL_STRING_SET_POINTER(s
, trace
); /* mark start of text */
2212 /* find next delimiter or end of line */
2214 (trace
!= stop
) && (c
!= delimiter
) && (c
!= '\r') && (c
!= '\n');
2219 /* set length of result */
2220 FICL_STRING_SET_LENGTH(s
, trace
- FICL_STRING_GET_POINTER(s
));
2222 /* gobble trailing delimiter */
2223 if ((trace
!= stop
) && (*trace
== delimiter
))
2226 ficlVmUpdateTib(vm
, trace
);
2235 ficlVmPop(ficlVm
*vm
)
2237 return (ficlStackPop(vm
->dataStack
));
2244 ficlVmPush(ficlVm
*vm
, ficlCell c
)
2246 ficlStackPush(vm
->dataStack
, c
);
2253 ficlVmPopIP(ficlVm
*vm
)
2255 vm
->ip
= (ficlIp
)(ficlStackPopPointer(vm
->returnStack
));
2262 ficlVmPushIP(ficlVm
*vm
, ficlIp newIP
)
2264 ficlStackPushPointer(vm
->returnStack
, (void *)vm
->ip
);
2270 * Binds the specified input string to the VM and clears >IN (the index)
2273 ficlVmPushTib(ficlVm
*vm
, char *text
, ficlInteger nChars
, ficlTIB
*pSaveTib
)
2276 *pSaveTib
= vm
->tib
;
2278 vm
->tib
.text
= text
;
2279 vm
->tib
.end
= text
+ nChars
;
2284 ficlVmPopTib(ficlVm
*vm
, ficlTIB
*pTib
)
2295 ficlVmQuit(ficlVm
*vm
)
2297 ficlStackReset(vm
->returnStack
);
2300 vm
->runningWord
= NULL
;
2301 vm
->state
= FICL_VM_STATE_INTERPRET
;
2302 vm
->tib
.text
= NULL
;
2313 ficlVmReset(ficlVm
*vm
)
2316 ficlStackReset(vm
->dataStack
);
2318 ficlStackReset(vm
->floatStack
);
2324 * v m S e t T e x t O u t
2325 * Binds the specified output callback to the vm. If you pass NULL,
2326 * binds the default output function (ficlTextOut)
2329 ficlVmSetTextOut(ficlVm
*vm
, ficlOutputFunction textOut
)
2331 vm
->callback
.textOut
= textOut
;
2335 ficlVmTextOut(ficlVm
*vm
, char *text
)
2337 ficlCallbackTextOut((ficlCallback
*)vm
, text
);
2342 ficlVmErrorOut(ficlVm
*vm
, char *text
)
2344 ficlCallbackErrorOut((ficlCallback
*)vm
, text
);
2352 ficlVmThrow(ficlVm
*vm
, int except
)
2354 if (vm
->exceptionHandler
)
2355 longjmp(*(vm
->exceptionHandler
), except
);
2359 ficlVmThrowError(ficlVm
*vm
, char *fmt
, ...)
2363 va_start(list
, fmt
);
2364 (void) vsprintf(vm
->pad
, fmt
, list
);
2366 (void) strcat(vm
->pad
, "\n");
2368 ficlVmErrorOut(vm
, vm
->pad
);
2369 longjmp(*(vm
->exceptionHandler
), FICL_VM_STATUS_ERROR_EXIT
);
2373 ficlVmThrowErrorVararg(ficlVm
*vm
, char *fmt
, va_list list
)
2375 (void) vsprintf(vm
->pad
, fmt
, list
);
2377 * well, we can try anyway, we're certainly not
2378 * returning to our caller!
2381 (void) strcat(vm
->pad
, "\n");
2383 ficlVmErrorOut(vm
, vm
->pad
);
2384 longjmp(*(vm
->exceptionHandler
), FICL_VM_STATUS_ERROR_EXIT
);
2388 * f i c l E v a l u a t e
2389 * Wrapper for ficlExec() which sets SOURCE-ID to -1.
2392 ficlVmEvaluate(ficlVm
*vm
, char *s
)
2395 ficlCell id
= vm
->sourceId
;
2397 vm
->sourceId
.i
= -1;
2398 FICL_STRING_SET_FROM_CSTRING(string
, s
);
2399 returnValue
= ficlVmExecuteString(vm
, string
);
2401 return (returnValue
);
2406 * Evaluates a block of input text in the context of the
2407 * specified interpreter. Emits any requested output to the
2408 * interpreter's output function.
2410 * Contains the "inner interpreter" code in a tight loop
2412 * Returns one of the VM_XXXX codes defined in ficl.h:
2413 * VM_OUTOFTEXT is the normal exit condition
2414 * VM_ERREXIT means that the interpreter encountered a syntax error
2415 * and the vm has been reset to recover (some or all
2416 * of the text block got ignored
2417 * VM_USEREXIT means that the user executed the "bye" command
2418 * to shut down the interpreter. This would be a good
2419 * time to delete the vm, etc -- or you can ignore this
2423 ficlVmExecuteString(ficlVm
*vm
, ficlString s
)
2425 ficlSystem
*system
= vm
->callback
.system
;
2426 ficlDictionary
*dictionary
= system
->dictionary
;
2431 ficlTIB saveficlTIB
;
2433 FICL_VM_ASSERT(vm
, vm
);
2434 FICL_VM_ASSERT(vm
, system
->interpreterLoop
[0]);
2436 ficlVmPushTib(vm
, FICL_STRING_GET_POINTER(s
),
2437 FICL_STRING_GET_LENGTH(s
), &saveficlTIB
);
2440 * Save and restore VM's jmp_buf to enable nested calls to ficlExec
2442 oldState
= vm
->exceptionHandler
;
2444 /* This has to come before the setjmp! */
2445 vm
->exceptionHandler
= &vmState
;
2446 except
= setjmp(vmState
);
2451 vm
->runningWord
->code(vm
);
2453 } else { /* set VM up to interpret text */
2454 ficlVmPushIP(vm
, &(system
->interpreterLoop
[0]));
2457 ficlVmInnerLoop(vm
, 0);
2460 case FICL_VM_STATUS_RESTART
:
2462 except
= FICL_VM_STATUS_OUT_OF_TEXT
;
2465 case FICL_VM_STATUS_OUT_OF_TEXT
:
2467 #if 0 /* we dont output prompt in loader */
2468 if ((vm
->state
!= FICL_VM_STATE_COMPILE
) &&
2469 (vm
->sourceId
.i
== 0))
2470 ficlVmTextOut(vm
, FICL_PROMPT
);
2474 case FICL_VM_STATUS_USER_EXIT
:
2475 case FICL_VM_STATUS_INNER_EXIT
:
2476 case FICL_VM_STATUS_BREAK
:
2479 case FICL_VM_STATUS_QUIT
:
2480 if (vm
->state
== FICL_VM_STATE_COMPILE
) {
2481 ficlDictionaryAbortDefinition(dictionary
);
2482 #if FICL_WANT_LOCALS
2483 ficlDictionaryEmpty(system
->locals
,
2484 system
->locals
->forthWordlist
->size
);
2490 case FICL_VM_STATUS_ERROR_EXIT
:
2491 case FICL_VM_STATUS_ABORT
:
2492 case FICL_VM_STATUS_ABORTQ
:
2493 default: /* user defined exit code?? */
2494 if (vm
->state
== FICL_VM_STATE_COMPILE
) {
2495 ficlDictionaryAbortDefinition(dictionary
);
2496 #if FICL_WANT_LOCALS
2497 ficlDictionaryEmpty(system
->locals
,
2498 system
->locals
->forthWordlist
->size
);
2501 ficlDictionaryResetSearchOrder(dictionary
);
2506 vm
->exceptionHandler
= oldState
;
2507 ficlVmPopTib(vm
, &saveficlTIB
);
2512 * f i c l E x e c X T
2513 * Given a pointer to a ficlWord, push an inner interpreter and
2514 * execute the word to completion. This is in contrast with vmExecute,
2515 * which does not guarantee that the word will have completed when
2516 * the function returns (ie in the case of colon definitions, which
2517 * need an inner interpreter to finish)
2519 * Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
2520 * exit condition is VM_INNEREXIT, Ficl's private signal to exit the
2521 * inner loop under normal circumstances. If another code is thrown to
2522 * exit the loop, this function will re-throw it if it's nested under
2523 * itself or ficlExec.
2525 * NOTE: this function is intended so that C code can execute ficlWords
2526 * given their address in the dictionary (xt).
2529 ficlVmExecuteXT(ficlVm
*vm
, ficlWord
*pWord
)
2534 ficlWord
*oldRunningWord
;
2536 FICL_VM_ASSERT(vm
, vm
);
2537 FICL_VM_ASSERT(vm
, vm
->callback
.system
->exitInnerWord
);
2540 * Save the runningword so that RESTART behaves correctly
2541 * over nested calls.
2543 oldRunningWord
= vm
->runningWord
;
2545 * Save and restore VM's jmp_buf to enable nested calls
2547 oldState
= vm
->exceptionHandler
;
2548 /* This has to come before the setjmp! */
2549 vm
->exceptionHandler
= &vmState
;
2550 except
= setjmp(vmState
);
2555 ficlVmPushIP(vm
, &(vm
->callback
.system
->exitInnerWord
));
2559 ficlVmExecuteWord(vm
, pWord
);
2560 ficlVmInnerLoop(vm
, 0);
2563 case FICL_VM_STATUS_INNER_EXIT
:
2564 case FICL_VM_STATUS_BREAK
:
2567 case FICL_VM_STATUS_RESTART
:
2568 case FICL_VM_STATUS_OUT_OF_TEXT
:
2569 case FICL_VM_STATUS_USER_EXIT
:
2570 case FICL_VM_STATUS_QUIT
:
2571 case FICL_VM_STATUS_ERROR_EXIT
:
2572 case FICL_VM_STATUS_ABORT
:
2573 case FICL_VM_STATUS_ABORTQ
:
2574 default: /* user defined exit code?? */
2576 vm
->exceptionHandler
= oldState
;
2577 ficlVmThrow(vm
, except
);
2582 vm
->exceptionHandler
= oldState
;
2583 vm
->runningWord
= oldRunningWord
;
2588 * f i c l P a r s e N u m b e r
2589 * Attempts to convert the NULL terminated string in the VM's pad to
2590 * a number using the VM's current base. If successful, pushes the number
2591 * onto the param stack and returns FICL_TRUE. Otherwise, returns FICL_FALSE.
2592 * (jws 8/01) Trailing decimal point causes a zero ficlCell to be pushed. (See
2593 * the standard for DOUBLE wordset.
2596 ficlVmParseNumber(ficlVm
*vm
, ficlString s
)
2598 ficlInteger accumulator
= 0;
2599 char isNegative
= 0;
2601 unsigned base
= vm
->base
;
2602 char *trace
= FICL_STRING_GET_POINTER(s
);
2603 ficlUnsigned8 length
= (ficlUnsigned8
)FICL_STRING_GET_LENGTH(s
);
2624 /* detect & remove trailing decimal */
2625 if ((length
> 0) && (trace
[length
- 1] == '.')) {
2630 if (length
== 0) /* detect "+", "-", ".", "+." etc */
2631 return (0); /* false */
2633 while ((length
--) && ((c
= *trace
++) != '\0')) {
2635 return (0); /* false */
2640 digit
= tolower(c
) - 'a' + 10;
2643 return (0); /* false */
2645 accumulator
= accumulator
* base
+ digit
;
2649 accumulator
= -accumulator
;
2651 ficlStackPushInteger(vm
->dataStack
, accumulator
);
2652 if (vm
->state
== FICL_VM_STATE_COMPILE
)
2653 ficlPrimitiveLiteralIm(vm
);
2655 if (isDouble
) { /* simple (required) DOUBLE support */
2657 ficlStackPushInteger(vm
->dataStack
, -1);
2659 ficlStackPushInteger(vm
->dataStack
, 0);
2660 if (vm
->state
== FICL_VM_STATE_COMPILE
)
2661 ficlPrimitiveLiteralIm(vm
);
2664 return (1); /* true */
2669 * Checks the dictionary for corruption and throws appropriate
2671 * Input: +n number of ADDRESS UNITS (not ficlCells) proposed to allot
2672 * -n number of ADDRESS UNITS proposed to de-allot
2673 * 0 just do a consistency check
2676 ficlVmDictionarySimpleCheck(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2678 #if FICL_ROBUST >= 1
2680 (ficlDictionaryCellsAvailable(dictionary
) *
2681 (int)sizeof (ficlCell
) < cells
)) {
2682 ficlVmThrowError(vm
, "Error: dictionary full");
2686 (ficlDictionaryCellsUsed(dictionary
) *
2687 (int)sizeof (ficlCell
) < -cells
)) {
2688 ficlVmThrowError(vm
, "Error: dictionary underflow");
2690 #else /* FICL_ROBUST >= 1 */
2692 FICL_IGNORE(dictionary
);
2694 #endif /* FICL_ROBUST >= 1 */
2698 ficlVmDictionaryCheck(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2700 #if FICL_ROBUST >= 1
2701 ficlVmDictionarySimpleCheck(vm
, dictionary
, cells
);
2703 if (dictionary
->wordlistCount
> FICL_MAX_WORDLISTS
) {
2704 ficlDictionaryResetSearchOrder(dictionary
);
2705 ficlVmThrowError(vm
, "Error: search order overflow");
2706 } else if (dictionary
->wordlistCount
< 0) {
2707 ficlDictionaryResetSearchOrder(dictionary
);
2708 ficlVmThrowError(vm
, "Error: search order underflow");
2710 #else /* FICL_ROBUST >= 1 */
2712 FICL_IGNORE(dictionary
);
2714 #endif /* FICL_ROBUST >= 1 */
2718 ficlVmDictionaryAllot(ficlVm
*vm
, ficlDictionary
*dictionary
, int n
)
2720 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm
, dictionary
, n
);
2722 ficlDictionaryAllot(dictionary
, n
);
2726 ficlVmDictionaryAllotCells(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2728 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm
, dictionary
, cells
);
2730 ficlDictionaryAllotCells(dictionary
, cells
);
2734 * f i c l P a r s e W o r d
2735 * From the standard, section 3.4
2736 * b) Search the dictionary name space (see 3.4.2). If a definition name
2737 * matching the string is found:
2738 * 1.if interpreting, perform the interpretation semantics of the definition
2739 * (see 3.4.3.2), and continue at a);
2740 * 2.if compiling, perform the compilation semantics of the definition
2741 * (see 3.4.3.3), and continue at a).
2743 * c) If a definition name matching the string is not found, attempt to
2744 * convert the string to a number (see 3.4.1.3). If successful:
2745 * 1.if interpreting, place the number on the data stack, and continue at a);
2746 * 2.if compiling, FICL_VM_STATE_COMPILE code that when executed will place
2747 * the number on the stack (see 6.1.1780 LITERAL), and continue at a);
2749 * d) If unsuccessful, an ambiguous condition exists (see 3.4.4).
2751 * (jws 4/01) Modified to be a ficlParseStep
2754 ficlVmParseWord(ficlVm
*vm
, ficlString name
)
2756 ficlDictionary
*dictionary
= ficlVmGetDictionary(vm
);
2759 FICL_VM_DICTIONARY_CHECK(vm
, dictionary
, 0);
2760 FICL_STACK_CHECK(vm
->dataStack
, 0, 0);
2762 #if FICL_WANT_LOCALS
2763 if (vm
->callback
.system
->localsCount
> 0) {
2764 tempFW
= ficlSystemLookupLocal(vm
->callback
.system
, name
);
2767 tempFW
= ficlDictionaryLookup(dictionary
, name
);
2769 if (vm
->state
== FICL_VM_STATE_INTERPRET
) {
2770 if (tempFW
!= NULL
) {
2771 if (ficlWordIsCompileOnly(tempFW
)) {
2772 ficlVmThrowError(vm
,
2773 "Error: FICL_VM_STATE_COMPILE only!");
2776 ficlVmExecuteWord(vm
, tempFW
);
2777 return (1); /* true */
2779 } else { /* (vm->state == FICL_VM_STATE_COMPILE) */
2780 if (tempFW
!= NULL
) {
2781 if (ficlWordIsImmediate(tempFW
)) {
2782 ficlVmExecuteWord(vm
, tempFW
);
2786 if (tempFW
->flags
& FICL_WORD_INSTRUCTION
)
2787 ficlDictionaryAppendUnsigned(dictionary
,
2788 (ficlInteger
)tempFW
->code
);
2790 ficlDictionaryAppendCell(dictionary
, c
);
2792 return (1); /* true */
2796 return (0); /* false */