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
);
151 case ficlInstructionBranchParen
:
153 destination
+= *(ficlInteger
*)destination
;
154 *ip
= (ficlWord
*)(destination
- ip
);
161 * v m I n n e r L o o p
162 * the mysterious inner interpreter...
163 * This loop is the address interpreter that makes colon definitions
164 * work. Upon entry, it assumes that the IP points to an entry in
165 * a definition (the body of a colon word). It runs one word at a time
166 * until something does vmThrow. The catcher for this is expected to exist
167 * in the calling code.
168 * vmThrow gets you out of this loop with a longjmp()
172 /* turn off stack checking for primitives */
173 #define _CHECK_STACK(stack, top, pop, push)
176 #define _CHECK_STACK(stack, top, pop, push) \
177 ficlStackCheckNospill(stack, top, pop, push)
179 FICL_PLATFORM_INLINE
void
180 ficlStackCheckNospill(ficlStack
*stack
, ficlCell
*top
, int popCells
,
184 * Why save and restore stack->top?
185 * So the simple act of stack checking doesn't force a "register" spill,
186 * which might mask bugs (places where we needed to spill but didn't).
189 ficlCell
*oldTop
= stack
->top
;
191 ficlStackCheck(stack
, popCells
, pushCells
);
195 #endif /* FICL_ROBUST <= 1 */
197 #define CHECK_STACK(pop, push) \
198 _CHECK_STACK(vm->dataStack, dataTop, pop, push)
199 #define CHECK_FLOAT_STACK(pop, push) \
200 _CHECK_STACK(vm->floatStack, floatTop, pop, push)
201 #define CHECK_RETURN_STACK(pop, push) \
202 _CHECK_STACK(vm->returnStack, returnTop, pop, push)
205 #define FLOAT_LOCAL_VARIABLE_SPILL \
206 vm->floatStack->top = floatTop;
207 #define FLOAT_LOCAL_VARIABLE_REFILL \
208 floatTop = vm->floatStack->top;
210 #define FLOAT_LOCAL_VARIABLE_SPILL
211 #define FLOAT_LOCAL_VARIABLE_REFILL
212 #endif /* FICL_WANT_FLOAT */
215 #define LOCALS_LOCAL_VARIABLE_SPILL \
216 vm->returnStack->frame = frame;
217 #define LOCALS_LOCAL_VARIABLE_REFILL \
218 frame = vm->returnStack->frame;
220 #define LOCALS_LOCAL_VARIABLE_SPILL
221 #define LOCALS_LOCAL_VARIABLE_REFILL
222 #endif /* FICL_WANT_FLOAT */
224 #define LOCAL_VARIABLE_SPILL \
225 vm->ip = (ficlIp)ip; \
226 vm->dataStack->top = dataTop; \
227 vm->returnStack->top = returnTop; \
228 FLOAT_LOCAL_VARIABLE_SPILL \
229 LOCALS_LOCAL_VARIABLE_SPILL
231 #define LOCAL_VARIABLE_REFILL \
232 ip = (ficlInstruction *)vm->ip; \
233 dataTop = vm->dataStack->top; \
234 returnTop = vm->returnStack->top; \
235 FLOAT_LOCAL_VARIABLE_REFILL \
236 LOCALS_LOCAL_VARIABLE_REFILL
239 ficlVmInnerLoop(ficlVm
*vm
, ficlWord
*fw
)
241 register ficlInstruction
*ip
;
242 register ficlCell
*dataTop
;
243 register ficlCell
*returnTop
;
245 register ficlCell
*floatTop
;
247 #endif /* FICL_WANT_FLOAT */
249 register ficlCell
*frame
;
250 #endif /* FICL_WANT_LOCALS */
251 jmp_buf *oldExceptionHandler
;
252 jmp_buf exceptionHandler
;
255 volatile int count
; /* volatile because of longjmp */
256 ficlInstruction instruction
;
260 ficlCountedString
*s
;
268 oldExceptionHandler
= vm
->exceptionHandler
;
269 /* This has to come before the setjmp! */
270 vm
->exceptionHandler
= &exceptionHandler
;
271 except
= setjmp(exceptionHandler
);
273 LOCAL_VARIABLE_REFILL
;
276 LOCAL_VARIABLE_SPILL
;
277 vm
->exceptionHandler
= oldExceptionHandler
;
278 ficlVmThrow(vm
, except
);
285 instruction
= (ficlInstruction
)((void *)fw
);
288 fw
= (ficlWord
*)instruction
;
292 switch (instruction
) {
293 case ficlInstructionInvalid
:
295 "Error: NULL instruction executed!");
298 case ficlInstruction1
:
299 case ficlInstruction2
:
300 case ficlInstruction3
:
301 case ficlInstruction4
:
302 case ficlInstruction5
:
303 case ficlInstruction6
:
304 case ficlInstruction7
:
305 case ficlInstruction8
:
306 case ficlInstruction9
:
307 case ficlInstruction10
:
308 case ficlInstruction11
:
309 case ficlInstruction12
:
310 case ficlInstruction13
:
311 case ficlInstruction14
:
312 case ficlInstruction15
:
313 case ficlInstruction16
:
315 (++dataTop
)->i
= instruction
;
318 case ficlInstruction0
:
319 case ficlInstructionNeg1
:
320 case ficlInstructionNeg2
:
321 case ficlInstructionNeg3
:
322 case ficlInstructionNeg4
:
323 case ficlInstructionNeg5
:
324 case ficlInstructionNeg6
:
325 case ficlInstructionNeg7
:
326 case ficlInstructionNeg8
:
327 case ficlInstructionNeg9
:
328 case ficlInstructionNeg10
:
329 case ficlInstructionNeg11
:
330 case ficlInstructionNeg12
:
331 case ficlInstructionNeg13
:
332 case ficlInstructionNeg14
:
333 case ficlInstructionNeg15
:
334 case ficlInstructionNeg16
:
336 (++dataTop
)->i
= ficlInstruction0
- instruction
;
340 * stringlit: Fetch the count from the dictionary, then push
341 * the address and count on the stack. Finally, update ip to
342 * point to the first aligned address after the string text.
344 case ficlInstructionStringLiteralParen
: {
345 ficlUnsigned8 length
;
348 s
= (ficlCountedString
*)(ip
);
352 (++dataTop
)->i
= length
;
355 cp
= ficlAlignPointer(cp
);
360 case ficlInstructionCStringLiteralParen
:
363 s
= (ficlCountedString
*)(ip
);
364 cp
= s
->text
+ s
->length
+ 1;
365 cp
= ficlAlignPointer(cp
);
370 #if FICL_WANT_OPTIMIZE == FICL_OPTIMIZE_FOR_SIZE
372 FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
:
373 *++floatTop
= cell
[1];
374 /* intentional fall-through */
375 FLOAT_PUSH_CELL_POINTER_MINIPROC
:
376 *++floatTop
= cell
[0];
379 FLOAT_POP_CELL_POINTER_MINIPROC
:
380 cell
[0] = *floatTop
--;
383 FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
:
384 cell
[0] = *floatTop
--;
385 cell
[1] = *floatTop
--;
388 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
389 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
390 #define FLOAT_PUSH_CELL_POINTER(cp) \
391 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_MINIPROC
392 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
393 cell = (cp); goto FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
394 #define FLOAT_POP_CELL_POINTER(cp) \
395 cell = (cp); goto FLOAT_POP_CELL_POINTER_MINIPROC
396 #endif /* FICL_WANT_FLOAT */
399 * Think of these as little mini-procedures.
402 PUSH_CELL_POINTER_DOUBLE_MINIPROC
:
403 *++dataTop
= cell
[1];
404 /* intentional fall-through */
405 PUSH_CELL_POINTER_MINIPROC
:
406 *++dataTop
= cell
[0];
409 POP_CELL_POINTER_MINIPROC
:
410 cell
[0] = *dataTop
--;
412 POP_CELL_POINTER_DOUBLE_MINIPROC
:
413 cell
[0] = *dataTop
--;
414 cell
[1] = *dataTop
--;
417 #define PUSH_CELL_POINTER_DOUBLE(cp) \
418 cell = (cp); goto PUSH_CELL_POINTER_DOUBLE_MINIPROC
419 #define PUSH_CELL_POINTER(cp) \
420 cell = (cp); goto PUSH_CELL_POINTER_MINIPROC
421 #define POP_CELL_POINTER_DOUBLE(cp) \
422 cell = (cp); goto POP_CELL_POINTER_DOUBLE_MINIPROC
423 #define POP_CELL_POINTER(cp) \
424 cell = (cp); goto POP_CELL_POINTER_MINIPROC
427 ip
+= *(ficlInteger
*)ip
;
430 #define BRANCH() goto BRANCH_MINIPROC
432 EXIT_FUNCTION_MINIPROC
:
433 ip
= (ficlInstruction
*)((returnTop
--)->p
);
436 #define EXIT_FUNCTION goto EXIT_FUNCTION_MINIPROC
438 #else /* FICL_WANT_SIZE */
441 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \
442 cell = (cp); *++floatTop = cell[1]; *++floatTop = *cell; continue
443 #define FLOAT_PUSH_CELL_POINTER(cp) \
444 cell = (cp); *++floatTop = *cell; continue
445 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \
446 cell = (cp); *cell = *floatTop--; cell[1] = *floatTop--; continue
447 #define FLOAT_POP_CELL_POINTER(cp) \
448 cell = (cp); *cell = *floatTop--; continue
449 #endif /* FICL_WANT_FLOAT */
451 #define PUSH_CELL_POINTER_DOUBLE(cp) \
452 cell = (cp); *++dataTop = cell[1]; *++dataTop = *cell; continue
453 #define PUSH_CELL_POINTER(cp) \
454 cell = (cp); *++dataTop = *cell; continue
455 #define POP_CELL_POINTER_DOUBLE(cp) \
456 cell = (cp); *cell = *dataTop--; cell[1] = *dataTop--; continue
457 #define POP_CELL_POINTER(cp) \
458 cell = (cp); *cell = *dataTop--; continue
460 #define BRANCH() ip += *(ficlInteger *)ip; continue
461 #define EXIT_FUNCTION() ip = (ficlInstruction *)((returnTop--)->p); continue
463 #endif /* FICL_WANT_SIZE */
467 * This is the runtime for (literal). It assumes that it is
468 * part of a colon definition, and that the next ficlCell
469 * contains a value to be pushed on the parameter stack at
470 * runtime. This code is compiled by "literal".
473 case ficlInstructionLiteralParen
:
475 (++dataTop
)->i
= *ip
++;
478 case ficlInstruction2LiteralParen
:
480 (++dataTop
)->i
= ip
[1];
481 (++dataTop
)->i
= ip
[0];
487 * Link a frame on the return stack, reserving nCells of space
488 * for locals - the value of nCells is the next ficlCell in
489 * the instruction stream.
490 * 1) Push frame onto returnTop
491 * 2) frame = returnTop
492 * 3) returnTop += nCells
494 case ficlInstructionLinkParen
: {
495 ficlInteger nCells
= *ip
++;
496 (++returnTop
)->p
= frame
;
497 frame
= returnTop
+ 1;
503 * Unink a stack frame previously created by stackLink
507 case ficlInstructionUnlinkParen
:
508 returnTop
= frame
- 1;
509 frame
= (returnTop
--)->p
;
513 * Immediate - cfa of a local while compiling - when executed,
514 * compiles code to fetch the value of a local given the
515 * local's index in the word's pfa
518 case ficlInstructionGetF2LocalParen
:
519 FLOAT_PUSH_CELL_POINTER_DOUBLE(frame
+ *ip
++);
521 case ficlInstructionGetFLocalParen
:
522 FLOAT_PUSH_CELL_POINTER(frame
+ *ip
++);
524 case ficlInstructionToF2LocalParen
:
525 FLOAT_POP_CELL_POINTER_DOUBLE(frame
+ *ip
++);
527 case ficlInstructionToFLocalParen
:
528 FLOAT_POP_CELL_POINTER(frame
+ *ip
++);
529 #endif /* FICL_WANT_FLOAT */
531 case ficlInstructionGet2LocalParen
:
532 PUSH_CELL_POINTER_DOUBLE(frame
+ *ip
++);
534 case ficlInstructionGetLocalParen
:
535 PUSH_CELL_POINTER(frame
+ *ip
++);
538 * Immediate - cfa of a local while compiling - when executed,
539 * compiles code to store the value of a local given the
540 * local's index in the word's pfa
543 case ficlInstructionTo2LocalParen
:
544 POP_CELL_POINTER_DOUBLE(frame
+ *ip
++);
546 case ficlInstructionToLocalParen
:
547 POP_CELL_POINTER(frame
+ *ip
++);
550 * Silly little minor optimizations.
553 case ficlInstructionGetLocal0
:
554 PUSH_CELL_POINTER(frame
);
556 case ficlInstructionGetLocal1
:
557 PUSH_CELL_POINTER(frame
+ 1);
559 case ficlInstructionGet2Local0
:
560 PUSH_CELL_POINTER_DOUBLE(frame
);
562 case ficlInstructionToLocal0
:
563 POP_CELL_POINTER(frame
);
565 case ficlInstructionToLocal1
:
566 POP_CELL_POINTER(frame
+ 1);
568 case ficlInstructionTo2Local0
:
569 POP_CELL_POINTER_DOUBLE(frame
);
571 #endif /* FICL_WANT_LOCALS */
573 case ficlInstructionPlus
:
579 case ficlInstructionMinus
:
585 case ficlInstruction1Plus
:
590 case ficlInstruction1Minus
:
595 case ficlInstruction2Plus
:
600 case ficlInstruction2Minus
:
605 case ficlInstructionDup
: {
606 ficlInteger i
= dataTop
->i
;
612 case ficlInstructionQuestionDup
:
615 if (dataTop
->i
!= 0) {
616 dataTop
[1] = dataTop
[0];
622 case ficlInstructionSwap
: {
626 dataTop
[0] = dataTop
[-1];
631 case ficlInstructionDrop
:
636 case ficlInstruction2Drop
:
641 case ficlInstruction2Dup
:
643 dataTop
[1] = dataTop
[-1];
644 dataTop
[2] = *dataTop
;
648 case ficlInstructionOver
:
650 dataTop
[1] = dataTop
[-1];
654 case ficlInstruction2Over
:
656 dataTop
[1] = dataTop
[-3];
657 dataTop
[2] = dataTop
[-2];
661 case ficlInstructionPick
:
666 CHECK_STACK(i
+ 2, i
+ 3);
667 *dataTop
= dataTop
[-i
- 1];
672 * rot ( 1 2 3 -- 2 3 1 )
674 case ficlInstructionRot
:
682 case ficlInstructionRoll
:
690 CHECK_STACK(i
+1, i
+2);
692 memmove(dataTop
- i
, dataTop
- (i
- 1),
693 i
* sizeof (ficlCell
));
699 * -rot ( 1 2 3 -- 3 1 2 )
701 case ficlInstructionMinusRot
:
709 case ficlInstructionMinusRoll
:
717 CHECK_STACK(i
+1, i
+2);
719 memmove(dataTop
- (i
- 1), dataTop
- i
,
720 i
* sizeof (ficlCell
));
727 * 2swap ( 1 2 3 4 -- 3 4 1 2 )
729 case ficlInstruction2Swap
: {
736 *dataTop
= dataTop
[-2];
737 dataTop
[-1] = dataTop
[-3];
744 case ficlInstructionPlusStore
: {
747 cell
= (ficlCell
*)(dataTop
--)->p
;
748 cell
->i
+= (dataTop
--)->i
;
752 case ficlInstructionQuadFetch
: {
753 ficlUnsigned32
*integer32
;
755 integer32
= (ficlUnsigned32
*)dataTop
->i
;
756 dataTop
->u
= (ficlUnsigned
)*integer32
;
760 case ficlInstructionQuadStore
: {
761 ficlUnsigned32
*integer32
;
763 integer32
= (ficlUnsigned32
*)(dataTop
--)->p
;
764 *integer32
= (ficlUnsigned32
)((dataTop
--)->u
);
768 case ficlInstructionWFetch
: {
769 ficlUnsigned16
*integer16
;
771 integer16
= (ficlUnsigned16
*)dataTop
->p
;
772 dataTop
->u
= ((ficlUnsigned
)*integer16
);
776 case ficlInstructionWStore
: {
777 ficlUnsigned16
*integer16
;
779 integer16
= (ficlUnsigned16
*)(dataTop
--)->p
;
780 *integer16
= (ficlUnsigned16
)((dataTop
--)->u
);
784 case ficlInstructionCFetch
: {
785 ficlUnsigned8
*integer8
;
787 integer8
= (ficlUnsigned8
*)dataTop
->p
;
788 dataTop
->u
= ((ficlUnsigned
)*integer8
);
792 case ficlInstructionCStore
: {
793 ficlUnsigned8
*integer8
;
795 integer8
= (ficlUnsigned8
*)(dataTop
--)->p
;
796 *integer8
= (ficlUnsigned8
)((dataTop
--)->u
);
802 * l o g i c a n d c o m p a r i s o n s
805 case ficlInstruction0Equals
:
807 dataTop
->i
= FICL_BOOL(dataTop
->i
== 0);
810 case ficlInstruction0Less
:
812 dataTop
->i
= FICL_BOOL(dataTop
->i
< 0);
815 case ficlInstruction0Greater
:
817 dataTop
->i
= FICL_BOOL(dataTop
->i
> 0);
820 case ficlInstructionEquals
:
823 dataTop
->i
= FICL_BOOL(dataTop
->i
== i
);
826 case ficlInstructionLess
:
829 dataTop
->i
= FICL_BOOL(dataTop
->i
< i
);
832 case ficlInstructionULess
:
835 dataTop
->i
= FICL_BOOL(dataTop
->u
< u
);
838 case ficlInstructionAnd
:
841 dataTop
->i
= dataTop
->i
& i
;
844 case ficlInstructionOr
:
847 dataTop
->i
= dataTop
->i
| i
;
850 case ficlInstructionXor
:
853 dataTop
->i
= dataTop
->i
^ i
;
856 case ficlInstructionInvert
:
858 dataTop
->i
= ~dataTop
->i
;
862 * r e t u r n s t a c k
864 case ficlInstructionToRStack
:
866 CHECK_RETURN_STACK(0, 1);
867 *++returnTop
= *dataTop
--;
870 case ficlInstructionFromRStack
:
872 CHECK_RETURN_STACK(1, 0);
873 *++dataTop
= *returnTop
--;
876 case ficlInstructionFetchRStack
:
878 CHECK_RETURN_STACK(1, 1);
879 *++dataTop
= *returnTop
;
882 case ficlInstruction2ToR
:
884 CHECK_RETURN_STACK(0, 2);
885 *++returnTop
= dataTop
[-1];
886 *++returnTop
= dataTop
[0];
890 case ficlInstruction2RFrom
:
892 CHECK_RETURN_STACK(2, 0);
893 *++dataTop
= returnTop
[-1];
894 *++dataTop
= returnTop
[0];
898 case ficlInstruction2RFetch
:
900 CHECK_RETURN_STACK(2, 2);
901 *++dataTop
= returnTop
[-1];
902 *++dataTop
= returnTop
[0];
907 * CORE ( c-addr u char -- )
908 * If u is greater than zero, store char in each of u
909 * consecutive characters of memory beginning at c-addr.
911 case ficlInstructionFill
: {
915 c
= (char)(dataTop
--)->i
;
917 memory
= (char *)(dataTop
--)->p
;
920 * memset() is faster than the previous hand-rolled
923 memset(memory
, c
, u
);
929 * l-shift CORE ( x1 u -- x2 )
930 * Perform a logical left shift of u bit-places on x1,
931 * giving x2. Put zeroes into the least significant bits
932 * vacated by the shift. An ambiguous condition exists if
933 * u is greater than or equal to the number of bits in a
936 * r-shift CORE ( x1 u -- x2 )
937 * Perform a logical right shift of u bit-places on x1,
938 * giving x2. Put zeroes into the most significant bits
939 * vacated by the shift. An ambiguous condition exists
940 * if u is greater than or equal to the number of bits
943 case ficlInstructionLShift
: {
948 nBits
= (dataTop
--)->u
;
950 dataTop
->u
= x1
<< nBits
;
954 case ficlInstructionRShift
: {
959 nBits
= (dataTop
--)->u
;
961 dataTop
->u
= x1
>> nBits
;
968 case ficlInstructionMax
: {
976 dataTop
->i
= ((n1
> n2
) ? n1
: n2
);
980 case ficlInstructionMin
: {
988 dataTop
->i
= ((n1
< n2
) ? n1
: n2
);
994 * CORE ( addr1 addr2 u -- )
995 * If u is greater than zero, copy the contents of u
996 * consecutive address units at addr1 to the u consecutive
997 * address units at addr2. After MOVE completes, the u
998 * consecutive address units at addr2 contain exactly
999 * what the u consecutive address units at addr1 contained
1001 * NOTE! This implementation assumes that a char is the same
1002 * size as an address unit.
1004 case ficlInstructionMove
: {
1011 addr2
= (dataTop
--)->p
;
1012 addr1
= (dataTop
--)->p
;
1017 * Do the copy carefully, so as to be
1018 * correct even if the two ranges overlap
1020 /* Which ANSI C's memmove() does for you! Yay! --lch */
1021 memmove(addr2
, addr1
, u
);
1027 * s-to-d CORE ( n -- d )
1028 * Convert the number n to the double-ficlCell number d with
1029 * the same numerical value.
1031 case ficlInstructionSToD
: {
1037 /* sign extend to 64 bits.. */
1038 (++dataTop
)->i
= (s
< 0) ? -1 : 0;
1044 * STRING ( c-addr1 u1 c-addr2 u2 -- n )
1045 * Compare the string specified by c-addr1 u1 to the string
1046 * specified by c-addr2 u2. The strings are compared, beginning
1047 * at the given addresses, character by character, up to the
1048 * length of the shorter string or until a difference is found.
1049 * If the two strings are identical, n is zero. If the two
1050 * strings are identical up to the length of the shorter string,
1051 * n is minus-one (-1) if u1 is less than u2 and one (1)
1052 * otherwise. If the two strings are not identical up to the
1053 * length of the shorter string, n is minus-one (-1) if the
1054 * first non-matching character in the string specified by
1055 * c-addr1 u1 has a lesser numeric value than the corresponding
1056 * character in the string specified by c-addr2 u2 and
1057 * one (1) otherwise.
1059 case ficlInstructionCompare
:
1064 case ficlInstructionCompareInsensitive
:
1071 ficlUnsigned u1
, u2
, uMin
;
1075 u2
= (dataTop
--)->u
;
1076 cp2
= (char *)(dataTop
--)->p
;
1077 u1
= (dataTop
--)->u
;
1078 cp1
= (char *)(dataTop
--)->p
;
1080 uMin
= (u1
< u2
)? u1
: u2
;
1081 for (; (uMin
> 0) && (n
== 0); uMin
--) {
1082 int c1
= (unsigned char)*cp1
++;
1083 int c2
= (unsigned char)*cp2
++;
1108 case ficlInstructionRandom
:
1109 (++dataTop
)->i
= random();
1113 * s e e d - r a n d o m
1116 case ficlInstructionSeedRandom
:
1117 srandom((dataTop
--)->i
);
1120 case ficlInstructionGreaterThan
: {
1125 dataTop
->i
= FICL_BOOL(x
> y
);
1128 case ficlInstructionUGreaterThan
:
1131 dataTop
->i
= FICL_BOOL(dataTop
->u
> u
);
1137 * This function simply pops the previous instruction
1138 * pointer and returns to the "next" loop. Used for exiting
1139 * from within a definition. Note that exitParen is identical
1140 * to semiParen - they are in two different functions so that
1141 * "see" can correctly identify the end of a colon definition,
1142 * even if it uses "exit".
1144 case ficlInstructionExitParen
:
1145 case ficlInstructionSemiParen
:
1149 * The first time we run "(branch)", perform a "peephole
1150 * optimization" to see if we're jumping to another
1151 * unconditional jump. If so, just jump directly there.
1153 case ficlInstructionBranchParenWithCheck
:
1154 LOCAL_VARIABLE_SPILL
;
1155 ficlVmOptimizeJumpToJump(vm
, vm
->ip
- 1);
1156 LOCAL_VARIABLE_REFILL
;
1160 * Same deal with branch0.
1162 case ficlInstructionBranch0ParenWithCheck
:
1163 LOCAL_VARIABLE_SPILL
;
1164 ficlVmOptimizeJumpToJump(vm
, vm
->ip
- 1);
1165 LOCAL_VARIABLE_REFILL
;
1166 /* intentional fall-through */
1169 * Runtime code for "(branch0)"; pop a flag from the stack,
1170 * branch if 0. fall through otherwise.
1171 * The heart of "if" and "until".
1173 case ficlInstructionBranch0Paren
:
1176 if ((dataTop
--)->i
) {
1178 * don't branch, but skip over branch
1184 /* otherwise, take branch (to else/endif/begin) */
1185 /* intentional fall-through! */
1188 * Runtime for "(branch)" -- expects a literal offset in the
1189 * next compilation address, and branches to that location.
1191 case ficlInstructionBranchParen
:
1195 case ficlInstructionOfParen
: {
1206 /* remove CASE argument */
1209 /* take branch to next of or endcase */
1216 case ficlInstructionDoParen
: {
1217 ficlCell index
, limit
;
1224 /* copy "leave" target addr to stack */
1225 (++returnTop
)->i
= *(ip
++);
1226 *++returnTop
= limit
;
1227 *++returnTop
= index
;
1232 case ficlInstructionQDoParen
: {
1233 ficlCell index
, limit
, leave
;
1242 if (limit
.u
== index
.u
) {
1246 *++returnTop
= leave
;
1247 *++returnTop
= limit
;
1248 *++returnTop
= index
;
1254 case ficlInstructionLoopParen
:
1255 case ficlInstructionPlusLoopParen
: {
1260 index
= returnTop
->i
;
1261 limit
= returnTop
[-1].i
;
1263 if (instruction
== ficlInstructionLoopParen
)
1266 ficlInteger increment
;
1268 increment
= (dataTop
--)->i
;
1270 direction
= (increment
< 0);
1273 if (direction
^ (index
>= limit
)) {
1274 /* nuke the loop indices & "leave" addr */
1276 ip
++; /* fall through the loop */
1277 } else { /* update index, branch to loop head */
1278 returnTop
->i
= index
;
1287 * Runtime code to break out of a do..loop construct
1288 * Drop the loop control variables; the branch address
1289 * past "loop" is next on the return stack.
1291 case ficlInstructionLeave
:
1297 case ficlInstructionUnloop
:
1301 case ficlInstructionI
:
1302 *++dataTop
= *returnTop
;
1305 case ficlInstructionJ
:
1306 *++dataTop
= returnTop
[-3];
1309 case ficlInstructionK
:
1310 *++dataTop
= returnTop
[-6];
1313 case ficlInstructionDoesParen
: {
1314 ficlDictionary
*dictionary
= ficlVmGetDictionary(vm
);
1315 dictionary
->smudge
->code
=
1316 (ficlPrimitive
)ficlInstructionDoDoes
;
1317 dictionary
->smudge
->param
[0].p
= ip
;
1318 ip
= (ficlInstruction
*)((returnTop
--)->p
);
1322 case ficlInstructionDoDoes
: {
1329 tempIP
= (ficlIp
)((*cell
).p
);
1330 (++dataTop
)->p
= (cell
+ 1);
1331 (++returnTop
)->p
= (void *)ip
;
1332 ip
= (ficlInstruction
*)tempIP
;
1337 case ficlInstructionF2Fetch
:
1338 CHECK_FLOAT_STACK(0, 2);
1340 FLOAT_PUSH_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1342 case ficlInstructionFFetch
:
1343 CHECK_FLOAT_STACK(0, 1);
1345 FLOAT_PUSH_CELL_POINTER((dataTop
--)->p
);
1347 case ficlInstructionF2Store
:
1348 CHECK_FLOAT_STACK(2, 0);
1350 FLOAT_POP_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1352 case ficlInstructionFStore
:
1353 CHECK_FLOAT_STACK(1, 0);
1355 FLOAT_POP_CELL_POINTER((dataTop
--)->p
);
1356 #endif /* FICL_WANT_FLOAT */
1359 * two-fetch CORE ( a-addr -- x1 x2 )
1361 * Fetch the ficlCell pair x1 x2 stored at a-addr.
1362 * x2 is stored at a-addr and x1 at the next consecutive
1363 * ficlCell. It is equivalent to the sequence
1364 * DUP ficlCell+ @ SWAP @ .
1366 case ficlInstruction2Fetch
:
1368 PUSH_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1371 * fetch CORE ( a-addr -- x )
1373 * x is the value stored at a-addr.
1375 case ficlInstructionFetch
:
1377 PUSH_CELL_POINTER((dataTop
--)->p
);
1380 * two-store CORE ( x1 x2 a-addr -- )
1381 * Store the ficlCell pair x1 x2 at a-addr, with x2 at a-addr
1382 * and x1 at the next consecutive ficlCell. It is equivalent
1383 * to the sequence SWAP OVER ! ficlCell+ !
1385 case ficlInstruction2Store
:
1387 POP_CELL_POINTER_DOUBLE((dataTop
--)->p
);
1390 * store CORE ( x a-addr -- )
1391 * Store x at a-addr.
1393 case ficlInstructionStore
:
1395 POP_CELL_POINTER((dataTop
--)->p
);
1397 case ficlInstructionComma
: {
1398 ficlDictionary
*dictionary
;
1401 dictionary
= ficlVmGetDictionary(vm
);
1402 ficlDictionaryAppendCell(dictionary
, *dataTop
--);
1406 case ficlInstructionCComma
: {
1407 ficlDictionary
*dictionary
;
1411 dictionary
= ficlVmGetDictionary(vm
);
1412 c
= (char)(dataTop
--)->i
;
1413 ficlDictionaryAppendCharacter(dictionary
, c
);
1417 case ficlInstructionCells
:
1419 dataTop
->i
*= sizeof (ficlCell
);
1422 case ficlInstructionCellPlus
:
1424 dataTop
->i
+= sizeof (ficlCell
);
1427 case ficlInstructionStar
:
1433 case ficlInstructionNegate
:
1435 dataTop
->i
= - dataTop
->i
;
1438 case ficlInstructionSlash
:
1445 * slash-mod CORE ( n1 n2 -- n3 n4 )
1446 * Divide n1 by n2, giving the single-ficlCell remainder n3
1447 * and the single-ficlCell quotient n4. An ambiguous condition
1448 * exists if n2 is zero. If n1 and n2 differ in sign, the
1449 * implementation-defined result returned will be the
1450 * same as that returned by either the phrase
1451 * >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM.
1452 * NOTE: Ficl complies with the second phrase
1453 * (symmetric division)
1455 case ficlInstructionSlashMod
: {
1462 FICL_INTEGER_TO_2INTEGER(dataTop
[-1].i
, n1
);
1464 qr
= ficl2IntegerDivideSymmetric(n1
, n2
);
1465 dataTop
[-1].i
= qr
.remainder
;
1466 dataTop
[0].i
= FICL_2UNSIGNED_GET_LOW(qr
.quotient
);
1470 case ficlInstruction2Star
:
1475 case ficlInstruction2Slash
:
1480 case ficlInstructionStarSlash
: {
1481 ficlInteger x
, y
, z
;
1489 prod
= ficl2IntegerMultiply(x
, y
);
1490 dataTop
->i
= FICL_2UNSIGNED_GET_LOW(
1491 ficl2IntegerDivideSymmetric(prod
, z
).quotient
);
1495 case ficlInstructionStarSlashMod
: {
1496 ficlInteger x
, y
, z
;
1506 prod
= ficl2IntegerMultiply(x
, y
);
1507 qr
= ficl2IntegerDivideSymmetric(prod
, z
);
1509 dataTop
[-1].i
= qr
.remainder
;
1510 dataTop
[0].i
= FICL_2UNSIGNED_GET_LOW(qr
.quotient
);
1515 case ficlInstructionF0
:
1516 CHECK_FLOAT_STACK(0, 1);
1517 (++floatTop
)->f
= 0.0f
;
1520 case ficlInstructionF1
:
1521 CHECK_FLOAT_STACK(0, 1);
1522 (++floatTop
)->f
= 1.0f
;
1525 case ficlInstructionFNeg1
:
1526 CHECK_FLOAT_STACK(0, 1);
1527 (++floatTop
)->f
= -1.0f
;
1531 * Floating point literal execution word.
1533 case ficlInstructionFLiteralParen
:
1534 CHECK_FLOAT_STACK(0, 1);
1537 * Yes, I'm using ->i here,
1538 * but it's really a float. --lch
1540 (++floatTop
)->i
= *ip
++;
1544 * Do float addition r1 + r2.
1547 case ficlInstructionFPlus
:
1548 CHECK_FLOAT_STACK(2, 1);
1550 f
= (floatTop
--)->f
;
1555 * Do float subtraction r1 - r2.
1558 case ficlInstructionFMinus
:
1559 CHECK_FLOAT_STACK(2, 1);
1561 f
= (floatTop
--)->f
;
1566 * Do float multiplication r1 * r2.
1569 case ficlInstructionFStar
:
1570 CHECK_FLOAT_STACK(2, 1);
1572 f
= (floatTop
--)->f
;
1577 * Do float negation.
1578 * fnegate ( r -- r )
1580 case ficlInstructionFNegate
:
1581 CHECK_FLOAT_STACK(1, 1);
1583 floatTop
->f
= -(floatTop
->f
);
1587 * Do float division r1 / r2.
1590 case ficlInstructionFSlash
:
1591 CHECK_FLOAT_STACK(2, 1);
1593 f
= (floatTop
--)->f
;
1598 * Do float + integer r + n.
1601 case ficlInstructionFPlusI
:
1602 CHECK_FLOAT_STACK(1, 1);
1605 f
= (ficlFloat
)(dataTop
--)->f
;
1610 * Do float - integer r - n.
1613 case ficlInstructionFMinusI
:
1614 CHECK_FLOAT_STACK(1, 1);
1617 f
= (ficlFloat
)(dataTop
--)->f
;
1622 * Do float * integer r * n.
1625 case ficlInstructionFStarI
:
1626 CHECK_FLOAT_STACK(1, 1);
1629 f
= (ficlFloat
)(dataTop
--)->f
;
1634 * Do float / integer r / n.
1637 case ficlInstructionFSlashI
:
1638 CHECK_FLOAT_STACK(1, 1);
1641 f
= (ficlFloat
)(dataTop
--)->f
;
1646 * Do integer - float n - r.
1649 case ficlInstructionIMinusF
:
1650 CHECK_FLOAT_STACK(1, 1);
1653 f
= (ficlFloat
)(dataTop
--)->f
;
1654 floatTop
->f
= f
- floatTop
->f
;
1658 * Do integer / float n / r.
1661 case ficlInstructionISlashF
:
1662 CHECK_FLOAT_STACK(1, 1);
1665 f
= (ficlFloat
)(dataTop
--)->f
;
1666 floatTop
->f
= f
/ floatTop
->f
;
1670 * Do integer to float conversion.
1671 * int>float ( n -- r )
1673 case ficlInstructionIntToFloat
:
1675 CHECK_FLOAT_STACK(0, 1);
1677 (++floatTop
)->f
= ((dataTop
--)->f
);
1681 * Do float to integer conversion.
1682 * float>int ( r -- n )
1684 case ficlInstructionFloatToInt
:
1686 CHECK_FLOAT_STACK(1, 0);
1688 (++dataTop
)->i
= ((floatTop
--)->i
);
1692 * Add a floating point number to contents of a variable.
1695 case ficlInstructionFPlusStore
: {
1699 CHECK_FLOAT_STACK(1, 0);
1701 cell
= (ficlCell
*)(dataTop
--)->p
;
1702 cell
->f
+= (floatTop
--)->f
;
1707 * Do float stack drop.
1710 case ficlInstructionFDrop
:
1711 CHECK_FLOAT_STACK(1, 0);
1716 * Do float stack ?dup.
1719 case ficlInstructionFQuestionDup
:
1720 CHECK_FLOAT_STACK(1, 2);
1722 if (floatTop
->f
!= 0)
1728 * Do float stack dup.
1731 case ficlInstructionFDup
:
1732 CHECK_FLOAT_STACK(1, 2);
1735 floatTop
[1] = floatTop
[0];
1740 * Do float stack swap.
1741 * fswap ( r1 r2 -- r2 r1 )
1743 case ficlInstructionFSwap
:
1744 CHECK_FLOAT_STACK(2, 2);
1747 floatTop
[0] = floatTop
[-1];
1752 * Do float stack 2drop.
1755 case ficlInstructionF2Drop
:
1756 CHECK_FLOAT_STACK(2, 0);
1762 * Do float stack 2dup.
1763 * f2dup ( r1 r2 -- r1 r2 r1 r2 )
1765 case ficlInstructionF2Dup
:
1766 CHECK_FLOAT_STACK(2, 4);
1768 floatTop
[1] = floatTop
[-1];
1769 floatTop
[2] = *floatTop
;
1774 * Do float stack over.
1775 * fover ( r1 r2 -- r1 r2 r1 )
1777 case ficlInstructionFOver
:
1778 CHECK_FLOAT_STACK(2, 3);
1780 floatTop
[1] = floatTop
[-1];
1785 * Do float stack 2over.
1786 * f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
1788 case ficlInstructionF2Over
:
1789 CHECK_FLOAT_STACK(4, 6);
1791 floatTop
[1] = floatTop
[-2];
1792 floatTop
[2] = floatTop
[-1];
1797 * Do float stack pick.
1800 case ficlInstructionFPick
:
1803 CHECK_FLOAT_STACK(c
.i
+2, c
.i
+3);
1805 floatTop
[1] = floatTop
[- c
.i
- 1];
1809 * Do float stack rot.
1810 * frot ( r1 r2 r3 -- r2 r3 r1 )
1812 case ficlInstructionFRot
:
1817 * Do float stack roll.
1820 case ficlInstructionFRoll
:
1828 CHECK_FLOAT_STACK(i
+1, i
+2);
1830 memmove(floatTop
- i
, floatTop
- (i
- 1),
1831 i
* sizeof (ficlCell
));
1837 * Do float stack -rot.
1838 * f-rot ( r1 r2 r3 -- r3 r1 r2 )
1840 case ficlInstructionFMinusRot
:
1846 * Do float stack -roll.
1849 case ficlInstructionFMinusRoll
:
1857 CHECK_FLOAT_STACK(i
+1, i
+2);
1859 memmove(floatTop
- (i
- 1), floatTop
- i
,
1860 i
* sizeof (ficlCell
));
1866 * Do float stack 2swap
1867 * f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 )
1869 case ficlInstructionF2Swap
: {
1871 CHECK_FLOAT_STACK(4, 4);
1876 *floatTop
= floatTop
[-2];
1877 floatTop
[-1] = floatTop
[-3];
1885 * Do float 0= comparison r = 0.0.
1888 case ficlInstructionF0Equals
:
1889 CHECK_FLOAT_STACK(1, 0);
1892 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
!= 0.0f
);
1896 * Do float 0< comparison r < 0.0.
1899 case ficlInstructionF0Less
:
1900 CHECK_FLOAT_STACK(1, 0);
1903 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
< 0.0f
);
1907 * Do float 0> comparison r > 0.0.
1910 case ficlInstructionF0Greater
:
1911 CHECK_FLOAT_STACK(1, 0);
1914 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
> 0.0f
);
1918 * Do float = comparison r1 = r2.
1919 * f= ( r1 r2 -- T/F )
1921 case ficlInstructionFEquals
:
1922 CHECK_FLOAT_STACK(2, 0);
1925 f
= (floatTop
--)->f
;
1926 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
== f
);
1930 * Do float < comparison r1 < r2.
1931 * f< ( r1 r2 -- T/F )
1933 case ficlInstructionFLess
:
1934 CHECK_FLOAT_STACK(2, 0);
1937 f
= (floatTop
--)->f
;
1938 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
< f
);
1942 * Do float > comparison r1 > r2.
1943 * f> ( r1 r2 -- T/F )
1945 case ficlInstructionFGreater
:
1946 CHECK_FLOAT_STACK(2, 0);
1949 f
= (floatTop
--)->f
;
1950 (++dataTop
)->i
= FICL_BOOL((floatTop
--)->f
> f
);
1955 * Move float to param stack (assumes they both fit in a
1956 * single ficlCell) f>s
1958 case ficlInstructionFFrom
:
1959 CHECK_FLOAT_STACK(1, 0);
1962 *++dataTop
= *floatTop
--;
1965 case ficlInstructionToF
:
1966 CHECK_FLOAT_STACK(0, 1);
1969 *++floatTop
= *dataTop
--;
1972 #endif /* FICL_WANT_FLOAT */
1975 * c o l o n P a r e n
1976 * This is the code that executes a colon definition. It
1977 * assumes that the virtual machine is running a "next" loop
1978 * (See the vm.c for its implementation of member function
1979 * vmExecute()). The colon code simply copies the address of
1980 * the first word in the list of words to interpret into IP
1981 * after saving its old value. When we return to the "next"
1982 * loop, the virtual machine will call the code for each
1985 case ficlInstructionColonParen
:
1986 (++returnTop
)->p
= (void *)ip
;
1987 ip
= (ficlInstruction
*)(fw
->param
);
1990 case ficlInstructionCreateParen
:
1992 (++dataTop
)->p
= (fw
->param
+ 1);
1995 case ficlInstructionVariableParen
:
1997 (++dataTop
)->p
= fw
->param
;
2001 * c o n s t a n t P a r e n
2002 * This is the run-time code for "constant". It simply returns
2003 * the contents of its word's first data ficlCell.
2007 case ficlInstructionF2ConstantParen
:
2008 CHECK_FLOAT_STACK(0, 2);
2009 FLOAT_PUSH_CELL_POINTER_DOUBLE(fw
->param
);
2011 case ficlInstructionFConstantParen
:
2012 CHECK_FLOAT_STACK(0, 1);
2013 FLOAT_PUSH_CELL_POINTER(fw
->param
);
2014 #endif /* FICL_WANT_FLOAT */
2016 case ficlInstruction2ConstantParen
:
2018 PUSH_CELL_POINTER_DOUBLE(fw
->param
);
2020 case ficlInstructionConstantParen
:
2022 PUSH_CELL_POINTER(fw
->param
);
2025 case ficlInstructionUserParen
: {
2026 ficlInteger i
= fw
->param
[0].i
;
2027 (++dataTop
)->p
= &vm
->user
[i
];
2034 * Clever hack, or evil coding? You be the judge.
2036 * If the word we've been asked to execute is in fact
2037 * an *instruction*, we grab the instruction, stow it
2038 * in "i" (our local cache of *ip), and *jump* to the
2039 * top of the switch statement. --lch
2041 if (((ficlInstruction
)fw
->code
>
2042 ficlInstructionInvalid
) &&
2043 ((ficlInstruction
)fw
->code
< ficlInstructionLast
)) {
2044 instruction
= (ficlInstruction
)fw
->code
;
2048 LOCAL_VARIABLE_SPILL
;
2049 (vm
)->runningWord
= fw
;
2051 LOCAL_VARIABLE_REFILL
;
2056 LOCAL_VARIABLE_SPILL
;
2057 vm
->exceptionHandler
= oldExceptionHandler
;
2062 * Returns the address dictionary for this VM's system
2065 ficlVmGetDictionary(ficlVm
*vm
)
2067 FICL_VM_ASSERT(vm
, vm
);
2068 return (vm
->callback
.system
->dictionary
);
2072 * v m G e t S t r i n g
2073 * Parses a string out of the VM input buffer and copies up to the first
2074 * FICL_COUNTED_STRING_MAX characters to the supplied destination buffer, a
2075 * ficlCountedString. The destination string is NULL terminated.
2077 * Returns the address of the first unused character in the dest buffer.
2080 ficlVmGetString(ficlVm
*vm
, ficlCountedString
*counted
, char delimiter
)
2082 ficlString s
= ficlVmParseStringEx(vm
, delimiter
, 0);
2084 if (FICL_STRING_GET_LENGTH(s
) > FICL_COUNTED_STRING_MAX
) {
2085 FICL_STRING_SET_LENGTH(s
, FICL_COUNTED_STRING_MAX
);
2088 strncpy(counted
->text
, FICL_STRING_GET_POINTER(s
),
2089 FICL_STRING_GET_LENGTH(s
));
2090 counted
->text
[FICL_STRING_GET_LENGTH(s
)] = '\0';
2091 counted
->length
= (ficlUnsigned8
)FICL_STRING_GET_LENGTH(s
);
2093 return (counted
->text
+ FICL_STRING_GET_LENGTH(s
) + 1);
2098 * vmGetWord calls vmGetWord0 repeatedly until it gets a string with
2102 ficlVmGetWord(ficlVm
*vm
)
2104 ficlString s
= ficlVmGetWord0(vm
);
2106 if (FICL_STRING_GET_LENGTH(s
) == 0) {
2107 ficlVmThrow(vm
, FICL_VM_STATUS_RESTART
);
2114 * v m G e t W o r d 0
2115 * Skip leading whitespace and parse a space delimited word from the tib.
2116 * Returns the start address and length of the word. Updates the tib
2117 * to reflect characters consumed, including the trailing delimiter.
2118 * If there's nothing of interest in the tib, returns zero. This function
2119 * does not use vmParseString because it uses isspace() rather than a
2120 * single delimiter character.
2123 ficlVmGetWord0(ficlVm
*vm
)
2125 char *trace
= ficlVmGetInBuf(vm
);
2126 char *stop
= ficlVmGetInBufEnd(vm
);
2128 ficlUnsigned length
= 0;
2131 trace
= ficlStringSkipSpace(trace
, stop
);
2132 FICL_STRING_SET_POINTER(s
, trace
);
2134 /* Please leave this loop this way; it makes Purify happier. --lch */
2139 if (isspace((unsigned char)c
))
2145 FICL_STRING_SET_LENGTH(s
, length
);
2147 /* skip one trailing delimiter */
2148 if ((trace
!= stop
) && isspace((unsigned char)c
))
2151 ficlVmUpdateTib(vm
, trace
);
2157 * v m G e t W o r d T o P a d
2158 * Does vmGetWord and copies the result to the pad as a NULL terminated
2159 * string. Returns the length of the string. If the string is too long
2160 * to fit in the pad, it is truncated.
2163 ficlVmGetWordToPad(ficlVm
*vm
)
2166 char *pad
= (char *)vm
->pad
;
2167 s
= ficlVmGetWord(vm
);
2169 if (FICL_STRING_GET_LENGTH(s
) > FICL_PAD_SIZE
)
2170 FICL_STRING_SET_LENGTH(s
, FICL_PAD_SIZE
);
2172 strncpy(pad
, FICL_STRING_GET_POINTER(s
), FICL_STRING_GET_LENGTH(s
));
2173 pad
[FICL_STRING_GET_LENGTH(s
)] = '\0';
2174 return ((int)(FICL_STRING_GET_LENGTH(s
)));
2178 * v m P a r s e S t r i n g
2179 * Parses a string out of the input buffer using the delimiter
2180 * specified. Skips leading delimiters, marks the start of the string,
2181 * and counts characters to the next delimiter it encounters. It then
2182 * updates the vm input buffer to consume all these chars, including the
2183 * trailing delimiter.
2184 * Returns the address and length of the parsed string, not including the
2185 * trailing delimiter.
2188 ficlVmParseString(ficlVm
*vm
, char delimiter
)
2190 return (ficlVmParseStringEx(vm
, delimiter
, 1));
2194 ficlVmParseStringEx(ficlVm
*vm
, char delimiter
, char skipLeadingDelimiters
)
2197 char *trace
= ficlVmGetInBuf(vm
);
2198 char *stop
= ficlVmGetInBufEnd(vm
);
2201 if (skipLeadingDelimiters
) {
2202 while ((trace
!= stop
) && (*trace
== delimiter
))
2206 FICL_STRING_SET_POINTER(s
, trace
); /* mark start of text */
2208 /* find next delimiter or end of line */
2210 (trace
!= stop
) && (c
!= delimiter
) && (c
!= '\r') && (c
!= '\n');
2215 /* set length of result */
2216 FICL_STRING_SET_LENGTH(s
, trace
- FICL_STRING_GET_POINTER(s
));
2218 /* gobble trailing delimiter */
2219 if ((trace
!= stop
) && (*trace
== delimiter
))
2222 ficlVmUpdateTib(vm
, trace
);
2231 ficlVmPop(ficlVm
*vm
)
2233 return (ficlStackPop(vm
->dataStack
));
2240 ficlVmPush(ficlVm
*vm
, ficlCell c
)
2242 ficlStackPush(vm
->dataStack
, c
);
2249 ficlVmPopIP(ficlVm
*vm
)
2251 vm
->ip
= (ficlIp
)(ficlStackPopPointer(vm
->returnStack
));
2258 ficlVmPushIP(ficlVm
*vm
, ficlIp newIP
)
2260 ficlStackPushPointer(vm
->returnStack
, (void *)vm
->ip
);
2266 * Binds the specified input string to the VM and clears >IN (the index)
2269 ficlVmPushTib(ficlVm
*vm
, char *text
, ficlInteger nChars
, ficlTIB
*pSaveTib
)
2272 *pSaveTib
= vm
->tib
;
2274 vm
->tib
.text
= text
;
2275 vm
->tib
.end
= text
+ nChars
;
2280 ficlVmPopTib(ficlVm
*vm
, ficlTIB
*pTib
)
2291 ficlVmQuit(ficlVm
*vm
)
2293 ficlStackReset(vm
->returnStack
);
2296 vm
->runningWord
= NULL
;
2297 vm
->state
= FICL_VM_STATE_INTERPRET
;
2298 vm
->tib
.text
= NULL
;
2309 ficlVmReset(ficlVm
*vm
)
2312 ficlStackReset(vm
->dataStack
);
2314 ficlStackReset(vm
->floatStack
);
2320 * v m S e t T e x t O u t
2321 * Binds the specified output callback to the vm. If you pass NULL,
2322 * binds the default output function (ficlTextOut)
2325 ficlVmSetTextOut(ficlVm
*vm
, ficlOutputFunction textOut
)
2327 vm
->callback
.textOut
= textOut
;
2331 ficlVmTextOut(ficlVm
*vm
, char *text
)
2333 ficlCallbackTextOut((ficlCallback
*)vm
, text
);
2338 ficlVmErrorOut(ficlVm
*vm
, char *text
)
2340 ficlCallbackErrorOut((ficlCallback
*)vm
, text
);
2348 ficlVmThrow(ficlVm
*vm
, int except
)
2350 if (vm
->exceptionHandler
)
2351 longjmp(*(vm
->exceptionHandler
), except
);
2355 ficlVmThrowError(ficlVm
*vm
, char *fmt
, ...)
2359 va_start(list
, fmt
);
2360 vsprintf(vm
->pad
, fmt
, list
);
2362 strcat(vm
->pad
, "\n");
2364 ficlVmErrorOut(vm
, vm
->pad
);
2365 longjmp(*(vm
->exceptionHandler
), FICL_VM_STATUS_ERROR_EXIT
);
2369 ficlVmThrowErrorVararg(ficlVm
*vm
, char *fmt
, va_list list
)
2371 vsprintf(vm
->pad
, fmt
, list
);
2373 * well, we can try anyway, we're certainly not
2374 * returning to our caller!
2377 strcat(vm
->pad
, "\n");
2379 ficlVmErrorOut(vm
, vm
->pad
);
2380 longjmp(*(vm
->exceptionHandler
), FICL_VM_STATUS_ERROR_EXIT
);
2384 * f i c l E v a l u a t e
2385 * Wrapper for ficlExec() which sets SOURCE-ID to -1.
2388 ficlVmEvaluate(ficlVm
*vm
, char *s
)
2391 ficlCell id
= vm
->sourceId
;
2393 vm
->sourceId
.i
= -1;
2394 FICL_STRING_SET_FROM_CSTRING(string
, s
);
2395 returnValue
= ficlVmExecuteString(vm
, string
);
2397 return (returnValue
);
2402 * Evaluates a block of input text in the context of the
2403 * specified interpreter. Emits any requested output to the
2404 * interpreter's output function.
2406 * Contains the "inner interpreter" code in a tight loop
2408 * Returns one of the VM_XXXX codes defined in ficl.h:
2409 * VM_OUTOFTEXT is the normal exit condition
2410 * VM_ERREXIT means that the interpreter encountered a syntax error
2411 * and the vm has been reset to recover (some or all
2412 * of the text block got ignored
2413 * VM_USEREXIT means that the user executed the "bye" command
2414 * to shut down the interpreter. This would be a good
2415 * time to delete the vm, etc -- or you can ignore this
2419 ficlVmExecuteString(ficlVm
*vm
, ficlString s
)
2421 ficlSystem
*system
= vm
->callback
.system
;
2422 ficlDictionary
*dictionary
= system
->dictionary
;
2427 ficlTIB saveficlTIB
;
2429 FICL_VM_ASSERT(vm
, vm
);
2430 FICL_VM_ASSERT(vm
, system
->interpreterLoop
[0]);
2432 ficlVmPushTib(vm
, FICL_STRING_GET_POINTER(s
),
2433 FICL_STRING_GET_LENGTH(s
), &saveficlTIB
);
2436 * Save and restore VM's jmp_buf to enable nested calls to ficlExec
2438 oldState
= vm
->exceptionHandler
;
2440 /* This has to come before the setjmp! */
2441 vm
->exceptionHandler
= &vmState
;
2442 except
= setjmp(vmState
);
2447 vm
->runningWord
->code(vm
);
2449 } else { /* set VM up to interpret text */
2450 ficlVmPushIP(vm
, &(system
->interpreterLoop
[0]));
2453 ficlVmInnerLoop(vm
, 0);
2456 case FICL_VM_STATUS_RESTART
:
2458 except
= FICL_VM_STATUS_OUT_OF_TEXT
;
2461 case FICL_VM_STATUS_OUT_OF_TEXT
:
2463 #if 0 /* we dont output prompt in loader */
2464 if ((vm
->state
!= FICL_VM_STATE_COMPILE
) &&
2465 (vm
->sourceId
.i
== 0))
2466 ficlVmTextOut(vm
, FICL_PROMPT
);
2470 case FICL_VM_STATUS_USER_EXIT
:
2471 case FICL_VM_STATUS_INNER_EXIT
:
2472 case FICL_VM_STATUS_BREAK
:
2475 case FICL_VM_STATUS_QUIT
:
2476 if (vm
->state
== FICL_VM_STATE_COMPILE
) {
2477 ficlDictionaryAbortDefinition(dictionary
);
2478 #if FICL_WANT_LOCALS
2479 ficlDictionaryEmpty(system
->locals
,
2480 system
->locals
->forthWordlist
->size
);
2486 case FICL_VM_STATUS_ERROR_EXIT
:
2487 case FICL_VM_STATUS_ABORT
:
2488 case FICL_VM_STATUS_ABORTQ
:
2489 default: /* user defined exit code?? */
2490 if (vm
->state
== FICL_VM_STATE_COMPILE
) {
2491 ficlDictionaryAbortDefinition(dictionary
);
2492 #if FICL_WANT_LOCALS
2493 ficlDictionaryEmpty(system
->locals
,
2494 system
->locals
->forthWordlist
->size
);
2497 ficlDictionaryResetSearchOrder(dictionary
);
2502 vm
->exceptionHandler
= oldState
;
2503 ficlVmPopTib(vm
, &saveficlTIB
);
2508 * f i c l E x e c X T
2509 * Given a pointer to a ficlWord, push an inner interpreter and
2510 * execute the word to completion. This is in contrast with vmExecute,
2511 * which does not guarantee that the word will have completed when
2512 * the function returns (ie in the case of colon definitions, which
2513 * need an inner interpreter to finish)
2515 * Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
2516 * exit condition is VM_INNEREXIT, Ficl's private signal to exit the
2517 * inner loop under normal circumstances. If another code is thrown to
2518 * exit the loop, this function will re-throw it if it's nested under
2519 * itself or ficlExec.
2521 * NOTE: this function is intended so that C code can execute ficlWords
2522 * given their address in the dictionary (xt).
2525 ficlVmExecuteXT(ficlVm
*vm
, ficlWord
*pWord
)
2530 ficlWord
*oldRunningWord
;
2532 FICL_VM_ASSERT(vm
, vm
);
2533 FICL_VM_ASSERT(vm
, vm
->callback
.system
->exitInnerWord
);
2536 * Save the runningword so that RESTART behaves correctly
2537 * over nested calls.
2539 oldRunningWord
= vm
->runningWord
;
2541 * Save and restore VM's jmp_buf to enable nested calls
2543 oldState
= vm
->exceptionHandler
;
2544 /* This has to come before the setjmp! */
2545 vm
->exceptionHandler
= &vmState
;
2546 except
= setjmp(vmState
);
2551 ficlVmPushIP(vm
, &(vm
->callback
.system
->exitInnerWord
));
2555 ficlVmExecuteWord(vm
, pWord
);
2556 ficlVmInnerLoop(vm
, 0);
2559 case FICL_VM_STATUS_INNER_EXIT
:
2560 case FICL_VM_STATUS_BREAK
:
2563 case FICL_VM_STATUS_RESTART
:
2564 case FICL_VM_STATUS_OUT_OF_TEXT
:
2565 case FICL_VM_STATUS_USER_EXIT
:
2566 case FICL_VM_STATUS_QUIT
:
2567 case FICL_VM_STATUS_ERROR_EXIT
:
2568 case FICL_VM_STATUS_ABORT
:
2569 case FICL_VM_STATUS_ABORTQ
:
2570 default: /* user defined exit code?? */
2572 vm
->exceptionHandler
= oldState
;
2573 ficlVmThrow(vm
, except
);
2578 vm
->exceptionHandler
= oldState
;
2579 vm
->runningWord
= oldRunningWord
;
2584 * f i c l P a r s e N u m b e r
2585 * Attempts to convert the NULL terminated string in the VM's pad to
2586 * a number using the VM's current base. If successful, pushes the number
2587 * onto the param stack and returns FICL_TRUE. Otherwise, returns FICL_FALSE.
2588 * (jws 8/01) Trailing decimal point causes a zero ficlCell to be pushed. (See
2589 * the standard for DOUBLE wordset.
2592 ficlVmParseNumber(ficlVm
*vm
, ficlString s
)
2594 ficlInteger accumulator
= 0;
2595 char isNegative
= 0;
2597 unsigned base
= vm
->base
;
2598 char *trace
= FICL_STRING_GET_POINTER(s
);
2599 ficlUnsigned8 length
= (ficlUnsigned8
)FICL_STRING_GET_LENGTH(s
);
2620 /* detect & remove trailing decimal */
2621 if ((length
> 0) && (trace
[length
- 1] == '.')) {
2626 if (length
== 0) /* detect "+", "-", ".", "+." etc */
2627 return (0); /* false */
2629 while ((length
--) && ((c
= *trace
++) != '\0')) {
2631 return (0); /* false */
2636 digit
= tolower(c
) - 'a' + 10;
2639 return (0); /* false */
2641 accumulator
= accumulator
* base
+ digit
;
2645 accumulator
= -accumulator
;
2647 ficlStackPushInteger(vm
->dataStack
, accumulator
);
2648 if (vm
->state
== FICL_VM_STATE_COMPILE
)
2649 ficlPrimitiveLiteralIm(vm
);
2651 if (isDouble
) { /* simple (required) DOUBLE support */
2653 ficlStackPushInteger(vm
->dataStack
, -1);
2655 ficlStackPushInteger(vm
->dataStack
, 0);
2656 if (vm
->state
== FICL_VM_STATE_COMPILE
)
2657 ficlPrimitiveLiteralIm(vm
);
2660 return (1); /* true */
2665 * Checks the dictionary for corruption and throws appropriate
2667 * Input: +n number of ADDRESS UNITS (not ficlCells) proposed to allot
2668 * -n number of ADDRESS UNITS proposed to de-allot
2669 * 0 just do a consistency check
2672 ficlVmDictionarySimpleCheck(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2674 #if FICL_ROBUST >= 1
2676 (ficlDictionaryCellsAvailable(dictionary
) *
2677 (int)sizeof (ficlCell
) < cells
)) {
2678 ficlVmThrowError(vm
, "Error: dictionary full");
2682 (ficlDictionaryCellsUsed(dictionary
) *
2683 (int)sizeof (ficlCell
) < -cells
)) {
2684 ficlVmThrowError(vm
, "Error: dictionary underflow");
2686 #else /* FICL_ROBUST >= 1 */
2688 FICL_IGNORE(dictionary
);
2690 #endif /* FICL_ROBUST >= 1 */
2694 ficlVmDictionaryCheck(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2696 #if FICL_ROBUST >= 1
2697 ficlVmDictionarySimpleCheck(vm
, dictionary
, cells
);
2699 if (dictionary
->wordlistCount
> FICL_MAX_WORDLISTS
) {
2700 ficlDictionaryResetSearchOrder(dictionary
);
2701 ficlVmThrowError(vm
, "Error: search order overflow");
2702 } else if (dictionary
->wordlistCount
< 0) {
2703 ficlDictionaryResetSearchOrder(dictionary
);
2704 ficlVmThrowError(vm
, "Error: search order underflow");
2706 #else /* FICL_ROBUST >= 1 */
2708 FICL_IGNORE(dictionary
);
2710 #endif /* FICL_ROBUST >= 1 */
2714 ficlVmDictionaryAllot(ficlVm
*vm
, ficlDictionary
*dictionary
, int n
)
2716 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm
, dictionary
, n
);
2718 ficlDictionaryAllot(dictionary
, n
);
2722 ficlVmDictionaryAllotCells(ficlVm
*vm
, ficlDictionary
*dictionary
, int cells
)
2724 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm
, dictionary
, cells
);
2726 ficlDictionaryAllotCells(dictionary
, cells
);
2730 * f i c l P a r s e W o r d
2731 * From the standard, section 3.4
2732 * b) Search the dictionary name space (see 3.4.2). If a definition name
2733 * matching the string is found:
2734 * 1.if interpreting, perform the interpretation semantics of the definition
2735 * (see 3.4.3.2), and continue at a);
2736 * 2.if compiling, perform the compilation semantics of the definition
2737 * (see 3.4.3.3), and continue at a).
2739 * c) If a definition name matching the string is not found, attempt to
2740 * convert the string to a number (see 3.4.1.3). If successful:
2741 * 1.if interpreting, place the number on the data stack, and continue at a);
2742 * 2.if compiling, FICL_VM_STATE_COMPILE code that when executed will place
2743 * the number on the stack (see 6.1.1780 LITERAL), and continue at a);
2745 * d) If unsuccessful, an ambiguous condition exists (see 3.4.4).
2747 * (jws 4/01) Modified to be a ficlParseStep
2750 ficlVmParseWord(ficlVm
*vm
, ficlString name
)
2752 ficlDictionary
*dictionary
= ficlVmGetDictionary(vm
);
2755 FICL_VM_DICTIONARY_CHECK(vm
, dictionary
, 0);
2756 FICL_STACK_CHECK(vm
->dataStack
, 0, 0);
2758 #if FICL_WANT_LOCALS
2759 if (vm
->callback
.system
->localsCount
> 0) {
2760 tempFW
= ficlSystemLookupLocal(vm
->callback
.system
, name
);
2763 tempFW
= ficlDictionaryLookup(dictionary
, name
);
2765 if (vm
->state
== FICL_VM_STATE_INTERPRET
) {
2766 if (tempFW
!= NULL
) {
2767 if (ficlWordIsCompileOnly(tempFW
)) {
2768 ficlVmThrowError(vm
,
2769 "Error: FICL_VM_STATE_COMPILE only!");
2772 ficlVmExecuteWord(vm
, tempFW
);
2773 return (1); /* true */
2775 } else { /* (vm->state == FICL_VM_STATE_COMPILE) */
2776 if (tempFW
!= NULL
) {
2777 if (ficlWordIsImmediate(tempFW
)) {
2778 ficlVmExecuteWord(vm
, tempFW
);
2782 if (tempFW
->flags
& FICL_WORD_INSTRUCTION
)
2783 ficlDictionaryAppendUnsigned(dictionary
,
2784 (ficlInteger
)tempFW
->code
);
2786 ficlDictionaryAppendCell(dictionary
, c
);
2788 return (1); /* true */
2792 return (0); /* false */