]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/boot/ficl/vm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 = 0;
320
321     pSrc = skipSpace(pSrc, pEnd);
322     SI_SETPTR(si, pSrc);
323
324 /*
325     for (ch = *pSrc; (pEnd != pSrc) && !isspace(ch); ch = *++pSrc)
326     {
327         count++;
328     }
329 */
330
331     /* Changed to make Purify happier.  --lch */
332     for (;;)
333     {
334         if (pEnd == pSrc)
335             break;
336         ch = *pSrc;
337         if (isspace(ch))
338             break;
339         count++;
340         pSrc++;
341     }
342
343     SI_SETLEN(si, count);
344
345     if ((pEnd != pSrc) && isspace(ch))    /* skip one trailing delimiter */
346         pSrc++;
347
348     vmUpdateTib(pVM, pSrc);
349
350     return si;
351 }
352
353
354 /**************************************************************************
355                         v m G e t W o r d T o P a d
356 ** Does vmGetWord and copies the result to the pad as a NULL terminated
357 ** string. Returns the length of the string. If the string is too long 
358 ** to fit in the pad, it is truncated.
359 **************************************************************************/
360 int vmGetWordToPad(FICL_VM *pVM)
361 {
362     STRINGINFO si;
363     char *cp = (char *)pVM->pad;
364     si = vmGetWord(pVM);
365
366     if (SI_COUNT(si) > nPAD)
367         SI_SETLEN(si, nPAD);
368
369     strncpy(cp, SI_PTR(si), SI_COUNT(si));
370     cp[SI_COUNT(si)] = '\0';
371     return (int)(SI_COUNT(si));
372 }
373
374
375 /**************************************************************************
376                         v m P a r s e S t r i n g
377 ** Parses a string out of the input buffer using the delimiter
378 ** specified. Skips leading delimiters, marks the start of the string,
379 ** and counts characters to the next delimiter it encounters. It then 
380 ** updates the vm input buffer to consume all these chars, including the
381 ** trailing delimiter. 
382 ** Returns the address and length of the parsed string, not including the
383 ** trailing delimiter.
384 **************************************************************************/
385 STRINGINFO vmParseString(FICL_VM *pVM, char delim)
386
387     return vmParseStringEx(pVM, delim, 1);
388 }
389
390 STRINGINFO vmParseStringEx(FICL_VM *pVM, char delim, char fSkipLeading)
391 {
392     STRINGINFO si;
393     char *pSrc      = vmGetInBuf(pVM);
394     char *pEnd      = vmGetInBufEnd(pVM);
395     char ch;
396
397     if (fSkipLeading)
398     {                       /* skip lead delimiters */
399         while ((pSrc != pEnd) && (*pSrc == delim))
400             pSrc++;
401     }
402
403     SI_SETPTR(si, pSrc);    /* mark start of text */
404
405     for (ch = *pSrc; (pSrc != pEnd)
406                   && (ch != delim)
407                   && (ch != '\r') 
408                   && (ch != '\n'); ch = *++pSrc)
409     {
410         ;                   /* find next delimiter or end of line */
411     }
412
413                             /* set length of result */
414     SI_SETLEN(si, pSrc - SI_PTR(si));
415
416     if ((pSrc != pEnd) && (*pSrc == delim))     /* gobble trailing delimiter */
417         pSrc++;
418
419     vmUpdateTib(pVM, pSrc);
420     return si;
421 }
422
423
424 /**************************************************************************
425                         v m P o p
426 ** 
427 **************************************************************************/
428 CELL vmPop(FICL_VM *pVM)
429 {
430     return stackPop(pVM->pStack);
431 }
432
433
434 /**************************************************************************
435                         v m P u s h
436 ** 
437 **************************************************************************/
438 void vmPush(FICL_VM *pVM, CELL c)
439 {
440     stackPush(pVM->pStack, c);
441     return;
442 }
443
444
445 /**************************************************************************
446                         v m P o p I P
447 ** 
448 **************************************************************************/
449 void vmPopIP(FICL_VM *pVM)
450 {
451     pVM->ip = (IPTYPE)(stackPopPtr(pVM->rStack));
452     return;
453 }
454
455
456 /**************************************************************************
457                         v m P u s h I P
458 ** 
459 **************************************************************************/
460 void vmPushIP(FICL_VM *pVM, IPTYPE newIP)
461 {
462     stackPushPtr(pVM->rStack, (void *)pVM->ip);
463     pVM->ip = newIP;
464     return;
465 }
466
467
468 /**************************************************************************
469                         v m P u s h T i b
470 ** Binds the specified input string to the VM and clears >IN (the index)
471 **************************************************************************/
472 void vmPushTib(FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib)
473 {
474     if (pSaveTib)
475     {
476         *pSaveTib = pVM->tib;
477     }
478
479     pVM->tib.cp = text;
480     pVM->tib.end = text + nChars;
481     pVM->tib.index = 0;
482 }
483
484
485 void vmPopTib(FICL_VM *pVM, TIB *pTib)
486 {
487     if (pTib)
488     {
489         pVM->tib = *pTib;
490     }
491     return;
492 }
493
494
495 /**************************************************************************
496                         v m Q u i t
497 ** 
498 **************************************************************************/
499 void vmQuit(FICL_VM *pVM)
500 {
501     stackReset(pVM->rStack);
502     pVM->fRestart    = 0;
503     pVM->ip          = NULL;
504     pVM->runningWord = NULL;
505     pVM->state       = INTERPRET;
506     pVM->tib.cp      = NULL;
507     pVM->tib.end     = NULL;
508     pVM->tib.index   = 0;
509     pVM->pad[0]      = '\0';
510     pVM->sourceID.i  = 0;
511     return;
512 }
513
514
515 /**************************************************************************
516                         v m R e s e t 
517 ** 
518 **************************************************************************/
519 void vmReset(FICL_VM *pVM)
520 {
521     vmQuit(pVM);
522     stackReset(pVM->pStack);
523 #if FICL_WANT_FLOAT
524     stackReset(pVM->fStack);
525 #endif
526     pVM->base        = 10;
527     return;
528 }
529
530
531 /**************************************************************************
532                         v m S e t T e x t O u t
533 ** Binds the specified output callback to the vm. If you pass NULL,
534 ** binds the default output function (ficlTextOut)
535 **************************************************************************/
536 void vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut)
537 {
538     if (textOut)
539         pVM->textOut = textOut;
540     else
541         pVM->textOut = ficlTextOut;
542
543     return;
544 }
545
546
547 /**************************************************************************
548                         v m T e x t O u t
549 ** Feeds text to the vm's output callback
550 **************************************************************************/
551 void vmTextOut(FICL_VM *pVM, char *text, int fNewline)
552 {
553     assert(pVM);
554     assert(pVM->textOut);
555     (pVM->textOut)(pVM, text, fNewline);
556
557     return;
558 }
559
560
561 /**************************************************************************
562                         v m T h r o w
563 ** 
564 **************************************************************************/
565 void vmThrow(FICL_VM *pVM, int except)
566 {
567     if (pVM->pState)
568         longjmp(*(pVM->pState), except);
569 }
570
571
572 void vmThrowErr(FICL_VM *pVM, char *fmt, ...)
573 {
574     va_list va;
575     va_start(va, fmt);
576     vsprintf(pVM->pad, fmt, va);
577     vmTextOut(pVM, pVM->pad, 1);
578     va_end(va);
579     longjmp(*(pVM->pState), VM_ERREXIT);
580 }
581
582
583 /**************************************************************************
584                         w o r d I s I m m e d i a t e
585 ** 
586 **************************************************************************/
587 int wordIsImmediate(FICL_WORD *pFW)
588 {
589     return ((pFW != NULL) && (pFW->flags & FW_IMMEDIATE));
590 }
591
592
593 /**************************************************************************
594                         w o r d I s C o m p i l e O n l y
595 ** 
596 **************************************************************************/
597 int wordIsCompileOnly(FICL_WORD *pFW)
598 {
599     return ((pFW != NULL) && (pFW->flags & FW_COMPILE));
600 }
601
602
603 /**************************************************************************
604                         s t r r e v
605 ** 
606 **************************************************************************/
607 char *strrev( char *string )    
608 {                               /* reverse a string in-place */
609     int i = strlen(string);
610     char *p1 = string;          /* first char of string */
611     char *p2 = string + i - 1;  /* last non-NULL char of string */
612     char c;
613
614     if (i > 1)
615     {
616         while (p1 < p2)
617         {
618             c = *p2;
619             *p2 = *p1;
620             *p1 = c;
621             p1++; p2--;
622         }
623     }
624         
625     return string;
626 }
627
628
629 /**************************************************************************
630                         d i g i t _ t o _ c h a r
631 ** 
632 **************************************************************************/
633 char digit_to_char(int value)
634 {
635     return digits[value];
636 }
637
638
639 /**************************************************************************
640                         i s P o w e r O f T w o
641 ** Tests whether supplied argument is an integer power of 2 (2**n)
642 ** where 32 > n > 1, and returns n if so. Otherwise returns zero.
643 **************************************************************************/
644 int isPowerOfTwo(FICL_UNS u)
645 {
646     int i = 1;
647     FICL_UNS t = 2;
648
649     for (; ((t <= u) && (t != 0)); i++, t <<= 1)
650     {
651         if (u == t)
652             return i;
653     }
654
655     return 0;
656 }
657
658
659 /**************************************************************************
660                         l t o a
661 ** 
662 **************************************************************************/
663 char *ltoa( FICL_INT value, char *string, int radix )
664 {                               /* convert long to string, any base */
665     char *cp = string;
666     int sign = ((radix == 10) && (value < 0));
667     int pwr;
668
669     assert(radix > 1);
670     assert(radix < 37);
671     assert(string);
672
673     pwr = isPowerOfTwo((FICL_UNS)radix);
674
675     if (sign)
676         value = -value;
677
678     if (value == 0)
679         *cp++ = '0';
680     else if (pwr != 0)
681     {
682         FICL_UNS v = (FICL_UNS) value;
683         FICL_UNS mask = (FICL_UNS) ~(-1 << pwr);
684         while (v)
685         {
686             *cp++ = digits[v & mask];
687             v >>= pwr;
688         }
689     }
690     else
691     {
692         UNSQR result;
693         DPUNS v;
694         v.hi = 0;
695         v.lo = (FICL_UNS)value;
696         while (v.lo)
697         {
698             result = ficlLongDiv(v, (FICL_UNS)radix);
699             *cp++ = digits[result.rem];
700             v.lo = result.quot;
701         }
702     }
703
704     if (sign)
705         *cp++ = '-';
706
707     *cp++ = '\0';
708
709     return strrev(string);
710 }
711
712
713 /**************************************************************************
714                         u l t o a
715 ** 
716 **************************************************************************/
717 char *ultoa(FICL_UNS value, char *string, int radix )
718 {                               /* convert long to string, any base */
719     char *cp = string;
720     DPUNS ud;
721     UNSQR result;
722
723     assert(radix > 1);
724     assert(radix < 37);
725     assert(string);
726
727     if (value == 0)
728         *cp++ = '0';
729     else
730     {
731         ud.hi = 0;
732         ud.lo = value;
733         result.quot = value;
734
735         while (ud.lo)
736         {
737             result = ficlLongDiv(ud, (FICL_UNS)radix);
738             ud.lo = result.quot;
739             *cp++ = digits[result.rem];
740         }
741     }
742
743     *cp++ = '\0';
744
745     return strrev(string);
746 }
747
748
749 /**************************************************************************
750                         c a s e F o l d
751 ** Case folds a NULL terminated string in place. All characters
752 ** get converted to lower case.
753 **************************************************************************/
754 char *caseFold(char *cp)
755 {
756     char *oldCp = cp;
757
758     while (*cp)
759     {
760         if (isupper(*cp))
761             *cp = (char)tolower(*cp);
762         cp++;
763     }
764
765     return oldCp;
766 }
767
768
769 /**************************************************************************
770                         s t r i n c m p
771 ** (jws) simplified the code a bit in hopes of appeasing Purify
772 **************************************************************************/
773 int strincmp(char *cp1, char *cp2, FICL_UNS count)
774 {
775     int i = 0;
776
777     for (; 0 < count; ++cp1, ++cp2, --count)
778     {
779         i = tolower(*cp1) - tolower(*cp2);
780         if (i != 0)
781             return i;
782         else if (*cp1 == '\0')
783             return 0;
784     }
785     return 0;
786 }
787
788 /**************************************************************************
789                         s k i p S p a c e
790 ** Given a string pointer, returns a pointer to the first non-space
791 ** char of the string, or to the NULL terminator if no such char found.
792 ** If the pointer reaches "end" first, stop there. Pass NULL to 
793 ** suppress this behavior.
794 **************************************************************************/
795 char *skipSpace(char *cp, char *end)
796 {
797     assert(cp);
798
799     while ((cp != end) && isspace(*cp))
800         cp++;
801
802     return cp;
803 }
804
805