tools.c revision 271135
1/*******************************************************************
2** t o o l s . c
3** Forth Inspired Command Language - programming tools
4** Author: John Sadler (john_sadler@alum.mit.edu)
5** Created: 20 June 2000
6** $Id: tools.c,v 1.11 2001/12/05 07:21:34 jsadler Exp $
7*******************************************************************/
8/*
9** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
10** All rights reserved.
11**
12** Get the latest Ficl release at http://ficl.sourceforge.net
13**
14** I am interested in hearing from anyone who uses ficl. If you have
15** a problem, a success story, a defect, an enhancement request, or
16** if you would like to contribute to the ficl release, please
17** contact me by email at the address above.
18**
19** L I C E N S E  and  D I S C L A I M E R
20**
21** Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions
23** are met:
24** 1. Redistributions of source code must retain the above copyright
25**    notice, this list of conditions and the following disclaimer.
26** 2. Redistributions in binary form must reproduce the above copyright
27**    notice, this list of conditions and the following disclaimer in the
28**    documentation and/or other materials provided with the distribution.
29**
30** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
31** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
34** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40** SUCH DAMAGE.
41*/
42
43/*
44** NOTES:
45** SEE needs information about the addresses of functions that
46** are the CFAs of colon definitions, constants, variables, DOES>
47** words, and so on. It gets this information from a table and supporting
48** functions in words.c.
49** colonParen doDoes createParen variableParen userParen constantParen
50**
51** Step and break debugger for Ficl
52** debug  ( xt -- )   Start debugging an xt
53** Set a breakpoint
54** Specify breakpoint default action
55*/
56
57/* $FreeBSD: stable/10/sys/boot/ficl/tools.c 271135 2014-09-04 21:01:10Z emaste $ */
58
59#ifdef TESTMAIN
60#include <stdlib.h>
61#include <stdio.h>          /* sprintf */
62#include <ctype.h>
63#else
64#include <stand.h>
65#endif
66#include <string.h>
67#include "ficl.h"
68
69
70#if 0
71/*
72** nBREAKPOINTS sizes the breakpoint array. One breakpoint (bp 0) is reserved
73** for the STEP command. The rest are user programmable.
74*/
75#define nBREAKPOINTS 32
76
77#endif
78
79
80/**************************************************************************
81                        v m S e t B r e a k
82** Set a breakpoint at the current value of IP by
83** storing that address in a BREAKPOINT record
84**************************************************************************/
85static void vmSetBreak(FICL_VM *pVM, FICL_BREAKPOINT *pBP)
86{
87    FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");
88    assert(pStep);
89
90    pBP->address = pVM->ip;
91    pBP->origXT = *pVM->ip;
92    *pVM->ip = pStep;
93}
94
95
96/**************************************************************************
97**                      d e b u g P r o m p t
98**************************************************************************/
99static void debugPrompt(FICL_VM *pVM)
100{
101        vmTextOut(pVM, "dbg> ", 0);
102}
103
104
105/**************************************************************************
106**                      i s A F i c l W o r d
107** Vet a candidate pointer carefully to make sure
108** it's not some chunk o' inline data...
109** It has to have a name, and it has to look
110** like it's in the dictionary address range.
111** NOTE: this excludes :noname words!
112**************************************************************************/
113int isAFiclWord(FICL_DICT *pd, FICL_WORD *pFW)
114{
115
116    if (!dictIncludes(pd, pFW))
117       return 0;
118
119    if (!dictIncludes(pd, pFW->name))
120        return 0;
121
122	if ((pFW->link != NULL) && !dictIncludes(pd, pFW->link))
123		return 0;
124
125    if ((pFW->nName <= 0) || (pFW->name[pFW->nName] != '\0'))
126		return 0;
127
128	if (strlen(pFW->name) != pFW->nName)
129		return 0;
130
131	return 1;
132}
133
134
135#if 0
136static int isPrimitive(FICL_WORD *pFW)
137{
138    WORDKIND wk = ficlWordClassify(pFW);
139    return ((wk != COLON) && (wk != DOES));
140}
141#endif
142
143
144/**************************************************************************
145                        f i n d E n c l o s i n g W o r d
146** Given a pointer to something, check to make sure it's an address in the
147** dictionary. If so, search backwards until we find something that looks
148** like a dictionary header. If successful, return the address of the
149** FICL_WORD found. Otherwise return NULL.
150** nSEARCH_CELLS sets the maximum neighborhood this func will search before giving up
151**************************************************************************/
152#define nSEARCH_CELLS 100
153
154static FICL_WORD *findEnclosingWord(FICL_VM *pVM, CELL *cp)
155{
156    FICL_WORD *pFW;
157    FICL_DICT *pd = vmGetDict(pVM);
158    int i;
159
160    if (!dictIncludes(pd, (void *)cp))
161        return NULL;
162
163    for (i = nSEARCH_CELLS; i > 0; --i, --cp)
164    {
165        pFW = (FICL_WORD *)(cp + 1 - (sizeof (FICL_WORD) / sizeof (CELL)));
166        if (isAFiclWord(pd, pFW))
167            return pFW;
168    }
169
170    return NULL;
171}
172
173
174/**************************************************************************
175                        s e e
176** TOOLS ( "<spaces>name" -- )
177** Display a human-readable representation of the named word's definition.
178** The source of the representation (object-code decompilation, source
179** block, etc.) and the particular form of the display is implementation
180** defined.
181**************************************************************************/
182/*
183** seeColon (for proctologists only)
184** Walks a colon definition, decompiling
185** on the fly. Knows about primitive control structures.
186*/
187static void seeColon(FICL_VM *pVM, CELL *pc)
188{
189	char *cp;
190    CELL *param0 = pc;
191    FICL_DICT *pd = vmGetDict(pVM);
192	FICL_WORD *pSemiParen = ficlLookup(pVM->pSys, "(;)");
193    assert(pSemiParen);
194
195    for (; pc->p != pSemiParen; pc++)
196    {
197        FICL_WORD *pFW = (FICL_WORD *)(pc->p);
198
199        cp = pVM->pad;
200		if ((void *)pc == (void *)pVM->ip)
201			*cp++ = '>';
202		else
203			*cp++ = ' ';
204        cp += sprintf(cp, "%3d   ", (int)(pc-param0));
205
206        if (isAFiclWord(pd, pFW))
207        {
208            WORDKIND kind = ficlWordClassify(pFW);
209            CELL c;
210
211            switch (kind)
212            {
213            case LITERAL:
214                c = *++pc;
215                if (isAFiclWord(pd, c.p))
216                {
217                    FICL_WORD *pLit = (FICL_WORD *)c.p;
218                    sprintf(cp, "%.*s ( %#lx literal )",
219                        pLit->nName, pLit->name, (unsigned long)c.u);
220                }
221                else
222                    sprintf(cp, "literal %ld (%#lx)",
223                        (long)c.i, (unsigned long)c.u);
224                break;
225            case STRINGLIT:
226                {
227                    FICL_STRING *sp = (FICL_STRING *)(void *)++pc;
228                    pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;
229                    sprintf(cp, "s\" %.*s\"", sp->count, sp->text);
230                }
231                break;
232            case CSTRINGLIT:
233                {
234                    FICL_STRING *sp = (FICL_STRING *)(void *)++pc;
235                    pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;
236                    sprintf(cp, "c\" %.*s\"", sp->count, sp->text);
237                }
238                break;
239            case IF:
240                c = *++pc;
241                if (c.i > 0)
242                    sprintf(cp, "if / while (branch %d)", (int)(pc+c.i-param0));
243                else
244                    sprintf(cp, "until (branch %d)",      (int)(pc+c.i-param0));
245                break;
246            case BRANCH:
247                c = *++pc;
248                if (c.i == 0)
249                    sprintf(cp, "repeat (branch %d)",     (int)(pc+c.i-param0));
250                else if (c.i == 1)
251                    sprintf(cp, "else (branch %d)",       (int)(pc+c.i-param0));
252                else
253                    sprintf(cp, "endof (branch %d)",      (int)(pc+c.i-param0));
254                break;
255
256            case OF:
257                c = *++pc;
258                sprintf(cp, "of (branch %d)",       (int)(pc+c.i-param0));
259                break;
260
261            case QDO:
262                c = *++pc;
263                sprintf(cp, "?do (leave %d)",  (int)((CELL *)c.p-param0));
264                break;
265            case DO:
266                c = *++pc;
267                sprintf(cp, "do (leave %d)", (int)((CELL *)c.p-param0));
268                break;
269            case LOOP:
270                c = *++pc;
271                sprintf(cp, "loop (branch %d)", (int)(pc+c.i-param0));
272                break;
273            case PLOOP:
274                c = *++pc;
275                sprintf(cp, "+loop (branch %d)", (int)(pc+c.i-param0));
276                break;
277            default:
278                sprintf(cp, "%.*s", pFW->nName, pFW->name);
279                break;
280            }
281
282        }
283        else /* probably not a word - punt and print value */
284        {
285            sprintf(cp, "%ld ( %#lx )", (long)pc->i, (unsigned long)pc->u);
286        }
287
288		vmTextOut(pVM, pVM->pad, 1);
289    }
290
291    vmTextOut(pVM, ";", 1);
292}
293
294/*
295** Here's the outer part of the decompiler. It's
296** just a big nested conditional that checks the
297** CFA of the word to decompile for each kind of
298** known word-builder code, and tries to do
299** something appropriate. If the CFA is not recognized,
300** just indicate that it is a primitive.
301*/
302static void seeXT(FICL_VM *pVM)
303{
304    FICL_WORD *pFW;
305    WORDKIND kind;
306
307    pFW = (FICL_WORD *)stackPopPtr(pVM->pStack);
308    kind = ficlWordClassify(pFW);
309
310    switch (kind)
311    {
312    case COLON:
313        sprintf(pVM->pad, ": %.*s", pFW->nName, pFW->name);
314        vmTextOut(pVM, pVM->pad, 1);
315        seeColon(pVM, pFW->param);
316        break;
317
318    case DOES:
319        vmTextOut(pVM, "does>", 1);
320        seeColon(pVM, (CELL *)pFW->param->p);
321        break;
322
323    case CREATE:
324        vmTextOut(pVM, "create", 1);
325        break;
326
327    case VARIABLE:
328        sprintf(pVM->pad, "variable = %ld (%#lx)",
329            (long)pFW->param->i, (unsigned long)pFW->param->u);
330        vmTextOut(pVM, pVM->pad, 1);
331        break;
332
333#if FICL_WANT_USER
334    case USER:
335        sprintf(pVM->pad, "user variable %ld (%#lx)",
336            (long)pFW->param->i, (unsigned long)pFW->param->u);
337        vmTextOut(pVM, pVM->pad, 1);
338        break;
339#endif
340
341    case CONSTANT:
342        sprintf(pVM->pad, "constant = %ld (%#lx)",
343            (long)pFW->param->i, (unsigned long)pFW->param->u);
344        vmTextOut(pVM, pVM->pad, 1);
345
346    default:
347        sprintf(pVM->pad, "%.*s is a primitive", pFW->nName, pFW->name);
348        vmTextOut(pVM, pVM->pad, 1);
349        break;
350    }
351
352    if (pFW->flags & FW_IMMEDIATE)
353    {
354        vmTextOut(pVM, "immediate", 1);
355    }
356
357    if (pFW->flags & FW_COMPILE)
358    {
359        vmTextOut(pVM, "compile-only", 1);
360    }
361
362    return;
363}
364
365
366static void see(FICL_VM *pVM)
367{
368    ficlTick(pVM);
369    seeXT(pVM);
370    return;
371}
372
373
374/**************************************************************************
375                        f i c l D e b u g X T
376** debug  ( xt -- )
377** Given an xt of a colon definition or a word defined by DOES>, set the
378** VM up to debug the word: push IP, set the xt as the next thing to execute,
379** set a breakpoint at its first instruction, and run to the breakpoint.
380** Note: the semantics of this word are equivalent to "step in"
381**************************************************************************/
382void ficlDebugXT(FICL_VM *pVM)
383{
384    FICL_WORD *xt    = stackPopPtr(pVM->pStack);
385    WORDKIND   wk    = ficlWordClassify(xt);
386
387    stackPushPtr(pVM->pStack, xt);
388    seeXT(pVM);
389
390    switch (wk)
391    {
392    case COLON:
393    case DOES:
394        /*
395        ** Run the colon code and set a breakpoint at the next instruction
396        */
397        vmExecute(pVM, xt);
398        vmSetBreak(pVM, &(pVM->pSys->bpStep));
399        break;
400
401    default:
402        vmExecute(pVM, xt);
403        break;
404    }
405
406    return;
407}
408
409
410/**************************************************************************
411                        s t e p I n
412** FICL
413** Execute the next instruction, stepping into it if it's a colon definition
414** or a does> word. This is the easy kind of step.
415**************************************************************************/
416void stepIn(FICL_VM *pVM)
417{
418    /*
419    ** Do one step of the inner loop
420    */
421    {
422        M_VM_STEP(pVM)
423    }
424
425    /*
426    ** Now set a breakpoint at the next instruction
427    */
428    vmSetBreak(pVM, &(pVM->pSys->bpStep));
429
430    return;
431}
432
433
434/**************************************************************************
435                        s t e p O v e r
436** FICL
437** Execute the next instruction atomically. This requires some insight into
438** the memory layout of compiled code. Set a breakpoint at the next instruction
439** in this word, and run until we hit it
440**************************************************************************/
441void stepOver(FICL_VM *pVM)
442{
443    FICL_WORD *pFW;
444    WORDKIND kind;
445    FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");
446    assert(pStep);
447
448    pFW = *pVM->ip;
449    kind = ficlWordClassify(pFW);
450
451    switch (kind)
452    {
453    case COLON:
454    case DOES:
455        /*
456        ** assume that the next cell holds an instruction
457        ** set a breakpoint there and return to the inner interp
458        */
459        pVM->pSys->bpStep.address = pVM->ip + 1;
460        pVM->pSys->bpStep.origXT =  pVM->ip[1];
461        pVM->ip[1] = pStep;
462        break;
463
464    default:
465        stepIn(pVM);
466        break;
467    }
468
469    return;
470}
471
472
473/**************************************************************************
474                        s t e p - b r e a k
475** FICL
476** Handles breakpoints for stepped execution.
477** Upon entry, bpStep contains the address and replaced instruction
478** of the current breakpoint.
479** Clear the breakpoint
480** Get a command from the console.
481** i (step in) - execute the current instruction and set a new breakpoint
482**    at the IP
483** o (step over) - execute the current instruction to completion and set
484**    a new breakpoint at the IP
485** g (go) - execute the current instruction and exit
486** q (quit) - abort current word
487** b (toggle breakpoint)
488**************************************************************************/
489void stepBreak(FICL_VM *pVM)
490{
491    STRINGINFO si;
492    FICL_WORD *pFW;
493    FICL_WORD *pOnStep;
494
495    if (!pVM->fRestart)
496    {
497        assert(pVM->pSys->bpStep.address);
498        assert(pVM->pSys->bpStep.origXT);
499        /*
500        ** Clear the breakpoint that caused me to run
501        ** Restore the original instruction at the breakpoint,
502        ** and restore the IP
503        */
504        pVM->ip = (IPTYPE)(pVM->pSys->bpStep.address);
505        *pVM->ip = pVM->pSys->bpStep.origXT;
506
507        /*
508        ** If there's an onStep, do it
509        */
510        pOnStep = ficlLookup(pVM->pSys, "on-step");
511        if (pOnStep)
512            ficlExecXT(pVM, pOnStep);
513
514        /*
515        ** Print the name of the next instruction
516        */
517        pFW = pVM->pSys->bpStep.origXT;
518        sprintf(pVM->pad, "next: %.*s", pFW->nName, pFW->name);
519#if 0
520        if (isPrimitive(pFW))
521        {
522            strcat(pVM->pad, " ( primitive )");
523        }
524#endif
525
526        vmTextOut(pVM, pVM->pad, 1);
527        debugPrompt(pVM);
528    }
529    else
530    {
531        pVM->fRestart = 0;
532    }
533
534    si = vmGetWord(pVM);
535
536    if      (!strincmp(si.cp, "i", si.count))
537    {
538        stepIn(pVM);
539    }
540    else if (!strincmp(si.cp, "g", si.count))
541    {
542        return;
543    }
544    else if (!strincmp(si.cp, "l", si.count))
545    {
546        FICL_WORD *xt;
547        xt = findEnclosingWord(pVM, (CELL *)(pVM->ip));
548        if (xt)
549        {
550            stackPushPtr(pVM->pStack, xt);
551            seeXT(pVM);
552        }
553        else
554        {
555            vmTextOut(pVM, "sorry - can't do that", 1);
556        }
557        vmThrow(pVM, VM_RESTART);
558    }
559    else if (!strincmp(si.cp, "o", si.count))
560    {
561        stepOver(pVM);
562    }
563    else if (!strincmp(si.cp, "q", si.count))
564    {
565        ficlTextOut(pVM, FICL_PROMPT, 0);
566        vmThrow(pVM, VM_ABORT);
567    }
568    else if (!strincmp(si.cp, "x", si.count))
569    {
570        /*
571        ** Take whatever's left in the TIB and feed it to a subordinate ficlExec
572        */
573        int ret;
574        char *cp = pVM->tib.cp + pVM->tib.index;
575        int count = pVM->tib.end - cp;
576        FICL_WORD *oldRun = pVM->runningWord;
577
578        ret = ficlExecC(pVM, cp, count);
579
580        if (ret == VM_OUTOFTEXT)
581        {
582            ret = VM_RESTART;
583            pVM->runningWord = oldRun;
584            vmTextOut(pVM, "", 1);
585        }
586
587        vmThrow(pVM, ret);
588    }
589    else
590    {
591        vmTextOut(pVM, "i -- step In", 1);
592        vmTextOut(pVM, "o -- step Over", 1);
593        vmTextOut(pVM, "g -- Go (execute to completion)", 1);
594        vmTextOut(pVM, "l -- List source code", 1);
595        vmTextOut(pVM, "q -- Quit (stop debugging and abort)", 1);
596        vmTextOut(pVM, "x -- eXecute the rest of the line as ficl words", 1);
597        debugPrompt(pVM);
598        vmThrow(pVM, VM_RESTART);
599    }
600
601    return;
602}
603
604
605/**************************************************************************
606                        b y e
607** TOOLS
608** Signal the system to shut down - this causes ficlExec to return
609** VM_USEREXIT. The rest is up to you.
610**************************************************************************/
611static void bye(FICL_VM *pVM)
612{
613    vmThrow(pVM, VM_USEREXIT);
614    return;
615}
616
617
618/**************************************************************************
619                        d i s p l a y S t a c k
620** TOOLS
621** Display the parameter stack (code for ".s")
622**************************************************************************/
623static void displayPStack(FICL_VM *pVM)
624{
625    FICL_STACK *pStk = pVM->pStack;
626    int d = stackDepth(pStk);
627    int i;
628    CELL *pCell;
629
630    vmCheckStack(pVM, 0, 0);
631
632    if (d == 0)
633        vmTextOut(pVM, "(Stack Empty) ", 0);
634    else
635    {
636        pCell = pStk->base;
637        for (i = 0; i < d; i++)
638        {
639            vmTextOut(pVM, ltoa((*pCell++).i, pVM->pad, pVM->base), 0);
640            vmTextOut(pVM, " ", 0);
641        }
642    }
643    return;
644}
645
646
647static void displayRStack(FICL_VM *pVM)
648{
649    FICL_STACK *pStk = pVM->rStack;
650    int d = stackDepth(pStk);
651    int i;
652    CELL *pCell;
653    FICL_DICT *dp = vmGetDict(pVM);
654
655    vmCheckStack(pVM, 0, 0);
656
657    if (d == 0)
658        vmTextOut(pVM, "(Stack Empty) ", 0);
659    else
660    {
661        pCell = pStk->base;
662        for (i = 0; i < d; i++)
663        {
664            CELL c = *pCell++;
665            /*
666            ** Attempt to find the word that contains the
667            ** stacked address (as if it is part of a colon definition).
668            ** If this works, print the name of the word. Otherwise print
669            ** the value as a number.
670            */
671            if (dictIncludes(dp, c.p))
672            {
673                FICL_WORD *pFW = findEnclosingWord(pVM, c.p);
674                if (pFW)
675                {
676                    int offset = (CELL *)c.p - &pFW->param[0];
677                    sprintf(pVM->pad, "%s+%d ", pFW->name, offset);
678                    vmTextOut(pVM, pVM->pad, 0);
679                    continue;  /* no need to print the numeric value */
680                }
681            }
682            vmTextOut(pVM, ltoa(c.i, pVM->pad, pVM->base), 0);
683            vmTextOut(pVM, " ", 0);
684        }
685    }
686
687    return;
688}
689
690
691/**************************************************************************
692                        f o r g e t - w i d
693**
694**************************************************************************/
695static void forgetWid(FICL_VM *pVM)
696{
697    FICL_DICT *pDict = vmGetDict(pVM);
698    FICL_HASH *pHash;
699
700    pHash = (FICL_HASH *)stackPopPtr(pVM->pStack);
701    hashForget(pHash, pDict->here);
702
703    return;
704}
705
706
707/**************************************************************************
708                        f o r g e t
709** TOOLS EXT  ( "<spaces>name" -- )
710** Skip leading space delimiters. Parse name delimited by a space.
711** Find name, then delete name from the dictionary along with all
712** words added to the dictionary after name. An ambiguous
713** condition exists if name cannot be found.
714**
715** If the Search-Order word set is present, FORGET searches the
716** compilation word list. An ambiguous condition exists if the
717** compilation word list is deleted.
718**************************************************************************/
719static void forget(FICL_VM *pVM)
720{
721    void *where;
722    FICL_DICT *pDict = vmGetDict(pVM);
723    FICL_HASH *pHash = pDict->pCompile;
724
725    ficlTick(pVM);
726    where = ((FICL_WORD *)stackPopPtr(pVM->pStack))->name;
727    hashForget(pHash, where);
728    pDict->here = PTRtoCELL where;
729
730    return;
731}
732
733
734/**************************************************************************
735                        l i s t W o r d s
736**
737**************************************************************************/
738#define nCOLWIDTH 8
739static void listWords(FICL_VM *pVM)
740{
741    FICL_DICT *dp = vmGetDict(pVM);
742    FICL_HASH *pHash = dp->pSearch[dp->nLists - 1];
743    FICL_WORD *wp;
744    int nChars = 0;
745    int len;
746    int y = 0;
747    unsigned i;
748    int nWords = 0;
749    char *cp;
750    char *pPad = pVM->pad;
751
752    for (i = 0; i < pHash->size; i++)
753    {
754        for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)
755        {
756            if (wp->nName == 0) /* ignore :noname defs */
757                continue;
758
759            cp = wp->name;
760            nChars += sprintf(pPad + nChars, "%s", cp);
761
762            if (nChars > 70)
763            {
764                pPad[nChars] = '\0';
765                nChars = 0;
766                y++;
767                if(y>23) {
768                        y=0;
769                        vmTextOut(pVM, "--- Press Enter to continue ---",0);
770                        getchar();
771                        vmTextOut(pVM,"\r",0);
772                }
773                vmTextOut(pVM, pPad, 1);
774            }
775            else
776            {
777                len = nCOLWIDTH - nChars % nCOLWIDTH;
778                while (len-- > 0)
779                    pPad[nChars++] = ' ';
780            }
781
782            if (nChars > 70)
783            {
784                pPad[nChars] = '\0';
785                nChars = 0;
786                y++;
787                if(y>23) {
788                        y=0;
789                        vmTextOut(pVM, "--- Press Enter to continue ---",0);
790                        getchar();
791                        vmTextOut(pVM,"\r",0);
792                }
793                vmTextOut(pVM, pPad, 1);
794            }
795        }
796    }
797
798    if (nChars > 0)
799    {
800        pPad[nChars] = '\0';
801        nChars = 0;
802        vmTextOut(pVM, pPad, 1);
803    }
804
805    sprintf(pVM->pad, "Dictionary: %d words, %ld cells used of %u total",
806        nWords, (long) (dp->here - dp->dict), dp->size);
807    vmTextOut(pVM, pVM->pad, 1);
808    return;
809}
810
811
812/**************************************************************************
813                        l i s t E n v
814** Print symbols defined in the environment
815**************************************************************************/
816static void listEnv(FICL_VM *pVM)
817{
818    FICL_DICT *dp = pVM->pSys->envp;
819    FICL_HASH *pHash = dp->pForthWords;
820    FICL_WORD *wp;
821    unsigned i;
822    int nWords = 0;
823
824    for (i = 0; i < pHash->size; i++)
825    {
826        for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)
827        {
828            vmTextOut(pVM, wp->name, 1);
829        }
830    }
831
832    sprintf(pVM->pad, "Environment: %d words, %ld cells used of %u total",
833        nWords, (long) (dp->here - dp->dict), dp->size);
834    vmTextOut(pVM, pVM->pad, 1);
835    return;
836}
837
838
839/**************************************************************************
840                        e n v C o n s t a n t
841** Ficl interface to ficlSetEnv and ficlSetEnvD - allow ficl code to set
842** environment constants...
843**************************************************************************/
844static void envConstant(FICL_VM *pVM)
845{
846    unsigned value;
847
848#if FICL_ROBUST > 1
849    vmCheckStack(pVM, 1, 0);
850#endif
851
852    vmGetWordToPad(pVM);
853    value = POPUNS();
854    ficlSetEnv(pVM->pSys, pVM->pad, (FICL_UNS)value);
855    return;
856}
857
858static void env2Constant(FICL_VM *pVM)
859{
860    unsigned v1, v2;
861
862#if FICL_ROBUST > 1
863    vmCheckStack(pVM, 2, 0);
864#endif
865
866    vmGetWordToPad(pVM);
867    v2 = POPUNS();
868    v1 = POPUNS();
869    ficlSetEnvD(pVM->pSys, pVM->pad, v1, v2);
870    return;
871}
872
873
874/**************************************************************************
875                        f i c l C o m p i l e T o o l s
876** Builds wordset for debugger and TOOLS optional word set
877**************************************************************************/
878
879void ficlCompileTools(FICL_SYSTEM *pSys)
880{
881    FICL_DICT *dp = pSys->dp;
882    assert (dp);
883
884    /*
885    ** TOOLS and TOOLS EXT
886    */
887    dictAppendWord(dp, ".s",        displayPStack,  FW_DEFAULT);
888    dictAppendWord(dp, "bye",       bye,            FW_DEFAULT);
889    dictAppendWord(dp, "forget",    forget,         FW_DEFAULT);
890    dictAppendWord(dp, "see",       see,            FW_DEFAULT);
891    dictAppendWord(dp, "words",     listWords,      FW_DEFAULT);
892
893    /*
894    ** Set TOOLS environment query values
895    */
896    ficlSetEnv(pSys, "tools",            FICL_TRUE);
897    ficlSetEnv(pSys, "tools-ext",        FICL_FALSE);
898
899    /*
900    ** Ficl extras
901    */
902    dictAppendWord(dp, "r.s",       displayRStack,  FW_DEFAULT); /* guy carver */
903    dictAppendWord(dp, ".env",      listEnv,        FW_DEFAULT);
904    dictAppendWord(dp, "env-constant",
905                                    envConstant,    FW_DEFAULT);
906    dictAppendWord(dp, "env-2constant",
907                                    env2Constant,   FW_DEFAULT);
908    dictAppendWord(dp, "debug-xt",  ficlDebugXT,    FW_DEFAULT);
909    dictAppendWord(dp, "parse-order",
910                                    ficlListParseSteps,
911                                                    FW_DEFAULT);
912    dictAppendWord(dp, "step-break",stepBreak,      FW_DEFAULT);
913    dictAppendWord(dp, "forget-wid",forgetWid,      FW_DEFAULT);
914    dictAppendWord(dp, "see-xt",    seeXT,          FW_DEFAULT);
915
916    return;
917}
918
919