1 /*******************************************************************
3 ** Forth Inspired Command Language - virtual machine methods
4 ** Author: John Sadler (john_sadler@alum.mit.edu)
5 ** Created: 19 July 1997
7 *******************************************************************/
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 /* $FreeBSD: src/sys/boot/ficl/vm.c,v 1.5.2.1 2000/07/06 23:51:45 obrien Exp $ */
29 static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
32 /**************************************************************************
33 v m B r a n c h R e l a t i v e
35 **************************************************************************/
36 void vmBranchRelative(FICL_VM *pVM, int offset)
43 /**************************************************************************
46 **************************************************************************/
47 FICL_VM *vmCreate(FICL_VM *pVM, unsigned nPStack, unsigned nRStack)
51 pVM = (FICL_VM *)ficlMalloc(sizeof (FICL_VM));
53 memset(pVM, 0, sizeof (FICL_VM));
57 stackDelete(pVM->pStack);
58 pVM->pStack = stackCreate(nPStack);
61 stackDelete(pVM->rStack);
62 pVM->rStack = stackCreate(nRStack);
64 pVM->textOut = ficlTextOut;
71 /**************************************************************************
74 **************************************************************************/
75 void vmDelete (FICL_VM *pVM)
79 ficlFree(pVM->pStack);
80 ficlFree(pVM->rStack);
88 /**************************************************************************
90 ** Sets up the specified word to be run by the inner interpreter.
91 ** Executes the word's code part immediately, but in the case of
92 ** colon definition, the definition itself needs the inner interp
93 ** to complete. This does not happen until control reaches ficlExec
94 **************************************************************************/
95 void vmExecute(FICL_VM *pVM, FICL_WORD *pWord)
97 pVM->runningWord = pWord;
103 /**************************************************************************
104 v m I n n e r L o o p
105 ** the mysterious inner interpreter...
106 ** This loop is the address interpreter that makes colon definitions
107 ** work. Upon entry, it assumes that the IP points to an entry in
108 ** a definition (the body of a colon word). It runs one word at a time
109 ** until something does vmThrow. The catcher for this is expected to exist
110 ** in the calling code.
111 ** vmThrow gets you out of this loop with a longjmp()
112 ** Visual C++ 5 chokes on this loop in Release mode. Aargh.
113 **************************************************************************/
114 #if INLINE_INNER_LOOP == 0
115 void vmInnerLoop(FICL_VM *pVM)
122 /**************************************************************************
123 v m G e t S t r i n g
124 ** Parses a string out of the VM input buffer and copies up to the first
125 ** FICL_STRING_MAX characters to the supplied destination buffer, a
126 ** FICL_STRING. The destination string is NULL terminated.
128 ** Returns the address of the first unused character in the dest buffer.
129 **************************************************************************/
130 char *vmGetString(FICL_VM *pVM, FICL_STRING *spDest, char delimiter)
132 STRINGINFO si = vmParseStringEx(pVM, delimiter, 0);
134 if (SI_COUNT(si) > FICL_STRING_MAX)
136 SI_SETLEN(si, FICL_STRING_MAX);
139 strncpy(spDest->text, SI_PTR(si), SI_COUNT(si));
140 spDest->text[SI_COUNT(si)] = '\0';
141 spDest->count = (FICL_COUNT)SI_COUNT(si);
143 return spDest->text + SI_COUNT(si) + 1;
147 /**************************************************************************
149 ** vmGetWord calls vmGetWord0 repeatedly until it gets a string with
151 **************************************************************************/
152 STRINGINFO vmGetWord(FICL_VM *pVM)
154 STRINGINFO si = vmGetWord0(pVM);
156 if (SI_COUNT(si) == 0)
158 vmThrow(pVM, VM_RESTART);
165 /**************************************************************************
167 ** Skip leading whitespace and parse a space delimited word from the tib.
168 ** Returns the start address and length of the word. Updates the tib
169 ** to reflect characters consumed, including the trailing delimiter.
170 ** If there's nothing of interest in the tib, returns zero. This function
171 ** does not use vmParseString because it uses isspace() rather than a
172 ** single delimiter character.
173 **************************************************************************/
174 STRINGINFO vmGetWord0(FICL_VM *pVM)
176 char *pSrc = vmGetInBuf(pVM);
177 char *pEnd = vmGetInBufEnd(pVM);
182 pSrc = skipSpace(pSrc, pEnd);
185 for (ch = *pSrc; (pEnd != pSrc) && !isspace(ch); ch = *++pSrc)
190 SI_SETLEN(si, count);
192 if ((pEnd != pSrc) && isspace(ch)) /* skip one trailing delimiter */
195 vmUpdateTib(pVM, pSrc);
201 /**************************************************************************
202 v m G e t W o r d T o P a d
203 ** Does vmGetWord0 and copies the result to the pad as a NULL terminated
204 ** string. Returns the length of the string. If the string is too long
205 ** to fit in the pad, it is truncated.
206 **************************************************************************/
207 int vmGetWordToPad(FICL_VM *pVM)
210 char *cp = (char *)pVM->pad;
211 si = vmGetWord0(pVM);
213 if (SI_COUNT(si) > nPAD)
216 strncpy(cp, SI_PTR(si), SI_COUNT(si));
217 cp[SI_COUNT(si)] = '\0';
218 return (int)(SI_COUNT(si));
222 /**************************************************************************
223 v m P a r s e S t r i n g
224 ** Parses a string out of the input buffer using the delimiter
225 ** specified. Skips leading delimiters, marks the start of the string,
226 ** and counts characters to the next delimiter it encounters. It then
227 ** updates the vm input buffer to consume all these chars, including the
228 ** trailing delimiter.
229 ** Returns the address and length of the parsed string, not including the
230 ** trailing delimiter.
231 **************************************************************************/
232 STRINGINFO vmParseString(FICL_VM *pVM, char delim)
234 return vmParseStringEx(pVM, delim, 1);
237 STRINGINFO vmParseStringEx(FICL_VM *pVM, char delim, char fSkipLeading)
240 char *pSrc = vmGetInBuf(pVM);
241 char *pEnd = vmGetInBufEnd(pVM);
245 { /* skip lead delimiters */
246 while ((pSrc != pEnd) && (*pSrc == delim))
250 SI_SETPTR(si, pSrc); /* mark start of text */
252 for (ch = *pSrc; (pSrc != pEnd)
255 && (ch != '\n'); ch = *++pSrc)
257 ; /* find next delimiter or end of line */
260 /* set length of result */
261 SI_SETLEN(si, pSrc - SI_PTR(si));
263 if ((pSrc != pEnd) && (*pSrc == delim)) /* gobble trailing delimiter */
266 vmUpdateTib(pVM, pSrc);
271 /**************************************************************************
274 **************************************************************************/
275 CELL vmPop(FICL_VM *pVM)
277 return stackPop(pVM->pStack);
281 /**************************************************************************
284 **************************************************************************/
285 void vmPush(FICL_VM *pVM, CELL c)
287 stackPush(pVM->pStack, c);
292 /**************************************************************************
295 **************************************************************************/
296 void vmPopIP(FICL_VM *pVM)
298 pVM->ip = (IPTYPE)(stackPopPtr(pVM->rStack));
303 /**************************************************************************
306 **************************************************************************/
307 void vmPushIP(FICL_VM *pVM, IPTYPE newIP)
309 stackPushPtr(pVM->rStack, (void *)pVM->ip);
315 /**************************************************************************
317 ** Binds the specified input string to the VM and clears >IN (the index)
318 **************************************************************************/
319 void vmPushTib(FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib)
323 *pSaveTib = pVM->tib;
327 pVM->tib.end = text + nChars;
332 void vmPopTib(FICL_VM *pVM, TIB *pTib)
342 /**************************************************************************
345 **************************************************************************/
346 void vmQuit(FICL_VM *pVM)
348 static FICL_WORD *pInterp = NULL;
350 pInterp = ficlLookup("interpret");
353 stackReset(pVM->rStack);
356 pVM->runningWord = pInterp;
357 pVM->state = INTERPRET;
367 /**************************************************************************
370 **************************************************************************/
371 void vmReset(FICL_VM *pVM)
374 stackReset(pVM->pStack);
380 /**************************************************************************
381 v m S e t T e x t O u t
382 ** Binds the specified output callback to the vm. If you pass NULL,
383 ** binds the default output function (ficlTextOut)
384 **************************************************************************/
385 void vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut)
388 pVM->textOut = textOut;
390 pVM->textOut = ficlTextOut;
396 /**************************************************************************
398 ** Single step the vm - equivalent to "step into" - used for debugging
399 **************************************************************************/
400 #if FICL_WANT_DEBUGGER
401 void vmStep(FICL_VM *pVM)
408 /**************************************************************************
410 ** Feeds text to the vm's output callback
411 **************************************************************************/
412 void vmTextOut(FICL_VM *pVM, char *text, int fNewline)
415 assert(pVM->textOut);
416 (pVM->textOut)(pVM, text, fNewline);
422 /**************************************************************************
425 **************************************************************************/
426 void vmThrow(FICL_VM *pVM, int except)
429 longjmp(*(pVM->pState), except);
433 void vmThrowErr(FICL_VM *pVM, char *fmt, ...)
437 vsprintf(pVM->pad, fmt, va);
438 vmTextOut(pVM, pVM->pad, 1);
440 longjmp(*(pVM->pState), VM_ERREXIT);
444 /**************************************************************************
445 w o r d I s I m m e d i a t e
447 **************************************************************************/
448 int wordIsImmediate(FICL_WORD *pFW)
450 return ((pFW != NULL) && (pFW->flags & FW_IMMEDIATE));
454 /**************************************************************************
455 w o r d I s C o m p i l e O n l y
457 **************************************************************************/
458 int wordIsCompileOnly(FICL_WORD *pFW)
460 return ((pFW != NULL) && (pFW->flags & FW_COMPILE));
464 /**************************************************************************
467 **************************************************************************/
468 char *strrev( char *string )
469 { /* reverse a string in-place */
470 int i = strlen(string);
471 char *p1 = string; /* first char of string */
472 char *p2 = string + i - 1; /* last non-NULL char of string */
490 /**************************************************************************
491 d i g i t _ t o _ c h a r
493 **************************************************************************/
494 char digit_to_char(int value)
496 return digits[value];
500 /**************************************************************************
501 i s P o w e r O f T w o
502 ** Tests whether supplied argument is an integer power of 2 (2**n)
503 ** where 32 > n > 1, and returns n if so. Otherwise returns zero.
504 **************************************************************************/
505 int isPowerOfTwo(FICL_UNS u)
510 for (; ((t <= u) && (t != 0)); i++, t <<= 1)
520 /**************************************************************************
523 **************************************************************************/
524 char *ltoa( FICL_INT value, char *string, int radix )
525 { /* convert long to string, any base */
527 int sign = ((radix == 10) && (value < 0));
534 pwr = isPowerOfTwo((FICL_UNS)radix);
543 FICL_UNS v = (FICL_UNS) value;
544 FICL_UNS mask = (FICL_UNS) ~(-1 << pwr);
547 *cp++ = digits[v & mask];
556 v.lo = (FICL_UNS)value;
559 result = ficlLongDiv(v, (FICL_UNS)radix);
560 *cp++ = digits[result.rem];
570 return strrev(string);
574 /**************************************************************************
577 **************************************************************************/
578 char *ultoa(FICL_UNS value, char *string, int radix )
579 { /* convert long to string, any base */
598 result = ficlLongDiv(ud, (FICL_UNS)radix);
600 *cp++ = digits[result.rem];
606 return strrev(string);
610 /**************************************************************************
612 ** Case folds a NULL terminated string in place. All characters
613 ** get converted to lower case.
614 **************************************************************************/
615 char *caseFold(char *cp)
622 *cp = (char)tolower(*cp);
630 /**************************************************************************
633 **************************************************************************/
634 int strincmp(char *cp1, char *cp2, FICL_COUNT count)
639 for (c1 = *cp1, c2 = *cp2;
640 ((i == 0) && count && c1 && c2);
641 c1 = *++cp1, c2 = *++cp2, count--)
643 i = tolower(c1) - tolower(c2);
651 /**************************************************************************
653 ** Given a string pointer, returns a pointer to the first non-space
654 ** char of the string, or to the NULL terminator if no such char found.
655 ** If the pointer reaches "end" first, stop there. Pass NULL to
656 ** suppress this behavior.
657 **************************************************************************/
658 char *skipSpace(char *cp, char *end)
662 while ((cp != end) && isspace(*cp))