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