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