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
53 #define FICL_VM_CHECK(vm) \
54 FICL_VM_ASSERT(vm, (*(vm->ip - 1)) == vm->runningWord)
56 #define FICL_VM_CHECK(vm)
60 * v m B r a n c h R e l a t i v e
63 ficlVmBranchRelative(ficlVm
*vm
, int offset
)
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
75 ficlVmCreate(ficlVm
*vm
, unsigned nPStack
, unsigned nRStack
)
78 vm
= (ficlVm
*)ficlMalloc(sizeof (ficlVm
));
79 FICL_ASSERT(NULL
, vm
);
80 memset(vm
, 0, sizeof (ficlVm
));
84 ficlStackDestroy(vm
->dataStack
);
85 vm
->dataStack
= ficlStackCreate(vm
, "data", nPStack
);
88 ficlStackDestroy(vm
->returnStack
);
89 vm
->returnStack
= ficlStackCreate(vm
, "return", nRStack
);
93 ficlStackDestroy(vm
->floatStack
);
94 vm
->floatStack
= ficlStackCreate(vm
, "float", nPStack
);
103 * Free all memory allocated to the specified VM and its subordinate
107 ficlVmDestroy(ficlVm
*vm
)
110 ficlFree(vm
->dataStack
);
111 ficlFree(vm
->returnStack
);
113 ficlFree(vm
->floatStack
);
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
127 ficlVmExecuteWord(ficlVm
*vm
, ficlWord
*pWord
)
129 ficlVmInnerLoop(vm
, pWord
);
133 ficlVmOptimizeJumpToJump(ficlVm
*vm
, ficlIp ip
)
136 switch ((ficlInstruction
)(*ip
)) {
137 case ficlInstructionBranchParenWithCheck
:
138 *ip
= (ficlWord
*)ficlInstructionBranchParen
;
141 case ficlInstructionBranch0ParenWithCheck
:
142 *ip
= (ficlWord
*)ficlInstructionBranch0Paren
;
145 destination
= ip
+ *(ficlInteger
*)ip
;
146 switch ((ficlInstruction
)*destination
) {
147 case ficlInstructionBranchParenWithCheck
:
148 /* preoptimize where we're jumping to */
149 ficlVmOptimizeJumpToJump(vm
, destination
);
150 case ficlInstructionBranchParen
:
152 destination
+= *(ficlInteger
*)destination
;
153 *ip
= (ficlWord
*)(destination
- ip
);
160 * v m I n n e r L o o p
161 * the mysterious inner interpreter...
162 * This loop is the address interpreter that makes colon definitions
163 * work. Upon entry, it assumes that the IP points to an entry in
164 * a definition (the body of a colon word). It runs one word at a time
165 * until something does vmThrow. The catcher for this is expected to exist
166 * in the calling code.
167 * vmThrow gets you out of this loop with a longjmp()
171 /* turn off stack checking for primitives */
172 #define _CHECK_STACK(stack, top, pop, push)
175 #define _CHECK_STACK(stack, top, pop, push) \
176 ficlStackCheckNospill(stack, top, pop, push)
178 FICL_PLATFORM_INLINE
void
179 ficlStackCheckNospill(ficlStack
*stack
, ficlCell
*top
, int popCells
,
183 * Why save and restore stack->top?
184 * So the simple act of stack checking doesn't force a "register" spill,
185 * which might mask bugs (places where we needed to spill but didn't).
188 ficlCell
*oldTop
= stack
->top
;
190 ficlStackCheck(stack
, popCells
, pushCells
);
194 #endif /* FICL_ROBUST <= 1 */
196 #define CHECK_STACK(pop, push) \
197 _CHECK_STACK(vm->dataStack, dataTop, pop, push)
198 #define CHECK_FLOAT_STACK(pop, push) \
199 _CHECK_STACK(vm->floatStack, floatTop, pop, push)
200 #define CHECK_RETURN_STACK(pop, push) \
201 _CHECK_STACK(vm->returnStack, returnTop, pop, push)
204 #define FLOAT_LOCAL_VARIABLE_SPILL \
205 vm->floatStack->top = floatTop;
206 #define FLOAT_LOCAL_VARIABLE_REFILL \
207 floatTop = vm->floatStack->top;
209 #define FLOAT_LOCAL_VARIABLE_SPILL
210 #define FLOAT_LOCAL_VARIABLE_REFILL
211 #endif /* FICL_WANT_FLOAT */
214 #define LOCALS_LOCAL_VARIABLE_SPILL \
215 vm->returnStack->frame = frame;
216 #define LOCALS_LOCAL_VARIABLE_REFILL \
217 frame = vm->returnStack->frame;
219 #define LOCALS_LOCAL_VARIABLE_SPILL
220 #define LOCALS_LOCAL_VARIABLE_REFILL
221 #endif /* FICL_WANT_FLOAT */
223 #define LOCAL_VARIABLE_SPILL \
224 vm->ip = (ficlIp)ip; \
225 vm->dataStack->top = dataTop; \
226 vm->returnStack->top = returnTop; \
227 FLOAT_LOCAL_VARIABLE_SPILL \
228 LOCALS_LOCAL_VARIABLE_SPILL
230 #define LOCAL_VARIABLE_REFILL \
231 ip = (ficlInstruction *)vm->ip; \
232 dataTop = vm->dataStack->top; \
233 returnTop = vm->returnStack->top; \
234 FLOAT_LOCAL_VARIABLE_REFILL \
235 LOCALS_LOCAL_VARIABLE_REFILL
238 ficlVmInnerLoop(ficlVm
*vm
, ficlWord
*fw
)
240 register ficlInstruction
*ip
;
241 register ficlCell
*dataTop
;
242 register ficlCell
*returnTop
;
244 register ficlCell
*floatTop
;
246 #endif /* FICL_WANT_FLOAT */
248 register ficlCell
*frame
;
249 #endif /* FICL_WANT_LOCALS */
250 jmp_buf *oldExceptionHandler
;
251 jmp_buf exceptionHandler
;
255 ficlInstruction instruction
;
259 ficlCountedString
*s
;
267 oldExceptionHandler
= vm
->exceptionHandler
;
268 /* This has to come before the setjmp! */
269 vm
->exceptionHandler
= &exceptionHandler
;
270 except
= setjmp(exceptionHandler
);
272 LOCAL_VARIABLE_REFILL
;
275 LOCAL_VARIABLE_SPILL
;
276 vm
->exceptionHandler
= oldExceptionHandler
;
277 ficlVmThrow(vm
, except
);
284 instruction
= (ficlInstruction
)((void *)fw
);
287 fw
= (ficlWord
*)instruction
;
291 switch (instruction
) {
292 case ficlInstructionInvalid
:
294 "Error: NULL instruction executed!");
297 case ficlInstruction1
:
298 case ficlInstruction2
:
299 case ficlInstruction3
:
300 case ficlInstruction4
:
301 case ficlInstruction5
:
302 case ficlInstruction6
:
303 case ficlInstruction7
:
304 case ficlInstruction8
:
305 case ficlInstruction9
:
306 case ficlInstruction10
:
307 case ficlInstruction11
:
308 case ficlInstruction12
:
309 case ficlInstruction13
:
310 case ficlInstruction14
:
311 case ficlInstruction15
:
312 case ficlInstruction16
:
314 (++dataTop
)->i
= instruction
;
317 case ficlInstruction0
:
318 case ficlInstructionNeg1
:
319 case ficlInstructionNeg2
:
320 case ficlInstructionNeg3
:
321 case ficlInstructionNeg4
:
322 case ficlInstructionNeg5
:
323 case ficlInstructionNeg6
:
324 case ficlInstructionNeg7
:
325 case ficlInstructionNeg8
:
326 case ficlInstructionNeg9
:
327 case ficlInstructionNeg10
:
328 case ficlInstructionNeg11
:
329 case ficlInstructionNeg12
:
330 case ficlInstructionNeg13
:
331 case ficlInstructionNeg14
:
332 case ficlInstructionNeg15
:
333 case ficlInstructionNeg16
:
335 (++dataTop
)->i
= ficlInstruction0
- instruction
;
339 * stringlit: Fetch the count from the dictionary, then push
340 * the address and count on the stack. Finally, update ip to
341 * point to the first aligned address after the string text.
343 case ficlInstructionStringLiteralParen
: {
344 ficlUnsigned8 length
;
347 s
= (ficlCountedString
*)(ip
);
351 (++dataTop
)->i
= length
;
354 cp
= ficlAlignPointer(cp
);
359 case ficlInstructionCStringLiteralParen
:
362 s
= (ficlCountedString
*)(ip
);
363 cp
= s
->text
+ s
->length
+ 1;
364 cp
= ficlAlignPointer(cp
);
369 #if FICL_WANT_OPTIMIZE == FICL_OPTIMIZE_FOR_SIZE
371 FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
:
372 *++floatTop
= cell
[1];
373 /* intentional fall-through */
374 FLOAT_PUSH_CELL_POINTER_MINIPROC
:
375 *++floatTop
= cell
[0];
378 FLOAT_POP_CELL_POINTER_MINIPROC
:
379 cell
[0] = *floatTop
--;
382 FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
:
383 cell
[0] = *floatTop
--;
384 cell
[1] = *floatTop
--;
387 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
388 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
389 #define FLOAT_PUSH_CELL_POINTER(cp) \
390 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_MINIPROC
391 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
392 cell = (cp); goto FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
393 #define FLOAT_POP_CELL_POINTER(cp) \
394 cell = (cp); goto FLOAT_POP_CELL_POINTER_MINIPROC
395 #endif /* FICL_WANT_FLOAT */
398 * Think of these as little mini-procedures.
401 PUSH_CELL_POINTER_DOUBLE_MINIPROC
:
402 *++dataTop
= cell
[1];
403 /* intentional fall-through */
404 PUSH_CELL_POINTER_MINIPROC
:
405 *++dataTop
= cell
[0];
408 POP_CELL_POINTER_MINIPROC
:
409 cell
[0] = *dataTop
--;
411 POP_CELL_POINTER_DOUBLE_MINIPROC
:
412 cell
[0] = *dataTop
--;
413 cell
[1] = *dataTop
--;
416 #define PUSH_CELL_POINTER_DOUBLE(cp) \
417 cell = (cp); goto PUSH_CELL_POINTER_DOUBLE_MINIPROC
418 #define PUSH_CELL_POINTER(cp) \
419 cell = (cp); goto PUSH_CELL_POINTER_MINIPROC
420 #define POP_CELL_POINTER_DOUBLE(cp) \
421 cell = (cp); goto POP_CELL_POINTER_DOUBLE_MINIPROC
422 #define POP_CELL_POINTER(cp) \
423 cell = (cp); goto POP_CELL_POINTER_MINIPROC
426 ip
+= *(ficlInteger
*)ip
;
429 #define BRANCH() goto BRANCH_MINIPROC
431 EXIT_FUNCTION_MINIPROC
:
432 ip
= (ficlInstruction
*)((returnTop
--)->p
);
435 #define EXIT_FUNCTION goto EXIT_FUNCTION_MINIPROC
437 #else /* FICL_WANT_SIZE */
440 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
441 cell = (cp); *++floatTop = cell[1]; *++floatTop = *cell; continue
442 #define FLOAT_PUSH_CELL_POINTER(cp) \
443 cell = (cp); *++floatTop = *cell; continue
444 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
445 cell = (cp); *cell = *floatTop--; cell[1] = *floatTop--; continue
446 #define FLOAT_POP_CELL_POINTER(cp) \
447 cell = (cp); *cell = *floatTop--; continue
448 #endif /* FICL_WANT_FLOAT */
450 #define PUSH_CELL_POINTER_DOUBLE(cp) \
451 cell = (cp); *++dataTop = cell[1]; *++dataTop = *cell; continue
452 #define PUSH_CELL_POINTER(cp) \
453 cell = (cp); *++dataTop = *cell; continue
454 #define POP_CELL_POINTER_DOUBLE(cp) \
455 cell = (cp); *cell = *dataTop--; cell[1] = *dataTop--; continue
456 #define POP_CELL_POINTER(cp) \
457 cell = (cp); *cell = *dataTop--; continue
459 #define BRANCH() ip += *(ficlInteger *)ip; continue
460 #define EXIT_FUNCTION() ip = (ficlInstruction *)((returnTop--)->p); continue
462 #endif /* FICL_WANT_SIZE */
466 * This is the runtime for (literal). It assumes that it is
467 * part of a colon definition, and that the next ficlCell
468 * contains a value to be pushed on the parameter stack at
469 * runtime. This code is compiled by "literal".
472 case ficlInstructionLiteralParen
:
474 (++dataTop
)->i
= *ip
++;
477 case ficlInstruction2LiteralParen
:
479 (++dataTop
)->i
= ip
[1];
480 (++dataTop
)->i
= ip
[0];
486 * Link a frame on the return stack, reserving nCells of space
487 * for locals - the value of nCells is the next ficlCell in
488 * the instruction stream.
489 * 1) Push frame onto returnTop
490 * 2) frame = returnTop
491 * 3) returnTop += nCells
493 case ficlInstructionLinkParen
: {
494 ficlInteger nCells
= *ip
++;
495 (++returnTop
)->p
= frame
;
496 frame
= returnTop
+ 1;
502 * Unink a stack frame previously created by stackLink
506 case ficlInstructionUnlinkParen
:
507 returnTop
= frame
- 1;
508 frame
= (returnTop
--)->p
;
512 * Immediate - cfa of a local while compiling - when executed,
513 * compiles code to fetch the value of a local given the
514 * local's index in the word's pfa
517 case ficlInstructionGetF2LocalParen
:
518 FLOAT_PUSH_CELL_POINTER_DOUBLE(frame
+ *ip
++);
520 case ficlInstructionGetFLocalParen
:
521 FLOAT_PUSH_CELL_POINTER(frame
+ *ip
++);
523 case ficlInstructionToF2LocalParen
:
524 FLOAT_POP_CELL_POINTER_DOUBLE(frame
+ *ip
++);
526 case ficlInstructionToFLocalParen
:
527 FLOAT_POP_CELL_POINTER(frame
+ *ip
++);
528 #endif /* FICL_WANT_FLOAT */
530 case ficlInstructionGet2LocalParen
:
531 PUSH_CELL_POINTER_DOUBLE(frame
+ *ip
++);
533 case ficlInstructionGetLocalParen
:
534 PUSH_CELL_POINTER(frame
+ *ip
++);
537 * Immediate - cfa of a local while compiling - when executed,
538 * compiles code to store the value of a local given the
539 * local's index in the word's pfa
542 case ficlInstructionTo2LocalParen
:
543 POP_CELL_POINTER_DOUBLE(frame
+ *ip
++);
545 case ficlInstructionToLocalParen
:
546 POP_CELL_POINTER(frame
+ *ip
++);
549 * Silly little minor optimizations.
552 case ficlInstructionGetLocal0
:
553 PUSH_CELL_POINTER(frame
);
555 case ficlInstructionGetLocal1
:
556 PUSH_CELL_POINTER(frame
+ 1);
558 case ficlInstructionGet2Local0
:
559 PUSH_CELL_POINTER_DOUBLE(frame
);
561 case ficlInstructionToLocal0
:
562 POP_CELL_POINTER(frame
);
564 case ficlInstructionToLocal1
:
565 POP_CELL_POINTER(frame
+ 1);
567 case ficlInstructionTo2Local0
:
568 POP_CELL_POINTER_DOUBLE(frame
);
570 #endif /* FICL_WANT_LOCALS */
572 case ficlInstructionPlus
:
578 case ficlInstructionMinus
:
584 case ficlInstruction1Plus
:
589 case ficlInstruction1Minus
:
594 case ficlInstruction2Plus
:
599 case ficlInstruction2Minus
:
604 case ficlInstructionDup
: {
605 ficlInteger i
= dataTop
->i
;
611 case ficlInstructionQuestionDup
:
614 if (dataTop
->i
!= 0) {
615 dataTop
[1] = dataTop
[0];
621 case ficlInstructionSwap
: {
625 dataTop
[0] = dataTop
[-1];
630 case ficlInstructionDrop
:
635 case ficlInstruction2Drop
:
640 case ficlInstruction2Dup
:
642 dataTop
[1] = dataTop
[-1];
643 dataTop
[2] = *dataTop
;
647 case ficlInstructionOver
:
649 dataTop
[1] = dataTop
[-1];
653 case ficlInstruction2Over
:
655 dataTop
[1] = dataTop
[-3];
656 dataTop
[2] = dataTop
[-2];
660 case ficlInstructionPick
:
665 CHECK_STACK(i
+ 2, i
+ 3);
666 *dataTop
= dataTop
[-i
- 1];
671 * rot ( 1 2 3 -- 2 3 1 )
673 case ficlInstructionRot
:
681 case ficlInstructionRoll
:
689 CHECK_STACK(i
+1, i
+2);
691 memmove(dataTop
- i
, dataTop
- (i
- 1),
692 i
* sizeof (ficlCell
));
698 * -rot ( 1 2 3 -- 3 1 2 )
700 case ficlInstructionMinusRot
:
708 case ficlInstructionMinusRoll
:
716 CHECK_STACK(i
+1, i
+2);
718 memmove(dataTop
- (i
- 1), dataTop
- i
,
719 i
* sizeof (ficlCell
));
726 * 2swap ( 1 2 3 4 -- 3 4 1 2 )
728 case ficlInstruction2Swap
: {
735 *dataTop
= dataTop
[-2];
736 dataTop
[-1] = dataTop
[-3];
743 case ficlInstructionPlusStore
: {
746 cell
= (ficlCell
*)(dataTop
--)->p
;
747 cell
->i
+= (dataTop
--)->i
;
751 case ficlInstructionQuadFetch
: {
752 ficlUnsigned32
*integer32
;
754 integer32
= (ficlUnsigned32
*)dataTop
->i
;
755 dataTop
->u
= (ficlUnsigned
)*integer32
;
759 case ficlInstructionQuadStore
: {
760 ficlUnsigned32
*integer32
;
762 integer32
= (ficlUnsigned32
*)(dataTop
--)->p
;
763 *integer32
= (ficlUnsigned32
)((dataTop
--)->u
);
767 case ficlInstructionWFetch
: {
768 ficlUnsigned16
*integer16
;
770 integer16
= (ficlUnsigned16
*)dataTop
->p
;
771 dataTop
->u
= ((ficlUnsigned
)*integer16
);
775 case ficlInstructionWStore
: {
776 ficlUnsigned16
*integer16
;
778 integer16
= (ficlUnsigned16
*)(dataTop
--)->p
;
779 *integer16
= (ficlUnsigned16
)((dataTop
--)->u
);
783 case ficlInstructionCFetch
: {
784 ficlUnsigned8
*integer8
;
786 integer8
= (ficlUnsigned8
*)dataTop
->p
;
787 dataTop
->u
= ((ficlUnsigned
)*integer8
);
791 case ficlInstructionCStore
: {
792 ficlUnsigned8
*integer8
;
794 integer8
= (ficlUnsigned8
*)(dataTop
--)->p
;
795 *integer8
= (ficlUnsigned8
)((dataTop
--)->u
);
801 * l o g i c a n d c o m p a r i s o n s
804 case ficlInstruction0Equals
:
806 dataTop
->i
= FICL_BOOL(dataTop
->i
== 0);
809 case ficlInstruction0Less
:
811 dataTop
->i
= FICL_BOOL(dataTop
->i
< 0);
814 case ficlInstruction0Greater
:
816 dataTop
->i
= FICL_BOOL(dataTop
->i
> 0);
819 case ficlInstructionEquals
:
822 dataTop
->i
= FICL_BOOL(dataTop
->i
== i
);
825 case ficlInstructionLess
:
828 dataTop
->i
= FICL_BOOL(dataTop
->i
< i
);
831 case ficlInstructionULess
:
834 dataTop
->i
= FICL_BOOL(dataTop
->u
< u
);
837 case ficlInstructionAnd
:
840 dataTop
->i
= dataTop
->i
& i
;
843 case ficlInstructionOr
:
846 dataTop
->i
= dataTop
->i
| i
;
849 case ficlInstructionXor
:
852 dataTop
->i
= dataTop
->i
^ i
;
855 case ficlInstructionInvert
:
857 dataTop
->i
= ~dataTop
->i
;
861 * r e t u r n s t a c k
863 case ficlInstructionToRStack
:
865 CHECK_RETURN_STACK(0, 1);
866 *++returnTop
= *dataTop
--;
869 case ficlInstructionFromRStack
:
871 CHECK_RETURN_STACK(1, 0);
872 *++dataTop
= *returnTop
--;
875 case ficlInstructionFetchRStack
:
877 CHECK_RETURN_STACK(1, 1);
878 *++dataTop
= *returnTop
;
881 case ficlInstruction2ToR
:
883 CHECK_RETURN_STACK(0, 2);
884 *++returnTop
= dataTop
[-1];
885 *++returnTop
= dataTop
[0];
889 case ficlInstruction2RFrom
:
891 CHECK_RETURN_STACK(2, 0);
892 *++dataTop
= returnTop
[-1];
893 *++dataTop
= returnTop
[0];
897 case ficlInstruction2RFetch
:
899 CHECK_RETURN_STACK(2, 2);
900 *++dataTop
= returnTop
[-1];
901 *++dataTop
= returnTop
[0];
906 * CORE ( c-addr u char -- )
907 * If u is greater than zero, store char in each of u
908 * consecutive characters of memory beginning at c-addr.
910 case ficlInstructionFill
: {
914 c
= (char)(dataTop
--)->i
;
916 memory
= (char *)(dataTop
--)->p
;
919 * memset() is faster than the previous hand-rolled
922 memset(memory
, c
, u
);
928 * l-shift CORE ( x1 u -- x2 )
929 * Perform a logical left shift of u bit-places on x1,
930 * giving x2. Put zeroes into the least significant bits
931 * vacated by the shift. An ambiguous condition exists if
932 * u is greater than or equal to the number of bits in a
935 * r-shift CORE ( x1 u -- x2 )
936 * Perform a logical right shift of u bit-places on x1,
937 * giving x2. Put zeroes into the most significant bits
938 * vacated by the shift. An ambiguous condition exists
939 * if u is greater than or equal to the number of bits
942 case ficlInstructionLShift
: {
947 nBits
= (dataTop
--)->u
;
949 dataTop
->u
= x1
<< nBits
;
953 case ficlInstructionRShift
: {
958 nBits
= (dataTop
--)->u
;
960 dataTop
->u
= x1
>> nBits
;
967 case ficlInstructionMax
: {
975 dataTop
->i
= ((n1
> n2
) ? n1
: n2
);
979 case ficlInstructionMin
: {
987 dataTop
->i
= ((n1
< n2
) ? n1
: n2
);
993 * CORE ( addr1 addr2 u -- )
994 * If u is greater than zero, copy the contents of u
995 * consecutive address units at addr1 to the u consecutive
996 * address units at addr2. After MOVE completes, the u
997 * consecutive address units at addr2 contain exactly
998 * what the u consecutive address units at addr1 contained
1000 * NOTE! This implementation assumes that a char is the same
1001 * size as an address unit.
1003 case ficlInstructionMove
: {
1010 addr2
= (dataTop
--)->p
;
1011 addr1
= (dataTop
--)->p
;
1016 * Do the copy carefully, so as to be
1017 * correct even if the two ranges overlap
1019 /* Which ANSI C's memmove() does for you! Yay! --lch */
1020 memmove(addr2
, addr1
, u
);
1026 * s-to-d CORE ( n -- d )
1027 * Convert the number n to the double-ficlCell number d with
1028 * the same numerical value.
1030 case ficlInstructionSToD
: {
1036 /* sign extend to 64 bits.. */
1037 (++dataTop
)->i
= (s
< 0) ? -1 : 0;
1043 * STRING ( c-addr1 u1 c-addr2 u2 -- n )
1044 * Compare the string specified by c-addr1 u1 to the string
1045 * specified by c-addr2 u2. The strings are compared, beginning
1046 * at the given addresses, character by character, up to the
1047 * length of the shorter string or until a difference is found.
1048 * If the two strings are identical, n is zero. If the two
1049 * strings are identical up to the length of the shorter string,
1050 * n is minus-one (-1) if u1 is less than u2 and one (1)
1051 * otherwise. If the two strings are not identical up to the
1052 * length of the shorter string, n is minus-one (-1) if the
1053 * first non-matching character in the string specified by
1054 * c-addr1 u1 has a lesser numeric value than the corresponding
1055 * character in the string specified by c-addr2 u2 and
1056 * one (1) otherwise.
1058 case ficlInstructionCompare
:
1063 case ficlInstructionCompareInsensitive
:
1070 ficlUnsigned u1
, u2
, uMin
;
1074 u2
= (dataTop
--)->u
;
1075 cp2
= (char *)(dataTop
--)->p
;
1076 u1
= (dataTop
--)->u
;
1077 cp1
= (char *)(dataTop
--)->p
;
1079 uMin
= (u1
< u2
)? u1
: u2
;
1080 for (; (uMin
> 0) && (n
== 0); uMin
--) {
1081 int c1
= (unsigned char)*cp1
++;
1082 int c2
= (unsigned char)*cp2
++;
1107 case ficlInstructionRandom
:
1108 (++dataTop
)->i
= random();
1112 * s e e d - r a n d o m
1115 case ficlInstructionSeedRandom
:
1116 srandom((dataTop
--)->i
);
1119 case ficlInstructionGreaterThan
: {
1124 dataTop
->i
= FICL_BOOL(x
> y
);
1127 case ficlInstructionUGreaterThan
:
1130 dataTop
->i
= FICL_BOOL(dataTop
->u
> u
);
1136 * This function simply pops the previous instruction
1137 * pointer and returns to the "next" loop. Used for exiting
1138 * from within a definition. Note that exitParen is identical
1139 * to semiParen - they are in two different functions so that
1140 * "see" can correctly identify the end of a colon definition,
1141 * even if it uses "exit".
1143 case ficlInstructionExitParen
:
1144 case ficlInstructionSemiParen
:
1148 * The first time we run "(branch)", perform a "peephole
1149 * optimization" to see if we're jumping to another
1150 * unconditional jump. If so, just jump directly there.
1152 case ficlInstructionBranchParenWithCheck
:
1153 LOCAL_VARIABLE_SPILL
;
1154 ficlVmOptimizeJumpToJump(vm
, vm
->ip
- 1);
1155 LOCAL_VARIABLE_REFILL
;
1159 * Same deal with branch0.
1161 case ficlInstructionBranch0ParenWithCheck
:
1162 LOCAL_VARIABLE_SPILL
;
1163 ficlVmOptimizeJumpToJump(vm
, vm
->ip
- 1);
1164 LOCAL_VARIABLE_REFILL
;
1165 /* intentional fall-through */
1168 * Runtime code for "(branch0)"; pop a flag from the stack,
1169 * branch if 0. fall through otherwise.
1170 * The heart of "if" and "until".
1172 case ficlInstructionBranch0Paren
:
1175 if ((dataTop
--)->i
) {
1177 * don't branch, but skip over branch
1183 /* otherwise, take branch (to else/endif/begin) */
1184 /* intentional fall-through! */
1187 * Runtime for "(branch)" -- expects a literal offset in the
1188 * next compilation address, and branches to that location.
1190 case ficlInstructionBranchParen
:
1194 case ficlInstructionOfParen
: {
1205 /* remove CASE argument */
1208 /* take branch to next of or endcase */
1215 case ficlInstructionDoParen
: {
1216 ficlCell index
, limit
;
1223 /* copy "leave" target addr to stack */
1224 (++returnTop
)->i
= *(ip
++);
1225 *++returnTop
= limit
;
1226 *++returnTop
= index
;
1231 case ficlInstructionQDoParen
: {
1232 ficlCell index
, limit
, leave
;
1241 if (limit
.u
== index
.u
) {
1245 *++returnTop
= leave
;
1246 *++returnTop
= limit
;
1247 *++returnTop
= index
;
1253 case ficlInstructionLoopParen
:
1254 case ficlInstructionPlusLoopParen
: {
1259 index
= returnTop
->i
;
1260 limit
= returnTop
[-1].i
;
1262 if (instruction
== ficlInstructionLoopParen
)
1265 ficlInteger increment
;
1267 increment
= (dataTop
--)->i
;
1269 direction
= (increment
< 0);
1272 if (direction
^ (index
>= limit
)) {
1273 /* nuke the loop indices & "leave" addr */
1275 ip
++; /* fall through the loop */
1276 } else { /* update index, branch to loop head */
1277 returnTop
->i
= index
;
1286 * Runtime code to break out of a do..loop construct
1287 * Drop the loop control variables; the branch address
1288 * past "loop" is next on the return stack.
1290 case ficlInstructionLeave
:
1296 case ficlInstructionUnloop
:
1300 case ficlInstructionI
:
1301 *++dataTop
= *returnTop
;
1304 case ficlInstructionJ
:
1305 *++dataTop
= returnTop
[-3];
1308 case ficlInstructionK
:
1309 *++dataTop
= returnTop
[-6];
1312 case ficlInstructionDoesParen
: {
1313 ficlDictionary
*dictionary
= ficlVmGetDictionary(vm
);
1314 dictionary
->smudge
->code
=
1315 (ficlPrimitive
)ficlInstructionDoDoes
;
1316 dictionary
->smudge
->param
[0].p
= ip
;
1317 ip
= (ficlInstruction
*)((returnTop
--)->p
);
1321 case ficlInstructionDoDoes
: {
1328 tempIP
= (ficlIp
)((*cell
).p
);
1329 (++dataTop
)->p
= (cell
+ 1);
1330 (++returnTop
)->p
= (void *)ip
;
1331 ip
= (ficlInstruction
*)tempIP
;
1336 case ficlInstructionF2Fetch
:
1337 CHECK_FLOAT_STACK(0, 2);
1339 FLOAT_PUSH_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1341 case ficlInstructionFFetch
:
1342 CHECK_FLOAT_STACK(0, 1);
1344 FLOAT_PUSH_CELL_POINTER((dataTop
--)->p
);
1346 case ficlInstructionF2Store
:
1347 CHECK_FLOAT_STACK(2, 0);
1349 FLOAT_POP_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1351 case ficlInstructionFStore
:
1352 CHECK_FLOAT_STACK(1, 0);
1354 FLOAT_POP_CELL_POINTER((dataTop
--)->p
);
1355 #endif /* FICL_WANT_FLOAT */
1358 * two-fetch CORE ( a-addr -- x1 x2 )
1360 * Fetch the ficlCell pair x1 x2 stored at a-addr.
1361 * x2 is stored at a-addr and x1 at the next consecutive
1362 * ficlCell. It is equivalent to the sequence
1363 * DUP ficlCell+ @ SWAP @ .
1365 case ficlInstruction2Fetch
:
1367 PUSH_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1370 * fetch CORE ( a-addr -- x )
1372 * x is the value stored at a-addr.
1374 case ficlInstructionFetch
:
1376 PUSH_CELL_POINTER((dataTop
--)->p
);
1379 * two-store CORE ( x1 x2 a-addr -- )
1380 * Store the ficlCell pair x1 x2 at a-addr, with x2 at a-addr
1381 * and x1 at the next consecutive ficlCell. It is equivalent
1382 * to the sequence SWAP OVER ! ficlCell+ !
1384 case ficlInstruction2Store
:
1386 POP_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1389 * store CORE ( x a-addr -- )
1390 * Store x at a-addr.
1392 case ficlInstructionStore
:
1394 POP_CELL_POINTER((dataTop
--)->p
);
1396 case ficlInstructionComma
: {
1397 ficlDictionary
*dictionary
;
1400 dictionary
= ficlVmGetDictionary(vm
);
1401 ficlDictionaryAppendCell(dictionary
, *dataTop
--);
1405 case ficlInstructionCComma
: {
1406 ficlDictionary
*dictionary
;
1410 dictionary
= ficlVmGetDictionary(vm
);
1411 c
= (char)(dataTop
--)->i
;
1412 ficlDictionaryAppendCharacter(dictionary
, c
);
1416 case ficlInstructionCells
:
1418 dataTop
->i
*= sizeof (ficlCell
);
1421 case ficlInstructionCellPlus
:
1423 dataTop
->i
+= sizeof (ficlCell
);
1426 case ficlInstructionStar
:
1432 case ficlInstructionNegate
:
1434 dataTop
->i
= - dataTop
->i
;
1437 case ficlInstructionSlash
:
1444 * slash-mod CORE ( n1 n2 -- n3 n4 )
1445 * Divide n1 by n2, giving the single-ficlCell remainder n3
1446 * and the single-ficlCell quotient n4. An ambiguous condition
1447 * exists if n2 is zero. If n1 and n2 differ in sign, the
1448 * implementation-defined result returned will be the
1449 * same as that returned by either the phrase
1450 * >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM.
1451 * NOTE: Ficl complies with the second phrase
1452 * (symmetric division)
1454 case ficlInstructionSlashMod
: {
1461 FICL_INTEGER_TO_2INTEGER(dataTop
[-1].i
, n1
);
1463 qr
= ficl2IntegerDivideSymmetric(n1
, n2
);
1464 dataTop
[-1].i
= qr
.remainder
;
1465 dataTop
[0].i
= FICL_2UNSIGNED_GET_LOW(qr
.quotient
);
1469 case ficlInstruction2Star
:
1474 case ficlInstruction2Slash
:
1479 case ficlInstructionStarSlash
: {
1480 ficlInteger x
, y
, z
;
1488 prod
= ficl2IntegerMultiply(x
, y
);
1489 dataTop
->i
= FICL_2UNSIGNED_GET_LOW(
1490 ficl2IntegerDivideSymmetric(prod
, z
).quotient
);
1494 case ficlInstructionStarSlashMod
: {
1495 ficlInteger x
, y
, z
;
1505 prod
= ficl2IntegerMultiply(x
, y
);
1506 qr
= ficl2IntegerDivideSymmetric(prod
, z
);
1508 dataTop
[-1].i
= qr
.remainder
;
1509 dataTop
[0].i
= FICL_2UNSIGNED_GET_LOW(qr
.quotient
);
1514 case ficlInstructionF0
:
1515 CHECK_FLOAT_STACK(0, 1);
1516 (++floatTop
)->f
= 0.0f
;
1519 case ficlInstructionF1
:
1520 CHECK_FLOAT_STACK(0, 1);
1521 (++floatTop
)->f
= 1.0f
;
1524 case ficlInstructionFNeg1
:
1525 CHECK_FLOAT_STACK(0, 1);
1526 (++floatTop
)->f
= -1.0f
;
1530 * Floating point literal execution word.
1532 case ficlInstructionFLiteralParen
:
1533 CHECK_FLOAT_STACK(0, 1);
1536 * Yes, I'm using ->i here,
1537 * but it's really a float. --lch
1539 (++floatTop
)->i
= *ip
++;
1543 * Do float addition r1 + r2.
1546 case ficlInstructionFPlus
:
1547 CHECK_FLOAT_STACK(2, 1);
1549 f
= (floatTop
--)->f
;
1554 * Do float subtraction r1 - r2.
1557 case ficlInstructionFMinus
:
1558 CHECK_FLOAT_STACK(2, 1);
1560 f
= (floatTop
--)->f
;
1565 * Do float multiplication r1 * r2.
1568 case ficlInstructionFStar
:
1569 CHECK_FLOAT_STACK(2, 1);
1571 f
= (floatTop
--)->f
;
1576 * Do float negation.
1577 * fnegate ( r -- r )
1579 case ficlInstructionFNegate
:
1580 CHECK_FLOAT_STACK(1, 1);
1582 floatTop
->f
= -(floatTop
->f
);
1586 * Do float division r1 / r2.
1589 case ficlInstructionFSlash
:
1590 CHECK_FLOAT_STACK(2, 1);
1592 f
= (floatTop
--)->f
;
1597 * Do float + integer r + n.
1600 case ficlInstructionFPlusI
:
1601 CHECK_FLOAT_STACK(1, 1);
1604 f
= (ficlFloat
)(dataTop
--)->f
;
1609 * Do float - integer r - n.
1612 case ficlInstructionFMinusI
:
1613 CHECK_FLOAT_STACK(1, 1);
1616 f
= (ficlFloat
)(dataTop
--)->f
;
1621 * Do float * integer r * n.
1624 case ficlInstructionFStarI
:
1625 CHECK_FLOAT_STACK(1, 1);
1628 f
= (ficlFloat
)(dataTop
--)->f
;
1633 * Do float / integer r / n.
1636 case ficlInstructionFSlashI
:
1637 CHECK_FLOAT_STACK(1, 1);
1640 f
= (ficlFloat
)(dataTop
--)->f
;
1645 * Do integer - float n - r.
1648 case ficlInstructionIMinusF
:
1649 CHECK_FLOAT_STACK(1, 1);
1652 f
= (ficlFloat
)(dataTop
--)->f
;
1653 floatTop
->f
= f
- floatTop
->f
;
1657 * Do integer / float n / r.
1660 case ficlInstructionISlashF
:
1661 CHECK_FLOAT_STACK(1, 1);
1664 f
= (ficlFloat
)(dataTop
--)->f
;
1665 floatTop
->f
= f
/ floatTop
->f
;
1669 * Do integer to float conversion.
1670 * int>float ( n -- r )
1672 case ficlInstructionIntToFloat
:
1674 CHECK_FLOAT_STACK(0, 1);
1676 (++floatTop
)->f
= ((dataTop
--)->f
);
1680 * Do float to integer conversion.
1681 * float>int ( r -- n )
1683 case ficlInstructionFloatToInt
:
1685 CHECK_FLOAT_STACK(1, 0);
1687 (++dataTop
)->i
= ((floatTop
--)->i
);
1691 * Add a floating point number to contents of a variable.
1694 case ficlInstructionFPlusStore
: {
1698 CHECK_FLOAT_STACK(1, 0);
1700 cell
= (ficlCell
*)(dataTop
--)->p
;
1701 cell
->f
+= (floatTop
--)->f
;
1706 * Do float stack drop.
1709 case ficlInstructionFDrop
:
1710 CHECK_FLOAT_STACK(1, 0);
1715 * Do float stack ?dup.
1718 case ficlInstructionFQuestionDup
:
1719 CHECK_FLOAT_STACK(1, 2);
1721 if (floatTop
->f
!= 0)
1727 * Do float stack dup.
1730 case ficlInstructionFDup
:
1731 CHECK_FLOAT_STACK(1, 2);
1734 floatTop
[1] = floatTop
[0];
1739 * Do float stack swap.
1740 * fswap ( r1 r2 -- r2 r1 )
1742 case ficlInstructionFSwap
:
1743 CHECK_FLOAT_STACK(2, 2);
1746 floatTop
[0] = floatTop
[-1];
1751 * Do float stack 2drop.
1754 case ficlInstructionF2Drop
:
1755 CHECK_FLOAT_STACK(2, 0);
1761 * Do float stack 2dup.
1762 * f2dup ( r1 r2 -- r1 r2 r1 r2 )
1764 case ficlInstructionF2Dup
:
1765 CHECK_FLOAT_STACK(2, 4);
1767 floatTop
[1] = floatTop
[-1];
1768 floatTop
[2] = *floatTop
;
1773 * Do float stack over.
1774 * fover ( r1 r2 -- r1 r2 r1 )
1776 case ficlInstructionFOver
:
1777 CHECK_FLOAT_STACK(2, 3);
1779 floatTop
[1] = floatTop
[-1];
1784 * Do float stack 2over.
1785 * f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
1787 case ficlInstructionF2Over
:
1788 CHECK_FLOAT_STACK(4, 6);
1790 floatTop
[1] = floatTop
[-2];
1791 floatTop
[2] = floatTop
[-1];
1796 * Do float stack pick.
1799 case ficlInstructionFPick
:
1802 CHECK_FLOAT_STACK(c
.i
+2, c
.i
+3);
1804 floatTop
[1] = floatTop
[- c
.i
- 1];
1808 * Do float stack rot.
1809 * frot ( r1 r2 r3 -- r2 r3 r1 )
1811 case ficlInstructionFRot
:
1816 * Do float stack roll.
1819 case ficlInstructionFRoll
:
1827 CHECK_FLOAT_STACK(i
+1, i
+2);
1829 memmove(floatTop
- i
, floatTop
- (i
- 1),
1830 i
* sizeof (ficlCell
));
1836 * Do float stack -rot.
1837 * f-rot ( r1 r2 r3 -- r3 r1 r2 )
1839 case ficlInstructionFMinusRot
:
1845 * Do float stack -roll.
1848 case ficlInstructionFMinusRoll
:
1856 CHECK_FLOAT_STACK(i
+1, i
+2);
1858 memmove(floatTop
- (i
- 1), floatTop
- i
,
1859 i
* sizeof (ficlCell
));
1865 * Do float stack 2swap
1866 * f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 )
1868 case ficlInstructionF2Swap
: {
1870 CHECK_FLOAT_STACK(4, 4);
1875 *floatTop
= floatTop
[-2];
1876 floatTop
[-1] = floatTop
[-3];
1884 * Do float 0= comparison r = 0.0.
1887 case ficlInstructionF0Equals
:
1888 CHECK_FLOAT_STACK(1, 0);
1891 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
!= 0.0f
);
1895 * Do float 0< comparison r < 0.0.
1898 case ficlInstructionF0Less
:
1899 CHECK_FLOAT_STACK(1, 0);
1902 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
< 0.0f
);
1906 * Do float 0> comparison r > 0.0.
1909 case ficlInstructionF0Greater
:
1910 CHECK_FLOAT_STACK(1, 0);
1913 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
> 0.0f
);
1917 * Do float = comparison r1 = r2.
1918 * f= ( r1 r2 -- T/F )
1920 case ficlInstructionFEquals
:
1921 CHECK_FLOAT_STACK(2, 0);
1924 f
= (floatTop
--)->f
;
1925 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
== f
);
1929 * Do float < comparison r1 < r2.
1930 * f< ( r1 r2 -- T/F )
1932 case ficlInstructionFLess
:
1933 CHECK_FLOAT_STACK(2, 0);
1936 f
= (floatTop
--)->f
;
1937 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
< f
);
1941 * Do float > comparison r1 > r2.
1942 * f> ( r1 r2 -- T/F )
1944 case ficlInstructionFGreater
:
1945 CHECK_FLOAT_STACK(2, 0);
1948 f
= (floatTop
--)->f
;
1949 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
> f
);
1954 * Move float to param stack (assumes they both fit in a
1955 * single ficlCell) f>s
1957 case ficlInstructionFFrom
:
1958 CHECK_FLOAT_STACK(1, 0);
1961 *++dataTop
= *floatTop
--;
1964 case ficlInstructionToF
:
1965 CHECK_FLOAT_STACK(0, 1);
1968 *++floatTop
= *dataTop
--;
1971 #endif /* FICL_WANT_FLOAT */
1974 * c o l o n P a r e n
1975 * This is the code that executes a colon definition. It
1976 * assumes that the virtual machine is running a "next" loop
1977 * (See the vm.c for its implementation of member function
1978 * vmExecute()). The colon code simply copies the address of
1979 * the first word in the list of words to interpret into IP
1980 * after saving its old value. When we return to the "next"
1981 * loop, the virtual machine will call the code for each
1984 case ficlInstructionColonParen
:
1985 (++returnTop
)->p
= (void *)ip
;
1986 ip
= (ficlInstruction
*)(fw
->param
);
1989 case ficlInstructionCreateParen
:
1991 (++dataTop
)->p
= (fw
->param
+ 1);
1994 case ficlInstructionVariableParen
:
1996 (++dataTop
)->p
= fw
->param
;
2000 * c o n s t a n t P a r e n
2001 * This is the run-time code for "constant". It simply returns
2002 * the contents of its word's first data ficlCell.
2006 case ficlInstructionF2ConstantParen
:
2007 CHECK_FLOAT_STACK(0, 2);
2008 FLOAT_PUSH_CELL_POINTER_DOUBLE(fw
->param
);
2010 case ficlInstructionFConstantParen
:
2011 CHECK_FLOAT_STACK(0, 1);
2012 FLOAT_PUSH_CELL_POINTER(fw
->param
);
2013 #endif /* FICL_WANT_FLOAT */
2015 case ficlInstruction2ConstantParen
:
2017 PUSH_CELL_POINTER_DOUBLE(fw
->param
);
2019 case ficlInstructionConstantParen
:
2021 PUSH_CELL_POINTER(fw
->param
);
2024 case ficlInstructionUserParen
: {
2025 ficlInteger i
= fw
->param
[0].i
;
2026 (++dataTop
)->p
= &vm
->user
[i
];
2033 * Clever hack, or evil coding? You be the judge.
2035 * If the word we've been asked to execute is in fact
2036 * an *instruction*, we grab the instruction, stow it
2037 * in "i" (our local cache of *ip), and *jump* to the
2038 * top of the switch statement. --lch
2040 if (((ficlInstruction
)fw
->code
>
2041 ficlInstructionInvalid
) &&
2042 ((ficlInstruction
)fw
->code
< ficlInstructionLast
)) {
2043 instruction
= (ficlInstruction
)fw
->code
;
2047 LOCAL_VARIABLE_SPILL
;
2048 (vm
)->runningWord
= fw
;
2050 LOCAL_VARIABLE_REFILL
;
2055 LOCAL_VARIABLE_SPILL
;
2056 vm
->exceptionHandler
= oldExceptionHandler
;
2061 * Returns the address dictionary for this VM's system
2064 ficlVmGetDictionary(ficlVm
*vm
)
2066 FICL_VM_ASSERT(vm
, vm
);
2067 return (vm
->callback
.system
->dictionary
);
2071 * v m G e t S t r i n g
2072 * Parses a string out of the VM input buffer and copies up to the first
2073 * FICL_COUNTED_STRING_MAX characters to the supplied destination buffer, a
2074 * ficlCountedString. The destination string is NULL terminated.
2076 * Returns the address of the first unused character in the dest buffer.
2079 ficlVmGetString(ficlVm
*vm
, ficlCountedString
*counted
, char delimiter
)
2081 ficlString s
= ficlVmParseStringEx(vm
, delimiter
, 0);
2083 if (FICL_STRING_GET_LENGTH(s
) > FICL_COUNTED_STRING_MAX
) {
2084 FICL_STRING_SET_LENGTH(s
, FICL_COUNTED_STRING_MAX
);
2087 strncpy(counted
->text
, FICL_STRING_GET_POINTER(s
),
2088 FICL_STRING_GET_LENGTH(s
));
2089 counted
->text
[FICL_STRING_GET_LENGTH(s
)] = '\0';
2090 counted
->length
= (ficlUnsigned8
)FICL_STRING_GET_LENGTH(s
);
2092 return (counted
->text
+ FICL_STRING_GET_LENGTH(s
) + 1);
2097 * vmGetWord calls vmGetWord0 repeatedly until it gets a string with
2101 ficlVmGetWord(ficlVm
*vm
)
2103 ficlString s
= ficlVmGetWord0(vm
);
2105 if (FICL_STRING_GET_LENGTH(s
) == 0) {
2106 ficlVmThrow(vm
, FICL_VM_STATUS_RESTART
);
2113 * v m G e t W o r d 0
2114 * Skip leading whitespace and parse a space delimited word from the tib.
2115 * Returns the start address and length of the word. Updates the tib
2116 * to reflect characters consumed, including the trailing delimiter.
2117 * If there's nothing of interest in the tib, returns zero. This function
2118 * does not use vmParseString because it uses isspace() rather than a
2119 * single delimiter character.
2122 ficlVmGetWord0(ficlVm
*vm
)
2124 char *trace
= ficlVmGetInBuf(vm
);
2125 char *stop
= ficlVmGetInBufEnd(vm
);
2127 ficlUnsigned length
= 0;
2130 trace
= ficlStringSkipSpace(trace
, stop
);
2131 FICL_STRING_SET_POINTER(s
, trace
);
2133 /* Please leave this loop this way; it makes Purify happier. --lch */
2138 if (isspace((unsigned char)c
))
2144 FICL_STRING_SET_LENGTH(s
, length
);
2146 /* skip one trailing delimiter */
2147 if ((trace
!= stop
) && isspace((unsigned char)c
))
2150 ficlVmUpdateTib(vm
, trace
);
2156 * v m G e t W o r d T o P a d
2157 * Does vmGetWord and copies the result to the pad as a NULL terminated
2158 * string. Returns the length of the string. If the string is too long
2159 * to fit in the pad, it is truncated.
2162 ficlVmGetWordToPad(ficlVm
*vm
)
2165 char *pad
= (char *)vm
->pad
;
2166 s
= ficlVmGetWord(vm
);
2168 if (FICL_STRING_GET_LENGTH(s
) > FICL_PAD_SIZE
)
2169 FICL_STRING_SET_LENGTH(s
, FICL_PAD_SIZE
);
2171 strncpy(pad
, FICL_STRING_GET_POINTER(s
), FICL_STRING_GET_LENGTH(s
));
2172 pad
[FICL_STRING_GET_LENGTH(s
)] = '\0';
2173 return ((int)(FICL_STRING_GET_LENGTH(s
)));
2177 * v m P a r s e S t r i n g
2178 * Parses a string out of the input buffer using the delimiter
2179 * specified. Skips leading delimiters, marks the start of the string,
2180 * and counts characters to the next delimiter it encounters. It then
2181 * updates the vm input buffer to consume all these chars, including the
2182 * trailing delimiter.
2183 * Returns the address and length of the parsed string, not including the
2184 * trailing delimiter.
2187 ficlVmParseString(ficlVm
*vm
, char delimiter
)
2189 return (ficlVmParseStringEx(vm
, delimiter
, 1));
2193 ficlVmParseStringEx(ficlVm
*vm
, char delimiter
, char skipLeadingDelimiters
)
2196 char *trace
= ficlVmGetInBuf(vm
);
2197 char *stop
= ficlVmGetInBufEnd(vm
);
2200 if (skipLeadingDelimiters
) {
2201 while ((trace
!= stop
) && (*trace
== delimiter
))
2205 FICL_STRING_SET_POINTER(s
, trace
); /* mark start of text */
2207 /* find next delimiter or end of line */
2209 (trace
!= stop
) && (c
!= delimiter
) && (c
!= '\r') && (c
!= '\n');
2214 /* set length of result */
2215 FICL_STRING_SET_LENGTH(s
, trace
- FICL_STRING_GET_POINTER(s
));
2217 /* gobble trailing delimiter */
2218 if ((trace
!= stop
) && (*trace
== delimiter
))
2221 ficlVmUpdateTib(vm
, trace
);
2230 ficlVmPop(ficlVm
*vm
)
2232 return (ficlStackPop(vm
->dataStack
));
2239 ficlVmPush(ficlVm
*vm
, ficlCell c
)
2241 ficlStackPush(vm
->dataStack
, c
);
2248 ficlVmPopIP(ficlVm
*vm
)
2250 vm
->ip
= (ficlIp
)(ficlStackPopPointer(vm
->returnStack
));
2257 ficlVmPushIP(ficlVm
*vm
, ficlIp newIP
)
2259 ficlStackPushPointer(vm
->returnStack
, (void *)vm
->ip
);
2265 * Binds the specified input string to the VM and clears >IN (the index)
2268 ficlVmPushTib(ficlVm
*vm
, char *text
, ficlInteger nChars
, ficlTIB
*pSaveTib
)
2271 *pSaveTib
= vm
->tib
;
2273 vm
->tib
.text
= text
;
2274 vm
->tib
.end
= text
+ nChars
;
2279 ficlVmPopTib(ficlVm
*vm
, ficlTIB
*pTib
)
2290 ficlVmQuit(ficlVm
*vm
)
2292 ficlStackReset(vm
->returnStack
);
2295 vm
->runningWord
= NULL
;
2296 vm
->state
= FICL_VM_STATE_INTERPRET
;
2297 vm
->tib
.text
= NULL
;
2308 ficlVmReset(ficlVm
*vm
)
2311 ficlStackReset(vm
->dataStack
);
2313 ficlStackReset(vm
->floatStack
);
2319 * v m S e t T e x t O u t
2320 * Binds the specified output callback to the vm. If you pass NULL,
2321 * binds the default output function (ficlTextOut)
2324 ficlVmSetTextOut(ficlVm
*vm
, ficlOutputFunction textOut
)
2326 vm
->callback
.textOut
= textOut
;
2330 ficlVmTextOut(ficlVm
*vm
, char *text
)
2332 ficlCallbackTextOut((ficlCallback
*)vm
, text
);
2337 ficlVmErrorOut(ficlVm
*vm
, char *text
)
2339 ficlCallbackErrorOut((ficlCallback
*)vm
, text
);
2347 ficlVmThrow(ficlVm
*vm
, int except
)
2349 if (vm
->exceptionHandler
)
2350 longjmp(*(vm
->exceptionHandler
), except
);
2354 ficlVmThrowError(ficlVm
*vm
, char *fmt
, ...)
2358 va_start(list
, fmt
);
2359 vsprintf(vm
->pad
, fmt
, list
);
2361 strcat(vm
->pad
, "\n");
2363 ficlVmErrorOut(vm
, vm
->pad
);
2364 longjmp(*(vm
->exceptionHandler
), FICL_VM_STATUS_ERROR_EXIT
);
2368 ficlVmThrowErrorVararg(ficlVm
*vm
, char *fmt
, va_list list
)
2370 vsprintf(vm
->pad
, fmt
, list
);
2372 * well, we can try anyway, we're certainly not
2373 * returning to our caller!
2376 strcat(vm
->pad
, "\n");
2378 ficlVmErrorOut(vm
, vm
->pad
);
2379 longjmp(*(vm
->exceptionHandler
), FICL_VM_STATUS_ERROR_EXIT
);
2383 * f i c l E v a l u a t e
2384 * Wrapper for ficlExec() which sets SOURCE-ID to -1.
2387 ficlVmEvaluate(ficlVm
*vm
, char *s
)
2390 ficlCell id
= vm
->sourceId
;
2392 vm
->sourceId
.i
= -1;
2393 FICL_STRING_SET_FROM_CSTRING(string
, s
);
2394 returnValue
= ficlVmExecuteString(vm
, string
);
2396 return (returnValue
);
2401 * Evaluates a block of input text in the context of the
2402 * specified interpreter. Emits any requested output to the
2403 * interpreter's output function.
2405 * Contains the "inner interpreter" code in a tight loop
2407 * Returns one of the VM_XXXX codes defined in ficl.h:
2408 * VM_OUTOFTEXT is the normal exit condition
2409 * VM_ERREXIT means that the interpreter encountered a syntax error
2410 * and the vm has been reset to recover (some or all
2411 * of the text block got ignored
2412 * VM_USEREXIT means that the user executed the "bye" command
2413 * to shut down the interpreter. This would be a good
2414 * time to delete the vm, etc -- or you can ignore this
2418 ficlVmExecuteString(ficlVm
*vm
, ficlString s
)
2420 ficlSystem
*system
= vm
->callback
.system
;
2421 ficlDictionary
*dictionary
= system
->dictionary
;
2426 ficlTIB saveficlTIB
;
2428 FICL_VM_ASSERT(vm
, vm
);
2429 FICL_VM_ASSERT(vm
, system
->interpreterLoop
[0]);
2431 ficlVmPushTib(vm
, FICL_STRING_GET_POINTER(s
),
2432 FICL_STRING_GET_LENGTH(s
), &saveficlTIB
);
2435 * Save and restore VM's jmp_buf to enable nested calls to ficlExec
2437 oldState
= vm
->exceptionHandler
;
2439 /* This has to come before the setjmp! */
2440 vm
->exceptionHandler
= &vmState
;
2441 except
= setjmp(vmState
);
2446 vm
->runningWord
->code(vm
);
2448 } else { /* set VM up to interpret text */
2449 ficlVmPushIP(vm
, &(system
->interpreterLoop
[0]));
2452 ficlVmInnerLoop(vm
, 0);
2455 case FICL_VM_STATUS_RESTART
:
2457 except
= FICL_VM_STATUS_OUT_OF_TEXT
;
2460 case FICL_VM_STATUS_OUT_OF_TEXT
:
2462 #if 0 /* we dont output prompt in loader */
2463 if ((vm
->state
!= FICL_VM_STATE_COMPILE
) &&
2464 (vm
->sourceId
.i
== 0))
2465 ficlVmTextOut(vm
, FICL_PROMPT
);
2469 case FICL_VM_STATUS_USER_EXIT
:
2470 case FICL_VM_STATUS_INNER_EXIT
:
2471 case FICL_VM_STATUS_BREAK
:
2474 case FICL_VM_STATUS_QUIT
:
2475 if (vm
->state
== FICL_VM_STATE_COMPILE
) {
2476 ficlDictionaryAbortDefinition(dictionary
);
2477 #if FICL_WANT_LOCALS
2478 ficlDictionaryEmpty(system
->locals
,
2479 system
->locals
->forthWordlist
->size
);
2485 case FICL_VM_STATUS_ERROR_EXIT
:
2486 case FICL_VM_STATUS_ABORT
:
2487 case FICL_VM_STATUS_ABORTQ
:
2488 default: /* user defined exit code?? */
2489 if (vm
->state
== FICL_VM_STATE_COMPILE
) {
2490 ficlDictionaryAbortDefinition(dictionary
);
2491 #if FICL_WANT_LOCALS
2492 ficlDictionaryEmpty(system
->locals
,
2493 system
->locals
->forthWordlist
->size
);
2496 ficlDictionaryResetSearchOrder(dictionary
);
2501 vm
->exceptionHandler
= oldState
;
2502 ficlVmPopTib(vm
, &saveficlTIB
);
2507 * f i c l E x e c X T
2508 * Given a pointer to a ficlWord, push an inner interpreter and
2509 * execute the word to completion. This is in contrast with vmExecute,
2510 * which does not guarantee that the word will have completed when
2511 * the function returns (ie in the case of colon definitions, which
2512 * need an inner interpreter to finish)
2514 * Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
2515 * exit condition is VM_INNEREXIT, Ficl's private signal to exit the
2516 * inner loop under normal circumstances. If another code is thrown to
2517 * exit the loop, this function will re-throw it if it's nested under
2518 * itself or ficlExec.
2520 * NOTE: this function is intended so that C code can execute ficlWords
2521 * given their address in the dictionary (xt).
2524 ficlVmExecuteXT(ficlVm
*vm
, ficlWord
*pWord
)
2529 ficlWord
*oldRunningWord
;
2531 FICL_VM_ASSERT(vm
, vm
);
2532 FICL_VM_ASSERT(vm
, vm
->callback
.system
->exitInnerWord
);
2535 * Save the runningword so that RESTART behaves correctly
2536 * over nested calls.
2538 oldRunningWord
= vm
->runningWord
;
2540 * Save and restore VM's jmp_buf to enable nested calls
2542 oldState
= vm
->exceptionHandler
;
2543 /* This has to come before the setjmp! */
2544 vm
->exceptionHandler
= &vmState
;
2545 except
= setjmp(vmState
);
2550 ficlVmPushIP(vm
, &(vm
->callback
.system
->exitInnerWord
));
2554 ficlVmExecuteWord(vm
, pWord
);
2555 ficlVmInnerLoop(vm
, 0);
2558 case FICL_VM_STATUS_INNER_EXIT
:
2559 case FICL_VM_STATUS_BREAK
:
2562 case FICL_VM_STATUS_RESTART
:
2563 case FICL_VM_STATUS_OUT_OF_TEXT
:
2564 case FICL_VM_STATUS_USER_EXIT
:
2565 case FICL_VM_STATUS_QUIT
:
2566 case FICL_VM_STATUS_ERROR_EXIT
:
2567 case FICL_VM_STATUS_ABORT
:
2568 case FICL_VM_STATUS_ABORTQ
:
2569 default: /* user defined exit code?? */
2571 vm
->exceptionHandler
= oldState
;
2572 ficlVmThrow(vm
, except
);
2577 vm
->exceptionHandler
= oldState
;
2578 vm
->runningWord
= oldRunningWord
;
2583 * f i c l P a r s e N u m b e r
2584 * Attempts to convert the NULL terminated string in the VM's pad to
2585 * a number using the VM's current base. If successful, pushes the number
2586 * onto the param stack and returns FICL_TRUE. Otherwise, returns FICL_FALSE.
2587 * (jws 8/01) Trailing decimal point causes a zero ficlCell to be pushed. (See
2588 * the standard for DOUBLE wordset.
2591 ficlVmParseNumber(ficlVm
*vm
, ficlString s
)
2593 ficlInteger accumulator
= 0;
2594 char isNegative
= 0;
2596 unsigned base
= vm
->base
;
2597 char *trace
= FICL_STRING_GET_POINTER(s
);
2598 ficlUnsigned8 length
= (ficlUnsigned8
)FICL_STRING_GET_LENGTH(s
);
2619 /* detect & remove trailing decimal */
2620 if ((length
> 0) && (trace
[length
- 1] == '.')) {
2625 if (length
== 0) /* detect "+", "-", ".", "+." etc */
2626 return (0); /* false */
2628 while ((length
--) && ((c
= *trace
++) != '\0')) {
2630 return (0); /* false */
2635 digit
= tolower(c
) - 'a' + 10;
2638 return (0); /* false */
2640 accumulator
= accumulator
* base
+ digit
;
2644 accumulator
= -accumulator
;
2646 ficlStackPushInteger(vm
->dataStack
, accumulator
);
2647 if (vm
->state
== FICL_VM_STATE_COMPILE
)
2648 ficlPrimitiveLiteralIm(vm
);
2650 if (isDouble
) { /* simple (required) DOUBLE support */
2652 ficlStackPushInteger(vm
->dataStack
, -1);
2654 ficlStackPushInteger(vm
->dataStack
, 0);
2655 if (vm
->state
== FICL_VM_STATE_COMPILE
)
2656 ficlPrimitiveLiteralIm(vm
);
2659 return (1); /* true */
2664 * Checks the dictionary for corruption and throws appropriate
2666 * Input: +n number of ADDRESS UNITS (not ficlCells) proposed to allot
2667 * -n number of ADDRESS UNITS proposed to de-allot
2668 * 0 just do a consistency check
2671 ficlVmDictionarySimpleCheck(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2673 #if FICL_ROBUST >= 1
2675 (ficlDictionaryCellsAvailable(dictionary
) *
2676 (int)sizeof (ficlCell
) < cells
)) {
2677 ficlVmThrowError(vm
, "Error: dictionary full");
2681 (ficlDictionaryCellsUsed(dictionary
) *
2682 (int)sizeof (ficlCell
) < -cells
)) {
2683 ficlVmThrowError(vm
, "Error: dictionary underflow");
2685 #else /* FICL_ROBUST >= 1 */
2687 FICL_IGNORE(dictionary
);
2689 #endif /* FICL_ROBUST >= 1 */
2693 ficlVmDictionaryCheck(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2695 #if FICL_ROBUST >= 1
2696 ficlVmDictionarySimpleCheck(vm
, dictionary
, cells
);
2698 if (dictionary
->wordlistCount
> FICL_MAX_WORDLISTS
) {
2699 ficlDictionaryResetSearchOrder(dictionary
);
2700 ficlVmThrowError(vm
, "Error: search order overflow");
2701 } else if (dictionary
->wordlistCount
< 0) {
2702 ficlDictionaryResetSearchOrder(dictionary
);
2703 ficlVmThrowError(vm
, "Error: search order underflow");
2705 #else /* FICL_ROBUST >= 1 */
2707 FICL_IGNORE(dictionary
);
2709 #endif /* FICL_ROBUST >= 1 */
2713 ficlVmDictionaryAllot(ficlVm
*vm
, ficlDictionary
*dictionary
, int n
)
2715 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm
, dictionary
, n
);
2717 ficlDictionaryAllot(dictionary
, n
);
2721 ficlVmDictionaryAllotCells(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2723 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm
, dictionary
, cells
);
2725 ficlDictionaryAllotCells(dictionary
, cells
);
2729 * f i c l P a r s e W o r d
2730 * From the standard, section 3.4
2731 * b) Search the dictionary name space (see 3.4.2). If a definition name
2732 * matching the string is found:
2733 * 1.if interpreting, perform the interpretation semantics of the definition
2734 * (see 3.4.3.2), and continue at a);
2735 * 2.if compiling, perform the compilation semantics of the definition
2736 * (see 3.4.3.3), and continue at a).
2738 * c) If a definition name matching the string is not found, attempt to
2739 * convert the string to a number (see 3.4.1.3). If successful:
2740 * 1.if interpreting, place the number on the data stack, and continue at a);
2741 * 2.if compiling, FICL_VM_STATE_COMPILE code that when executed will place
2742 * the number on the stack (see 6.1.1780 LITERAL), and continue at a);
2744 * d) If unsuccessful, an ambiguous condition exists (see 3.4.4).
2746 * (jws 4/01) Modified to be a ficlParseStep
2749 ficlVmParseWord(ficlVm
*vm
, ficlString name
)
2751 ficlDictionary
*dictionary
= ficlVmGetDictionary(vm
);
2754 FICL_VM_DICTIONARY_CHECK(vm
, dictionary
, 0);
2755 FICL_STACK_CHECK(vm
->dataStack
, 0, 0);
2757 #if FICL_WANT_LOCALS
2758 if (vm
->callback
.system
->localsCount
> 0) {
2759 tempFW
= ficlSystemLookupLocal(vm
->callback
.system
, name
);
2762 tempFW
= ficlDictionaryLookup(dictionary
, name
);
2764 if (vm
->state
== FICL_VM_STATE_INTERPRET
) {
2765 if (tempFW
!= NULL
) {
2766 if (ficlWordIsCompileOnly(tempFW
)) {
2767 ficlVmThrowError(vm
,
2768 "Error: FICL_VM_STATE_COMPILE only!");
2771 ficlVmExecuteWord(vm
, tempFW
);
2772 return (1); /* true */
2774 } else { /* (vm->state == FICL_VM_STATE_COMPILE) */
2775 if (tempFW
!= NULL
) {
2776 if (ficlWordIsImmediate(tempFW
)) {
2777 ficlVmExecuteWord(vm
, tempFW
);
2781 if (tempFW
->flags
& FICL_WORD_INSTRUCTION
)
2782 ficlDictionaryAppendUnsigned(dictionary
,
2783 (ficlInteger
)tempFW
->code
);
2785 ficlDictionaryAppendCell(dictionary
, c
);
2787 return (1); /* true */
2791 return (0); /* false */