]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/boot/ficl/vm.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / sys / boot / ficl / vm.c
1 /*******************************************************************
2 ** v m . c
3 ** Forth Inspired Command Language - virtual machine methods
4 ** Author: John Sadler (john_sadler@alum.mit.edu)
5 ** Created: 19 July 1997
6 ** $Id: vm.c,v 1.13 2001/12/05 07:21:34 jsadler Exp $
7 *******************************************************************/
8 /*
9 ** This file implements the virtual machine of FICL. Each virtual
10 ** machine retains the state of an interpreter. A virtual machine
11 ** owns a pair of stacks for parameters and return addresses, as
12 ** well as a pile of state variables and the two dedicated registers
13 ** of the interp.
14 */
15 /*
16 ** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
17 ** All rights reserved.
18 **
19 ** Get the latest Ficl release at http://ficl.sourceforge.net
20 **
21 ** I am interested in hearing from anyone who uses ficl. If you have
22 ** a problem, a success story, a defect, an enhancement request, or
23 ** if you would like to contribute to the ficl release, please
24 ** contact me by email at the address above.
25 **
26 ** L I C E N S E  and  D I S C L A I M E R
27 ** 
28 ** Redistribution and use in source and binary forms, with or without
29 ** modification, are permitted provided that the following conditions
30 ** are met:
31 ** 1. Redistributions of source code must retain the above copyright
32 **    notice, this list of conditions and the following disclaimer.
33 ** 2. Redistributions in binary form must reproduce the above copyright
34 **    notice, this list of conditions and the following disclaimer in the
35 **    documentation and/or other materials provided with the distribution.
36 **
37 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
38 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
41 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 ** SUCH DAMAGE.
48 */
49
50 /* $FreeBSD$ */
51
52 #ifdef TESTMAIN
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <ctype.h>
56 #else
57 #include <stand.h>
58 #endif
59 #include <stdarg.h>
60 #include <string.h>
61 #include "ficl.h"
62
63 static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
64
65
66 /**************************************************************************
67                         v m B r a n c h R e l a t i v e 
68 ** 
69 **************************************************************************/
70 void vmBranchRelative(FICL_VM *pVM, int offset)
71 {
72     pVM->ip += offset;
73     return;
74 }
75
76
77 /**************************************************************************
78                         v m C r e a t e
79 ** Creates a virtual machine either from scratch (if pVM is NULL on entry)
80 ** or by resizing and reinitializing an existing VM to the specified stack
81 ** sizes.
82 **************************************************************************/
83 FICL_VM *vmCreate(FICL_VM *pVM, unsigned nPStack, unsigned nRStack)
84 {
85     if (pVM == NULL)
86     {
87         pVM = (FICL_VM *)ficlMalloc(sizeof (FICL_VM));
88         assert (pVM);
89         memset(pVM, 0, sizeof (FICL_VM));
90     }
91
92     if (pVM->pStack)
93         stackDelete(pVM->pStack);
94     pVM->pStack = stackCreate(nPStack);
95
96     if (pVM->rStack)
97         stackDelete(pVM->rStack);
98     pVM->rStack = stackCreate(nRStack);
99
100 #if FICL_WANT_FLOAT
101     if (pVM->fStack)
102         stackDelete(pVM->fStack);
103     pVM->fStack = stackCreate(nPStack);
104 #endif
105
106     pVM->textOut = ficlTextOut;
107
108     vmReset(pVM);
109     return pVM;
110 }
111
112
113 /**************************************************************************
114                         v m D e l e t e
115 ** Free all memory allocated to the specified VM and its subordinate 
116 ** structures.
117 **************************************************************************/
118 void vmDelete (FICL_VM *pVM)
119 {
120     if (pVM)
121     {
122         ficlFree(pVM->pStack);
123         ficlFree(pVM->rStack);
124 #if FICL_WANT_FLOAT
125         ficlFree(pVM->fStack);
126 #endif
127         ficlFree(pVM);
128     }
129
130     return;
131 }
132
133
134 /**************************************************************************
135                         v m E x e c u t e
136 ** Sets up the specified word to be run by the inner interpreter.
137 ** Executes the word's code part immediately, but in the case of
138 ** colon definition, the definition itself needs the inner interp
139 ** to complete. This does not happen until control reaches ficlExec
140 **************************************************************************/
141 void vmExecute(FICL_VM *pVM, FICL_WORD *pWord)
142 {
143     pVM->runningWord = pWord;
144     pWord->code(pVM);
145     return;
146 }
147
148
149 /**************************************************************************
150                         v m I n n e r L o o p
151 ** the mysterious inner interpreter...
152 ** This loop is the address interpreter that makes colon definitions
153 ** work. Upon entry, it assumes that the IP points to an entry in 
154 ** a definition (the body of a colon word). It runs one word at a time
155 ** until something does vmThrow. The catcher for this is expected to exist
156 ** in the calling code.
157 ** vmThrow gets you out of this loop with a longjmp()
158 ** Visual C++ 5 chokes on this loop in Release mode. Aargh.
159 **************************************************************************/
160 #if INLINE_INNER_LOOP == 0
161 void vmInnerLoop(FICL_VM *pVM)
162 {
163     M_INNER_LOOP(pVM);
164 }
165 #endif
166 #if 0
167 /*
168 ** Recast inner loop that inlines tokens for control structures, arithmetic and stack operations, 
169 ** as well as create does> : ; and various literals
170 */
171 typedef enum
172 {
173     PATCH = 0,
174     L0,
175     L1,
176     L2,
177     LMINUS1,
178     LMINUS2,
179     DROP,
180     SWAP,
181     DUP,
182     PICK,
183     ROLL,
184     FETCH,
185     STORE,
186     BRANCH,
187     CBRANCH,
188     LEAVE,
189     TO_R,
190     R_FROM,
191     EXIT;
192 } OPCODE;
193
194 typedef CELL *IPTYPE;
195
196 void vmInnerLoop(FICL_VM *pVM)
197 {
198     IPTYPE ip = pVM->ip;
199     FICL_STACK *pStack = pVM->pStack;
200
201     for (;;)
202     {
203         OPCODE o = (*ip++).i;
204         CELL c;
205         switch (o)
206         {
207         case L0:
208             stackPushINT(pStack, 0);
209             break;
210         case L1:
211             stackPushINT(pStack, 1);
212             break;
213         case L2:
214             stackPushINT(pStack, 2);
215             break;
216         case LMINUS1:
217             stackPushINT(pStack, -1);
218             break;
219         case LMINUS2:
220             stackPushINT(pStack, -2);
221             break;
222         case DROP:
223             stackDrop(pStack, 1);
224             break;
225         case SWAP:
226             stackRoll(pStack, 1);
227             break;
228         case DUP:
229             stackPick(pStack, 0);
230             break;
231         case PICK:
232             c = *ip++;
233             stackPick(pStack, c.i);
234             break;
235         case ROLL:
236             c = *ip++;
237             stackRoll(pStack, c.i);
238             break;
239         case EXIT:
240             return;
241         }
242     }
243
244     return;
245 }
246 #endif
247
248
249
250 /**************************************************************************
251                         v m G e t D i c t
252 ** Returns the address dictionary for this VM's system
253 **************************************************************************/
254 FICL_DICT  *vmGetDict(FICL_VM *pVM)
255 {
256         assert(pVM);
257         return pVM->pSys->dp;
258 }
259
260
261 /**************************************************************************
262                         v m G e t S t r i n g
263 ** Parses a string out of the VM input buffer and copies up to the first
264 ** FICL_STRING_MAX characters to the supplied destination buffer, a
265 ** FICL_STRING. The destination string is NULL terminated.
266 ** 
267 ** Returns the address of the first unused character in the dest buffer.
268 **************************************************************************/
269 char *vmGetString(FICL_VM *pVM, FICL_STRING *spDest, char delimiter)
270 {
271     STRINGINFO si = vmParseStringEx(pVM, delimiter, 0);
272
273     if (SI_COUNT(si) > FICL_STRING_MAX)
274     {
275         SI_SETLEN(si, FICL_STRING_MAX);
276     }
277
278     strncpy(spDest->text, SI_PTR(si), SI_COUNT(si));
279     spDest->text[SI_COUNT(si)] = '\0';
280     spDest->count = (FICL_COUNT)SI_COUNT(si);
281
282     return spDest->text + SI_COUNT(si) + 1;
283 }
284
285
286 /**************************************************************************
287                         v m G e t W o r d
288 ** vmGetWord calls vmGetWord0 repeatedly until it gets a string with 
289 ** non-zero length.
290 **************************************************************************/
291 STRINGINFO vmGetWord(FICL_VM *pVM)
292 {
293     STRINGINFO si = vmGetWord0(pVM);
294
295     if (SI_COUNT(si) == 0)
296     {
297         vmThrow(pVM, VM_RESTART);
298     }
299
300     return si;
301 }
302
303
304 /**************************************************************************
305                         v m G e t W o r d 0
306 ** Skip leading whitespace and parse a space delimited word from the tib.
307 ** Returns the start address and length of the word. Updates the tib
308 ** to reflect characters consumed, including the trailing delimiter.
309 ** If there's nothing of interest in the tib, returns zero. This function
310 ** does not use vmParseString because it uses isspace() rather than a
311 ** single  delimiter character.
312 **************************************************************************/
313 STRINGINFO vmGetWord0(FICL_VM *pVM)
314 {
315     char *pSrc      = vmGetInBuf(pVM);
316     char *pEnd      = vmGetInBufEnd(pVM);
317     STRINGINFO si;
318     FICL_UNS count = 0;
319     char ch;
320
321     pSrc = skipSpace(pSrc, pEnd);
322     SI_SETPTR(si, pSrc);
323
324     for (ch = *pSrc; (pEnd != pSrc) && !isspace(ch); ch = *++pSrc)
325     {
326         count++;
327     }
328
329     SI_SETLEN(si, count);
330
331     if ((pEnd != pSrc) && isspace(ch))    /* skip one trailing delimiter */
332         pSrc++;
333
334     vmUpdateTib(pVM, pSrc);
335
336     return si;
337 }
338
339
340 /**************************************************************************
341                         v m G e t W o r d T o P a d
342 ** Does vmGetWord and copies the result to the pad as a NULL terminated
343 ** string. Returns the length of the string. If the string is too long 
344 ** to fit in the pad, it is truncated.
345 **************************************************************************/
346 int vmGetWordToPad(FICL_VM *pVM)
347 {
348     STRINGINFO si;
349     char *cp = (char *)pVM->pad;
350     si = vmGetWord(pVM);
351
352     if (SI_COUNT(si) > nPAD)
353         SI_SETLEN(si, nPAD);
354
355     strncpy(cp, SI_PTR(si), SI_COUNT(si));
356     cp[SI_COUNT(si)] = '\0';
357     return (int)(SI_COUNT(si));
358 }
359
360
361 /**************************************************************************
362                         v m P a r s e S t r i n g
363 ** Parses a string out of the input buffer using the delimiter
364 ** specified. Skips leading delimiters, marks the start of the string,
365 ** and counts characters to the next delimiter it encounters. It then 
366 ** updates the vm input buffer to consume all these chars, including the
367 ** trailing delimiter. 
368 ** Returns the address and length of the parsed string, not including the
369 ** trailing delimiter.
370 **************************************************************************/
371 STRINGINFO vmParseString(FICL_VM *pVM, char delim)
372
373     return vmParseStringEx(pVM, delim, 1);
374 }
375
376 STRINGINFO vmParseStringEx(FICL_VM *pVM, char delim, char fSkipLeading)
377 {
378     STRINGINFO si;
379     char *pSrc      = vmGetInBuf(pVM);
380     char *pEnd      = vmGetInBufEnd(pVM);
381     char ch;
382
383     if (fSkipLeading)
384     {                       /* skip lead delimiters */
385         while ((pSrc != pEnd) && (*pSrc == delim))
386             pSrc++;
387     }
388
389     SI_SETPTR(si, pSrc);    /* mark start of text */
390
391     for (ch = *pSrc; (pSrc != pEnd)
392                   && (ch != delim)
393                   && (ch != '\r') 
394                   && (ch != '\n'); ch = *++pSrc)
395     {
396         ;                   /* find next delimiter or end of line */
397     }
398
399                             /* set length of result */
400     SI_SETLEN(si, pSrc - SI_PTR(si));
401
402     if ((pSrc != pEnd) && (*pSrc == delim))     /* gobble trailing delimiter */
403         pSrc++;
404
405     vmUpdateTib(pVM, pSrc);
406     return si;
407 }
408
409
410 /**************************************************************************
411                         v m P o p
412 ** 
413 **************************************************************************/
414 CELL vmPop(FICL_VM *pVM)
415 {
416     return stackPop(pVM->pStack);
417 }
418
419
420 /**************************************************************************
421                         v m P u s h
422 ** 
423 **************************************************************************/
424 void vmPush(FICL_VM *pVM, CELL c)
425 {
426     stackPush(pVM->pStack, c);
427     return;
428 }
429
430
431 /**************************************************************************
432                         v m P o p I P
433 ** 
434 **************************************************************************/
435 void vmPopIP(FICL_VM *pVM)
436 {
437     pVM->ip = (IPTYPE)(stackPopPtr(pVM->rStack));
438     return;
439 }
440
441
442 /**************************************************************************
443                         v m P u s h I P
444 ** 
445 **************************************************************************/
446 void vmPushIP(FICL_VM *pVM, IPTYPE newIP)
447 {
448     stackPushPtr(pVM->rStack, (void *)pVM->ip);
449     pVM->ip = newIP;
450     return;
451 }
452
453
454 /**************************************************************************
455                         v m P u s h T i b
456 ** Binds the specified input string to the VM and clears >IN (the index)
457 **************************************************************************/
458 void vmPushTib(FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib)
459 {
460     if (pSaveTib)
461     {
462         *pSaveTib = pVM->tib;
463     }
464
465     pVM->tib.cp = text;
466     pVM->tib.end = text + nChars;
467     pVM->tib.index = 0;
468 }
469
470
471 void vmPopTib(FICL_VM *pVM, TIB *pTib)
472 {
473     if (pTib)
474     {
475         pVM->tib = *pTib;
476     }
477     return;
478 }
479
480
481 /**************************************************************************
482                         v m Q u i t
483 ** 
484 **************************************************************************/
485 void vmQuit(FICL_VM *pVM)
486 {
487     stackReset(pVM->rStack);
488     pVM->fRestart    = 0;
489     pVM->ip          = NULL;
490     pVM->runningWord = NULL;
491     pVM->state       = INTERPRET;
492     pVM->tib.cp      = NULL;
493     pVM->tib.end     = NULL;
494     pVM->tib.index   = 0;
495     pVM->pad[0]      = '\0';
496     pVM->sourceID.i  = 0;
497     return;
498 }
499
500
501 /**************************************************************************
502                         v m R e s e t 
503 ** 
504 **************************************************************************/
505 void vmReset(FICL_VM *pVM)
506 {
507     vmQuit(pVM);
508     stackReset(pVM->pStack);
509 #if FICL_WANT_FLOAT
510     stackReset(pVM->fStack);
511 #endif
512     pVM->base        = 10;
513     return;
514 }
515
516
517 /**************************************************************************
518                         v m S e t T e x t O u t
519 ** Binds the specified output callback to the vm. If you pass NULL,
520 ** binds the default output function (ficlTextOut)
521 **************************************************************************/
522 void vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut)
523 {
524     if (textOut)
525         pVM->textOut = textOut;
526     else
527         pVM->textOut = ficlTextOut;
528
529     return;
530 }
531
532
533 /**************************************************************************
534                         v m T e x t O u t
535 ** Feeds text to the vm's output callback
536 **************************************************************************/
537 void vmTextOut(FICL_VM *pVM, char *text, int fNewline)
538 {
539     assert(pVM);
540     assert(pVM->textOut);
541     (pVM->textOut)(pVM, text, fNewline);
542
543     return;
544 }
545
546
547 /**************************************************************************
548                         v m T h r o w
549 ** 
550 **************************************************************************/
551 void vmThrow(FICL_VM *pVM, int except)
552 {
553     if (pVM->pState)
554         longjmp(*(pVM->pState), except);
555 }
556
557
558 void vmThrowErr(FICL_VM *pVM, char *fmt, ...)
559 {
560     va_list va;
561     va_start(va, fmt);
562     vsprintf(pVM->pad, fmt, va);
563     vmTextOut(pVM, pVM->pad, 1);
564     va_end(va);
565     longjmp(*(pVM->pState), VM_ERREXIT);
566 }
567
568
569 /**************************************************************************
570                         w o r d I s I m m e d i a t e
571 ** 
572 **************************************************************************/
573 int wordIsImmediate(FICL_WORD *pFW)
574 {
575     return ((pFW != NULL) && (pFW->flags & FW_IMMEDIATE));
576 }
577
578
579 /**************************************************************************
580                         w o r d I s C o m p i l e O n l y
581 ** 
582 **************************************************************************/
583 int wordIsCompileOnly(FICL_WORD *pFW)
584 {
585     return ((pFW != NULL) && (pFW->flags & FW_COMPILE));
586 }
587
588
589 /**************************************************************************
590                         s t r r e v
591 ** 
592 **************************************************************************/
593 char *strrev( char *string )    
594 {                               /* reverse a string in-place */
595     int i = strlen(string);
596     char *p1 = string;          /* first char of string */
597     char *p2 = string + i - 1;  /* last non-NULL char of string */
598     char c;
599
600     if (i > 1)
601     {
602         while (p1 < p2)
603         {
604             c = *p2;
605             *p2 = *p1;
606             *p1 = c;
607             p1++; p2--;
608         }
609     }
610         
611     return string;
612 }
613
614
615 /**************************************************************************
616                         d i g i t _ t o _ c h a r
617 ** 
618 **************************************************************************/
619 char digit_to_char(int value)
620 {
621     return digits[value];
622 }
623
624
625 /**************************************************************************
626                         i s P o w e r O f T w o
627 ** Tests whether supplied argument is an integer power of 2 (2**n)
628 ** where 32 > n > 1, and returns n if so. Otherwise returns zero.
629 **************************************************************************/
630 int isPowerOfTwo(FICL_UNS u)
631 {
632     int i = 1;
633     FICL_UNS t = 2;
634
635     for (; ((t <= u) && (t != 0)); i++, t <<= 1)
636     {
637         if (u == t)
638             return i;
639     }
640
641     return 0;
642 }
643
644
645 /**************************************************************************
646                         l t o a
647 ** 
648 **************************************************************************/
649 char *ltoa( FICL_INT value, char *string, int radix )
650 {                               /* convert long to string, any base */
651     char *cp = string;
652     int sign = ((radix == 10) && (value < 0));
653     int pwr;
654
655     assert(radix > 1);
656     assert(radix < 37);
657     assert(string);
658
659     pwr = isPowerOfTwo((FICL_UNS)radix);
660
661     if (sign)
662         value = -value;
663
664     if (value == 0)
665         *cp++ = '0';
666     else if (pwr != 0)
667     {
668         FICL_UNS v = (FICL_UNS) value;
669         FICL_UNS mask = (FICL_UNS) ~(-1 << pwr);
670         while (v)
671         {
672             *cp++ = digits[v & mask];
673             v >>= pwr;
674         }
675     }
676     else
677     {
678         UNSQR result;
679         DPUNS v;
680         v.hi = 0;
681         v.lo = (FICL_UNS)value;
682         while (v.lo)
683         {
684             result = ficlLongDiv(v, (FICL_UNS)radix);
685             *cp++ = digits[result.rem];
686             v.lo = result.quot;
687         }
688     }
689
690     if (sign)
691         *cp++ = '-';
692
693     *cp++ = '\0';
694
695     return strrev(string);
696 }
697
698
699 /**************************************************************************
700                         u l t o a
701 ** 
702 **************************************************************************/
703 char *ultoa(FICL_UNS value, char *string, int radix )
704 {                               /* convert long to string, any base */
705     char *cp = string;
706     DPUNS ud;
707     UNSQR result;
708
709     assert(radix > 1);
710     assert(radix < 37);
711     assert(string);
712
713     if (value == 0)
714         *cp++ = '0';
715     else
716     {
717         ud.hi = 0;
718         ud.lo = value;
719         result.quot = value;
720
721         while (ud.lo)
722         {
723             result = ficlLongDiv(ud, (FICL_UNS)radix);
724             ud.lo = result.quot;
725             *cp++ = digits[result.rem];
726         }
727     }
728
729     *cp++ = '\0';
730
731     return strrev(string);
732 }
733
734
735 /**************************************************************************
736                         c a s e F o l d
737 ** Case folds a NULL terminated string in place. All characters
738 ** get converted to lower case.
739 **************************************************************************/
740 char *caseFold(char *cp)
741 {
742     char *oldCp = cp;
743
744     while (*cp)
745     {
746         if (isupper(*cp))
747             *cp = (char)tolower(*cp);
748         cp++;
749     }
750
751     return oldCp;
752 }
753
754
755 /**************************************************************************
756                         s t r i n c m p
757 ** (jws) simplified the code a bit in hopes of appeasing Purify
758 **************************************************************************/
759 int strincmp(char *cp1, char *cp2, FICL_UNS count)
760 {
761     int i = 0;
762
763     for (; 0 < count; ++cp1, ++cp2, --count)
764     {
765         i = tolower(*cp1) - tolower(*cp2);
766         if (i != 0)
767             return i;
768         else if (*cp1 == '\0')
769             return 0;
770     }
771     return 0;
772 }
773
774 /**************************************************************************
775                         s k i p S p a c e
776 ** Given a string pointer, returns a pointer to the first non-space
777 ** char of the string, or to the NULL terminator if no such char found.
778 ** If the pointer reaches "end" first, stop there. Pass NULL to 
779 ** suppress this behavior.
780 **************************************************************************/
781 char *skipSpace(char *cp, char *end)
782 {
783     assert(cp);
784
785     while ((cp != end) && isspace(*cp))
786         cp++;
787
788     return cp;
789 }
790
791