xref: /titanic_51/usr/src/common/ficl/tools.c (revision a1bf3f785ae05c419b339c3a2061f2b18c024f61)
1*a1bf3f78SToomas Soome /*
2*a1bf3f78SToomas Soome  * t o o l s . c
3*a1bf3f78SToomas Soome  * Forth Inspired Command Language - programming tools
4*a1bf3f78SToomas Soome  * Author: John Sadler (john_sadler@alum.mit.edu)
5*a1bf3f78SToomas Soome  * Created: 20 June 2000
6*a1bf3f78SToomas Soome  * $Id: tools.c,v 1.12 2010/08/12 13:57:22 asau Exp $
7*a1bf3f78SToomas Soome  */
8*a1bf3f78SToomas Soome /*
9*a1bf3f78SToomas Soome  * Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
10*a1bf3f78SToomas Soome  * All rights reserved.
11*a1bf3f78SToomas Soome  *
12*a1bf3f78SToomas Soome  * Get the latest Ficl release at http://ficl.sourceforge.net
13*a1bf3f78SToomas Soome  *
14*a1bf3f78SToomas Soome  * I am interested in hearing from anyone who uses Ficl. If you have
15*a1bf3f78SToomas Soome  * a problem, a success story, a defect, an enhancement request, or
16*a1bf3f78SToomas Soome  * if you would like to contribute to the Ficl release, please
17*a1bf3f78SToomas Soome  * contact me by email at the address above.
18*a1bf3f78SToomas Soome  *
19*a1bf3f78SToomas Soome  * L I C E N S E  and  D I S C L A I M E R
20*a1bf3f78SToomas Soome  *
21*a1bf3f78SToomas Soome  * Redistribution and use in source and binary forms, with or without
22*a1bf3f78SToomas Soome  * modification, are permitted provided that the following conditions
23*a1bf3f78SToomas Soome  * are met:
24*a1bf3f78SToomas Soome  * 1. Redistributions of source code must retain the above copyright
25*a1bf3f78SToomas Soome  *    notice, this list of conditions and the following disclaimer.
26*a1bf3f78SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
27*a1bf3f78SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
28*a1bf3f78SToomas Soome  *    documentation and/or other materials provided with the distribution.
29*a1bf3f78SToomas Soome  *
30*a1bf3f78SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
31*a1bf3f78SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32*a1bf3f78SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33*a1bf3f78SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
34*a1bf3f78SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35*a1bf3f78SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36*a1bf3f78SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37*a1bf3f78SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38*a1bf3f78SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39*a1bf3f78SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40*a1bf3f78SToomas Soome  * SUCH DAMAGE.
41*a1bf3f78SToomas Soome  */
42*a1bf3f78SToomas Soome 
43*a1bf3f78SToomas Soome /*
44*a1bf3f78SToomas Soome  * NOTES:
45*a1bf3f78SToomas Soome  * SEE needs information about the addresses of functions that
46*a1bf3f78SToomas Soome  * are the CFAs of colon definitions, constants, variables, DOES>
47*a1bf3f78SToomas Soome  * words, and so on. It gets this information from a table and supporting
48*a1bf3f78SToomas Soome  * functions in words.c.
49*a1bf3f78SToomas Soome  * fiColonParen fiDoDoes createParen fiVariableParen fiUserParen fiConstantParen
50*a1bf3f78SToomas Soome  *
51*a1bf3f78SToomas Soome  * Step and break debugger for Ficl
52*a1bf3f78SToomas Soome  * debug  ( xt -- )   Start debugging an xt
53*a1bf3f78SToomas Soome  * Set a breakpoint
54*a1bf3f78SToomas Soome  * Specify breakpoint default action
55*a1bf3f78SToomas Soome  */
56*a1bf3f78SToomas Soome 
57*a1bf3f78SToomas Soome #include "ficl.h"
58*a1bf3f78SToomas Soome 
59*a1bf3f78SToomas Soome extern void exit(int);
60*a1bf3f78SToomas Soome 
61*a1bf3f78SToomas Soome static void ficlPrimitiveStepIn(ficlVm *vm);
62*a1bf3f78SToomas Soome static void ficlPrimitiveStepOver(ficlVm *vm);
63*a1bf3f78SToomas Soome static void ficlPrimitiveStepBreak(ficlVm *vm);
64*a1bf3f78SToomas Soome 
65*a1bf3f78SToomas Soome void
66*a1bf3f78SToomas Soome ficlCallbackAssert(ficlCallback *callback, int expression,
67*a1bf3f78SToomas Soome     char *expressionString, char *filename, int line)
68*a1bf3f78SToomas Soome {
69*a1bf3f78SToomas Soome #if FICL_ROBUST >= 1
70*a1bf3f78SToomas Soome 	if (!expression) {
71*a1bf3f78SToomas Soome 		static char buffer[256];
72*a1bf3f78SToomas Soome 		sprintf(buffer, "ASSERTION FAILED at %s:%d: \"%s\"\n",
73*a1bf3f78SToomas Soome 		    filename, line, expressionString);
74*a1bf3f78SToomas Soome 		ficlCallbackTextOut(callback, buffer);
75*a1bf3f78SToomas Soome 		exit(-1);
76*a1bf3f78SToomas Soome 	}
77*a1bf3f78SToomas Soome #else /* FICL_ROBUST >= 1 */
78*a1bf3f78SToomas Soome 	FICL_IGNORE(callback);
79*a1bf3f78SToomas Soome 	FICL_IGNORE(expression);
80*a1bf3f78SToomas Soome 	FICL_IGNORE(expressionString);
81*a1bf3f78SToomas Soome 	FICL_IGNORE(filename);
82*a1bf3f78SToomas Soome 	FICL_IGNORE(line);
83*a1bf3f78SToomas Soome #endif /* FICL_ROBUST >= 1 */
84*a1bf3f78SToomas Soome }
85*a1bf3f78SToomas Soome 
86*a1bf3f78SToomas Soome /*
87*a1bf3f78SToomas Soome  * v m S e t B r e a k
88*a1bf3f78SToomas Soome  * Set a breakpoint at the current value of IP by
89*a1bf3f78SToomas Soome  * storing that address in a BREAKPOINT record
90*a1bf3f78SToomas Soome  */
91*a1bf3f78SToomas Soome static void
92*a1bf3f78SToomas Soome ficlVmSetBreak(ficlVm *vm, ficlBreakpoint *pBP)
93*a1bf3f78SToomas Soome {
94*a1bf3f78SToomas Soome 	ficlWord *pStep = ficlSystemLookup(vm->callback.system, "step-break");
95*a1bf3f78SToomas Soome 	FICL_VM_ASSERT(vm, pStep);
96*a1bf3f78SToomas Soome 
97*a1bf3f78SToomas Soome 	pBP->address = vm->ip;
98*a1bf3f78SToomas Soome 	pBP->oldXT = *vm->ip;
99*a1bf3f78SToomas Soome 	*vm->ip = pStep;
100*a1bf3f78SToomas Soome }
101*a1bf3f78SToomas Soome 
102*a1bf3f78SToomas Soome /*
103*a1bf3f78SToomas Soome  * d e b u g P r o m p t
104*a1bf3f78SToomas Soome  */
105*a1bf3f78SToomas Soome static void
106*a1bf3f78SToomas Soome ficlDebugPrompt(ficlVm *vm, int debug)
107*a1bf3f78SToomas Soome {
108*a1bf3f78SToomas Soome 	if (debug)
109*a1bf3f78SToomas Soome 		setenv("prompt", "dbg> ", 1);
110*a1bf3f78SToomas Soome 	else
111*a1bf3f78SToomas Soome 		setenv("prompt", "${interpret}", 1);
112*a1bf3f78SToomas Soome }
113*a1bf3f78SToomas Soome 
114*a1bf3f78SToomas Soome #if 0
115*a1bf3f78SToomas Soome static int
116*a1bf3f78SToomas Soome isPrimitive(ficlWord *word)
117*a1bf3f78SToomas Soome {
118*a1bf3f78SToomas Soome 	ficlWordKind wk = ficlWordClassify(word);
119*a1bf3f78SToomas Soome 	return ((wk != COLON) && (wk != DOES));
120*a1bf3f78SToomas Soome }
121*a1bf3f78SToomas Soome #endif
122*a1bf3f78SToomas Soome 
123*a1bf3f78SToomas Soome /*
124*a1bf3f78SToomas Soome  * d i c t H a s h S u m m a r y
125*a1bf3f78SToomas Soome  * Calculate a figure of merit for the dictionary hash table based
126*a1bf3f78SToomas Soome  * on the average search depth for all the words in the dictionary,
127*a1bf3f78SToomas Soome  * assuming uniform distribution of target keys. The figure of merit
128*a1bf3f78SToomas Soome  * is the ratio of the total search depth for all keys in the table
129*a1bf3f78SToomas Soome  * versus a theoretical optimum that would be achieved if the keys
130*a1bf3f78SToomas Soome  * were distributed into the table as evenly as possible.
131*a1bf3f78SToomas Soome  * The figure would be worse if the hash table used an open
132*a1bf3f78SToomas Soome  * addressing scheme (i.e. collisions resolved by searching the
133*a1bf3f78SToomas Soome  * table for an empty slot) for a given size table.
134*a1bf3f78SToomas Soome  */
135*a1bf3f78SToomas Soome #if FICL_WANT_FLOAT
136*a1bf3f78SToomas Soome void
137*a1bf3f78SToomas Soome ficlPrimitiveHashSummary(ficlVm *vm)
138*a1bf3f78SToomas Soome {
139*a1bf3f78SToomas Soome 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
140*a1bf3f78SToomas Soome 	ficlHash *pFHash;
141*a1bf3f78SToomas Soome 	ficlWord **hash;
142*a1bf3f78SToomas Soome 	unsigned size;
143*a1bf3f78SToomas Soome 	ficlWord *word;
144*a1bf3f78SToomas Soome 	unsigned i;
145*a1bf3f78SToomas Soome 	int nMax = 0;
146*a1bf3f78SToomas Soome 	int nWords = 0;
147*a1bf3f78SToomas Soome 	int nFilled;
148*a1bf3f78SToomas Soome 	double avg = 0.0;
149*a1bf3f78SToomas Soome 	double best;
150*a1bf3f78SToomas Soome 	int nAvg, nRem, nDepth;
151*a1bf3f78SToomas Soome 
152*a1bf3f78SToomas Soome 	FICL_VM_DICTIONARY_CHECK(vm, dictionary, 0);
153*a1bf3f78SToomas Soome 
154*a1bf3f78SToomas Soome 	pFHash = dictionary->wordlists[dictionary->wordlistCount - 1];
155*a1bf3f78SToomas Soome 	hash = pFHash->table;
156*a1bf3f78SToomas Soome 	size = pFHash->size;
157*a1bf3f78SToomas Soome 	nFilled = size;
158*a1bf3f78SToomas Soome 
159*a1bf3f78SToomas Soome 	for (i = 0; i < size; i++) {
160*a1bf3f78SToomas Soome 		int n = 0;
161*a1bf3f78SToomas Soome 		word = hash[i];
162*a1bf3f78SToomas Soome 
163*a1bf3f78SToomas Soome 		while (word) {
164*a1bf3f78SToomas Soome 			++n;
165*a1bf3f78SToomas Soome 			++nWords;
166*a1bf3f78SToomas Soome 			word = word->link;
167*a1bf3f78SToomas Soome 		}
168*a1bf3f78SToomas Soome 
169*a1bf3f78SToomas Soome 		avg += (double)(n * (n+1)) / 2.0;
170*a1bf3f78SToomas Soome 
171*a1bf3f78SToomas Soome 		if (n > nMax)
172*a1bf3f78SToomas Soome 			nMax = n;
173*a1bf3f78SToomas Soome 		if (n == 0)
174*a1bf3f78SToomas Soome 			--nFilled;
175*a1bf3f78SToomas Soome 	}
176*a1bf3f78SToomas Soome 
177*a1bf3f78SToomas Soome 	/* Calc actual avg search depth for this hash */
178*a1bf3f78SToomas Soome 	avg = avg / nWords;
179*a1bf3f78SToomas Soome 
180*a1bf3f78SToomas Soome 	/* Calc best possible performance with this size hash */
181*a1bf3f78SToomas Soome 	nAvg = nWords / size;
182*a1bf3f78SToomas Soome 	nRem = nWords % size;
183*a1bf3f78SToomas Soome 	nDepth = size * (nAvg * (nAvg+1))/2 + (nAvg+1)*nRem;
184*a1bf3f78SToomas Soome 	best = (double)nDepth/nWords;
185*a1bf3f78SToomas Soome 
186*a1bf3f78SToomas Soome 	sprintf(vm->pad, "%d bins, %2.0f%% filled, Depth: "
187*a1bf3f78SToomas Soome 	    "Max=%d, Avg=%2.1f, Best=%2.1f, Score: %2.0f%%\n",
188*a1bf3f78SToomas Soome 	    size, (double)nFilled * 100.0 / size, nMax,
189*a1bf3f78SToomas Soome 	    avg, best, 100.0 * best / avg);
190*a1bf3f78SToomas Soome 
191*a1bf3f78SToomas Soome 	ficlVmTextOut(vm, vm->pad);
192*a1bf3f78SToomas Soome }
193*a1bf3f78SToomas Soome #endif
194*a1bf3f78SToomas Soome 
195*a1bf3f78SToomas Soome /*
196*a1bf3f78SToomas Soome  * Here's the outer part of the decompiler. It's
197*a1bf3f78SToomas Soome  * just a big nested conditional that checks the
198*a1bf3f78SToomas Soome  * CFA of the word to decompile for each kind of
199*a1bf3f78SToomas Soome  * known word-builder code, and tries to do
200*a1bf3f78SToomas Soome  * something appropriate. If the CFA is not recognized,
201*a1bf3f78SToomas Soome  * just indicate that it is a primitive.
202*a1bf3f78SToomas Soome  */
203*a1bf3f78SToomas Soome static void
204*a1bf3f78SToomas Soome ficlPrimitiveSeeXT(ficlVm *vm)
205*a1bf3f78SToomas Soome {
206*a1bf3f78SToomas Soome 	ficlWord *word;
207*a1bf3f78SToomas Soome 	ficlWordKind kind;
208*a1bf3f78SToomas Soome 
209*a1bf3f78SToomas Soome 	word = (ficlWord *)ficlStackPopPointer(vm->dataStack);
210*a1bf3f78SToomas Soome 	kind = ficlWordClassify(word);
211*a1bf3f78SToomas Soome 
212*a1bf3f78SToomas Soome 	switch (kind) {
213*a1bf3f78SToomas Soome 	case FICL_WORDKIND_COLON:
214*a1bf3f78SToomas Soome 		sprintf(vm->pad, ": %.*s\n", word->length, word->name);
215*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, vm->pad);
216*a1bf3f78SToomas Soome 		ficlDictionarySee(ficlVmGetDictionary(vm), word,
217*a1bf3f78SToomas Soome 		    &(vm->callback));
218*a1bf3f78SToomas Soome 	break;
219*a1bf3f78SToomas Soome 	case FICL_WORDKIND_DOES:
220*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, "does>\n");
221*a1bf3f78SToomas Soome 		ficlDictionarySee(ficlVmGetDictionary(vm),
222*a1bf3f78SToomas Soome 		    (ficlWord *)word->param->p, &(vm->callback));
223*a1bf3f78SToomas Soome 	break;
224*a1bf3f78SToomas Soome 	case FICL_WORDKIND_CREATE:
225*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, "create\n");
226*a1bf3f78SToomas Soome 	break;
227*a1bf3f78SToomas Soome 	case FICL_WORDKIND_VARIABLE:
228*a1bf3f78SToomas Soome 		sprintf(vm->pad, "variable = %ld (%#lx)\n",
229*a1bf3f78SToomas Soome 		    (long)word->param->i, (long unsigned)word->param->u);
230*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, vm->pad);
231*a1bf3f78SToomas Soome 	break;
232*a1bf3f78SToomas Soome #if FICL_WANT_USER
233*a1bf3f78SToomas Soome 	case FICL_WORDKIND_USER:
234*a1bf3f78SToomas Soome 		sprintf(vm->pad, "user variable %ld (%#lx)\n",
235*a1bf3f78SToomas Soome 		    (long)word->param->i, (long unsigned)word->param->u);
236*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, vm->pad);
237*a1bf3f78SToomas Soome 	break;
238*a1bf3f78SToomas Soome #endif
239*a1bf3f78SToomas Soome 	case FICL_WORDKIND_CONSTANT:
240*a1bf3f78SToomas Soome 		sprintf(vm->pad, "constant = %ld (%#lx)\n",
241*a1bf3f78SToomas Soome 		    (long)word->param->i, (long unsigned)word->param->u);
242*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, vm->pad);
243*a1bf3f78SToomas Soome 	break;
244*a1bf3f78SToomas Soome 	case FICL_WORDKIND_2CONSTANT:
245*a1bf3f78SToomas Soome 		sprintf(vm->pad, "constant = %ld %ld (%#lx %#lx)\n",
246*a1bf3f78SToomas Soome 		    (long)word->param[1].i, (long)word->param->i,
247*a1bf3f78SToomas Soome 		    (long unsigned)word->param[1].u,
248*a1bf3f78SToomas Soome 		    (long unsigned)word->param->u);
249*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, vm->pad);
250*a1bf3f78SToomas Soome 	break;
251*a1bf3f78SToomas Soome 
252*a1bf3f78SToomas Soome 	default:
253*a1bf3f78SToomas Soome 		sprintf(vm->pad, "%.*s is a primitive\n", word->length,
254*a1bf3f78SToomas Soome 		    word->name);
255*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, vm->pad);
256*a1bf3f78SToomas Soome 	break;
257*a1bf3f78SToomas Soome 	}
258*a1bf3f78SToomas Soome 
259*a1bf3f78SToomas Soome 	if (word->flags & FICL_WORD_IMMEDIATE) {
260*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, "immediate\n");
261*a1bf3f78SToomas Soome 	}
262*a1bf3f78SToomas Soome 
263*a1bf3f78SToomas Soome 	if (word->flags & FICL_WORD_COMPILE_ONLY) {
264*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, "compile-only\n");
265*a1bf3f78SToomas Soome 	}
266*a1bf3f78SToomas Soome }
267*a1bf3f78SToomas Soome 
268*a1bf3f78SToomas Soome static void
269*a1bf3f78SToomas Soome ficlPrimitiveSee(ficlVm *vm)
270*a1bf3f78SToomas Soome {
271*a1bf3f78SToomas Soome 	ficlPrimitiveTick(vm);
272*a1bf3f78SToomas Soome 	ficlPrimitiveSeeXT(vm);
273*a1bf3f78SToomas Soome }
274*a1bf3f78SToomas Soome 
275*a1bf3f78SToomas Soome /*
276*a1bf3f78SToomas Soome  * f i c l D e b u g X T
277*a1bf3f78SToomas Soome  * debug  ( xt -- )
278*a1bf3f78SToomas Soome  * Given an xt of a colon definition or a word defined by DOES>, set the
279*a1bf3f78SToomas Soome  * VM up to debug the word: push IP, set the xt as the next thing to execute,
280*a1bf3f78SToomas Soome  * set a breakpoint at its first instruction, and run to the breakpoint.
281*a1bf3f78SToomas Soome  * Note: the semantics of this word are equivalent to "step in"
282*a1bf3f78SToomas Soome  */
283*a1bf3f78SToomas Soome static void
284*a1bf3f78SToomas Soome ficlPrimitiveDebugXT(ficlVm *vm)
285*a1bf3f78SToomas Soome {
286*a1bf3f78SToomas Soome 	ficlWord *xt = ficlStackPopPointer(vm->dataStack);
287*a1bf3f78SToomas Soome 	ficlWordKind wk = ficlWordClassify(xt);
288*a1bf3f78SToomas Soome 
289*a1bf3f78SToomas Soome 	ficlStackPushPointer(vm->dataStack, xt);
290*a1bf3f78SToomas Soome 	ficlPrimitiveSeeXT(vm);
291*a1bf3f78SToomas Soome 
292*a1bf3f78SToomas Soome 	switch (wk) {
293*a1bf3f78SToomas Soome 	case FICL_WORDKIND_COLON:
294*a1bf3f78SToomas Soome 	case FICL_WORDKIND_DOES:
295*a1bf3f78SToomas Soome 		/*
296*a1bf3f78SToomas Soome 		 * Run the colon code and set a breakpoint at the next
297*a1bf3f78SToomas Soome 		 * instruction
298*a1bf3f78SToomas Soome 		 */
299*a1bf3f78SToomas Soome 		ficlVmExecuteWord(vm, xt);
300*a1bf3f78SToomas Soome 		ficlVmSetBreak(vm, &(vm->callback.system->breakpoint));
301*a1bf3f78SToomas Soome 	break;
302*a1bf3f78SToomas Soome 	default:
303*a1bf3f78SToomas Soome 		ficlVmExecuteWord(vm, xt);
304*a1bf3f78SToomas Soome 	break;
305*a1bf3f78SToomas Soome 	}
306*a1bf3f78SToomas Soome }
307*a1bf3f78SToomas Soome 
308*a1bf3f78SToomas Soome /*
309*a1bf3f78SToomas Soome  * s t e p I n
310*a1bf3f78SToomas Soome  * Ficl
311*a1bf3f78SToomas Soome  * Execute the next instruction, stepping into it if it's a colon definition
312*a1bf3f78SToomas Soome  * or a does> word. This is the easy kind of step.
313*a1bf3f78SToomas Soome  */
314*a1bf3f78SToomas Soome static void
315*a1bf3f78SToomas Soome ficlPrimitiveStepIn(ficlVm *vm)
316*a1bf3f78SToomas Soome {
317*a1bf3f78SToomas Soome 	/*
318*a1bf3f78SToomas Soome 	 * Do one step of the inner loop
319*a1bf3f78SToomas Soome 	 */
320*a1bf3f78SToomas Soome 	ficlVmExecuteWord(vm, *vm->ip++);
321*a1bf3f78SToomas Soome 
322*a1bf3f78SToomas Soome 	/*
323*a1bf3f78SToomas Soome 	 * Now set a breakpoint at the next instruction
324*a1bf3f78SToomas Soome 	 */
325*a1bf3f78SToomas Soome 	ficlVmSetBreak(vm, &(vm->callback.system->breakpoint));
326*a1bf3f78SToomas Soome }
327*a1bf3f78SToomas Soome 
328*a1bf3f78SToomas Soome /*
329*a1bf3f78SToomas Soome  * s t e p O v e r
330*a1bf3f78SToomas Soome  * Ficl
331*a1bf3f78SToomas Soome  * Execute the next instruction atomically. This requires some insight into
332*a1bf3f78SToomas Soome  * the memory layout of compiled code. Set a breakpoint at the next instruction
333*a1bf3f78SToomas Soome  * in this word, and run until we hit it
334*a1bf3f78SToomas Soome  */
335*a1bf3f78SToomas Soome static void
336*a1bf3f78SToomas Soome ficlPrimitiveStepOver(ficlVm *vm)
337*a1bf3f78SToomas Soome {
338*a1bf3f78SToomas Soome 	ficlWord *word;
339*a1bf3f78SToomas Soome 	ficlWordKind kind;
340*a1bf3f78SToomas Soome 	ficlWord *pStep = ficlSystemLookup(vm->callback.system, "step-break");
341*a1bf3f78SToomas Soome 	FICL_VM_ASSERT(vm, pStep);
342*a1bf3f78SToomas Soome 
343*a1bf3f78SToomas Soome 	word = *vm->ip;
344*a1bf3f78SToomas Soome 	kind = ficlWordClassify(word);
345*a1bf3f78SToomas Soome 
346*a1bf3f78SToomas Soome 	switch (kind) {
347*a1bf3f78SToomas Soome 	case FICL_WORDKIND_COLON:
348*a1bf3f78SToomas Soome 	case FICL_WORDKIND_DOES:
349*a1bf3f78SToomas Soome 		/*
350*a1bf3f78SToomas Soome 		 * assume that the next ficlCell holds an instruction
351*a1bf3f78SToomas Soome 		 * set a breakpoint there and return to the inner interpreter
352*a1bf3f78SToomas Soome 		 */
353*a1bf3f78SToomas Soome 		vm->callback.system->breakpoint.address = vm->ip + 1;
354*a1bf3f78SToomas Soome 		vm->callback.system->breakpoint.oldXT =  vm->ip[1];
355*a1bf3f78SToomas Soome 		vm->ip[1] = pStep;
356*a1bf3f78SToomas Soome 	break;
357*a1bf3f78SToomas Soome 	default:
358*a1bf3f78SToomas Soome 		ficlPrimitiveStepIn(vm);
359*a1bf3f78SToomas Soome 	break;
360*a1bf3f78SToomas Soome 	}
361*a1bf3f78SToomas Soome }
362*a1bf3f78SToomas Soome 
363*a1bf3f78SToomas Soome /*
364*a1bf3f78SToomas Soome  * s t e p - b r e a k
365*a1bf3f78SToomas Soome  * Ficl
366*a1bf3f78SToomas Soome  * Handles breakpoints for stepped execution.
367*a1bf3f78SToomas Soome  * Upon entry, breakpoint contains the address and replaced instruction
368*a1bf3f78SToomas Soome  * of the current breakpoint.
369*a1bf3f78SToomas Soome  * Clear the breakpoint
370*a1bf3f78SToomas Soome  * Get a command from the console.
371*a1bf3f78SToomas Soome  * i (step in) - execute the current instruction and set a new breakpoint
372*a1bf3f78SToomas Soome  *    at the IP
373*a1bf3f78SToomas Soome  * o (step over) - execute the current instruction to completion and set
374*a1bf3f78SToomas Soome  *    a new breakpoint at the IP
375*a1bf3f78SToomas Soome  * g (go) - execute the current instruction and exit
376*a1bf3f78SToomas Soome  * q (quit) - abort current word
377*a1bf3f78SToomas Soome  * b (toggle breakpoint)
378*a1bf3f78SToomas Soome  */
379*a1bf3f78SToomas Soome 
380*a1bf3f78SToomas Soome extern char *ficlDictionaryInstructionNames[];
381*a1bf3f78SToomas Soome 
382*a1bf3f78SToomas Soome static void
383*a1bf3f78SToomas Soome ficlPrimitiveStepBreak(ficlVm *vm)
384*a1bf3f78SToomas Soome {
385*a1bf3f78SToomas Soome 	ficlString command;
386*a1bf3f78SToomas Soome 	ficlWord *word;
387*a1bf3f78SToomas Soome 	ficlWord *pOnStep;
388*a1bf3f78SToomas Soome 	int debug = 1;
389*a1bf3f78SToomas Soome 
390*a1bf3f78SToomas Soome 	if (!vm->restart) {
391*a1bf3f78SToomas Soome 		FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.address);
392*a1bf3f78SToomas Soome 		FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.oldXT);
393*a1bf3f78SToomas Soome 
394*a1bf3f78SToomas Soome 		/*
395*a1bf3f78SToomas Soome 		 * Clear the breakpoint that caused me to run
396*a1bf3f78SToomas Soome 		 * Restore the original instruction at the breakpoint,
397*a1bf3f78SToomas Soome 		 * and restore the IP
398*a1bf3f78SToomas Soome 		 */
399*a1bf3f78SToomas Soome 		vm->ip = (ficlIp)(vm->callback.system->breakpoint.address);
400*a1bf3f78SToomas Soome 		*vm->ip = vm->callback.system->breakpoint.oldXT;
401*a1bf3f78SToomas Soome 
402*a1bf3f78SToomas Soome 		/*
403*a1bf3f78SToomas Soome 		 * If there's an onStep, do it
404*a1bf3f78SToomas Soome 		 */
405*a1bf3f78SToomas Soome 		pOnStep = ficlSystemLookup(vm->callback.system, "on-step");
406*a1bf3f78SToomas Soome 		if (pOnStep)
407*a1bf3f78SToomas Soome 			ficlVmExecuteXT(vm, pOnStep);
408*a1bf3f78SToomas Soome 
409*a1bf3f78SToomas Soome 		/*
410*a1bf3f78SToomas Soome 		 * Print the name of the next instruction
411*a1bf3f78SToomas Soome 		 */
412*a1bf3f78SToomas Soome 		word = vm->callback.system->breakpoint.oldXT;
413*a1bf3f78SToomas Soome 
414*a1bf3f78SToomas Soome 		if ((((ficlInstruction)word) > ficlInstructionInvalid) &&
415*a1bf3f78SToomas Soome 		    (((ficlInstruction)word) < ficlInstructionLast))
416*a1bf3f78SToomas Soome 			sprintf(vm->pad, "next: %s (instruction %ld)\n",
417*a1bf3f78SToomas Soome 			    ficlDictionaryInstructionNames[(long)word],
418*a1bf3f78SToomas Soome 			    (long)word);
419*a1bf3f78SToomas Soome 		else {
420*a1bf3f78SToomas Soome 			sprintf(vm->pad, "next: %s\n", word->name);
421*a1bf3f78SToomas Soome 			if (strcmp(word->name, "interpret") == 0)
422*a1bf3f78SToomas Soome 				debug = 0;
423*a1bf3f78SToomas Soome 		}
424*a1bf3f78SToomas Soome 
425*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, vm->pad);
426*a1bf3f78SToomas Soome 		ficlDebugPrompt(vm, debug);
427*a1bf3f78SToomas Soome 	} else {
428*a1bf3f78SToomas Soome 		vm->restart = 0;
429*a1bf3f78SToomas Soome 	}
430*a1bf3f78SToomas Soome 
431*a1bf3f78SToomas Soome 	command = ficlVmGetWord(vm);
432*a1bf3f78SToomas Soome 
433*a1bf3f78SToomas Soome 	switch (command.text[0]) {
434*a1bf3f78SToomas Soome 		case 'i':
435*a1bf3f78SToomas Soome 			ficlPrimitiveStepIn(vm);
436*a1bf3f78SToomas Soome 		break;
437*a1bf3f78SToomas Soome 
438*a1bf3f78SToomas Soome 		case 'o':
439*a1bf3f78SToomas Soome 			ficlPrimitiveStepOver(vm);
440*a1bf3f78SToomas Soome 		break;
441*a1bf3f78SToomas Soome 
442*a1bf3f78SToomas Soome 		case 'g':
443*a1bf3f78SToomas Soome 		break;
444*a1bf3f78SToomas Soome 
445*a1bf3f78SToomas Soome 		case 'l': {
446*a1bf3f78SToomas Soome 			ficlWord *xt;
447*a1bf3f78SToomas Soome 			xt = ficlDictionaryFindEnclosingWord(
448*a1bf3f78SToomas Soome 			    ficlVmGetDictionary(vm), (ficlCell *)(vm->ip));
449*a1bf3f78SToomas Soome 			if (xt) {
450*a1bf3f78SToomas Soome 				ficlStackPushPointer(vm->dataStack, xt);
451*a1bf3f78SToomas Soome 				ficlPrimitiveSeeXT(vm);
452*a1bf3f78SToomas Soome 			} else {
453*a1bf3f78SToomas Soome 				ficlVmTextOut(vm, "sorry - can't do that\n");
454*a1bf3f78SToomas Soome 			}
455*a1bf3f78SToomas Soome 			ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
456*a1bf3f78SToomas Soome 		break;
457*a1bf3f78SToomas Soome 		}
458*a1bf3f78SToomas Soome 
459*a1bf3f78SToomas Soome 		case 'q':
460*a1bf3f78SToomas Soome 			ficlDebugPrompt(vm, 0);
461*a1bf3f78SToomas Soome 			ficlVmThrow(vm, FICL_VM_STATUS_ABORT);
462*a1bf3f78SToomas Soome 			break;
463*a1bf3f78SToomas Soome 		case 'x': {
464*a1bf3f78SToomas Soome 			/*
465*a1bf3f78SToomas Soome 			 * Take whatever's left in the TIB and feed it to a
466*a1bf3f78SToomas Soome 			 * subordinate ficlVmExecuteString
467*a1bf3f78SToomas Soome 			 */
468*a1bf3f78SToomas Soome 			int returnValue;
469*a1bf3f78SToomas Soome 			ficlString s;
470*a1bf3f78SToomas Soome 			ficlWord *oldRunningWord = vm->runningWord;
471*a1bf3f78SToomas Soome 
472*a1bf3f78SToomas Soome 			FICL_STRING_SET_POINTER(s,
473*a1bf3f78SToomas Soome 			    vm->tib.text + vm->tib.index);
474*a1bf3f78SToomas Soome 			FICL_STRING_SET_LENGTH(s,
475*a1bf3f78SToomas Soome 			    vm->tib.end - FICL_STRING_GET_POINTER(s));
476*a1bf3f78SToomas Soome 
477*a1bf3f78SToomas Soome 			returnValue = ficlVmExecuteString(vm, s);
478*a1bf3f78SToomas Soome 
479*a1bf3f78SToomas Soome 			if (returnValue == FICL_VM_STATUS_OUT_OF_TEXT) {
480*a1bf3f78SToomas Soome 				returnValue = FICL_VM_STATUS_RESTART;
481*a1bf3f78SToomas Soome 				vm->runningWord = oldRunningWord;
482*a1bf3f78SToomas Soome 				ficlVmTextOut(vm, "\n");
483*a1bf3f78SToomas Soome 			}
484*a1bf3f78SToomas Soome 			if (returnValue == FICL_VM_STATUS_ERROR_EXIT)
485*a1bf3f78SToomas Soome 				ficlDebugPrompt(vm, 0);
486*a1bf3f78SToomas Soome 
487*a1bf3f78SToomas Soome 			ficlVmThrow(vm, returnValue);
488*a1bf3f78SToomas Soome 			break;
489*a1bf3f78SToomas Soome 		}
490*a1bf3f78SToomas Soome 
491*a1bf3f78SToomas Soome 		default:
492*a1bf3f78SToomas Soome 			ficlVmTextOut(vm,
493*a1bf3f78SToomas Soome 			    "i -- step In\n"
494*a1bf3f78SToomas Soome 			    "o -- step Over\n"
495*a1bf3f78SToomas Soome 			    "g -- Go (execute to completion)\n"
496*a1bf3f78SToomas Soome 			    "l -- List source code\n"
497*a1bf3f78SToomas Soome 			    "q -- Quit (stop debugging and abort)\n"
498*a1bf3f78SToomas Soome 			    "x -- eXecute the rest of the line "
499*a1bf3f78SToomas Soome 			    "as Ficl words\n");
500*a1bf3f78SToomas Soome 			ficlDebugPrompt(vm, 1);
501*a1bf3f78SToomas Soome 			ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
502*a1bf3f78SToomas Soome 		break;
503*a1bf3f78SToomas Soome 	}
504*a1bf3f78SToomas Soome 
505*a1bf3f78SToomas Soome 	ficlDebugPrompt(vm, 0);
506*a1bf3f78SToomas Soome }
507*a1bf3f78SToomas Soome 
508*a1bf3f78SToomas Soome /*
509*a1bf3f78SToomas Soome  * b y e
510*a1bf3f78SToomas Soome  * TOOLS
511*a1bf3f78SToomas Soome  * Signal the system to shut down - this causes ficlExec to return
512*a1bf3f78SToomas Soome  * VM_USEREXIT. The rest is up to you.
513*a1bf3f78SToomas Soome  */
514*a1bf3f78SToomas Soome static void
515*a1bf3f78SToomas Soome ficlPrimitiveBye(ficlVm *vm)
516*a1bf3f78SToomas Soome {
517*a1bf3f78SToomas Soome 	ficlVmThrow(vm, FICL_VM_STATUS_USER_EXIT);
518*a1bf3f78SToomas Soome }
519*a1bf3f78SToomas Soome 
520*a1bf3f78SToomas Soome /*
521*a1bf3f78SToomas Soome  * d i s p l a y S t a c k
522*a1bf3f78SToomas Soome  * TOOLS
523*a1bf3f78SToomas Soome  * Display the parameter stack (code for ".s")
524*a1bf3f78SToomas Soome  */
525*a1bf3f78SToomas Soome 
526*a1bf3f78SToomas Soome struct stackContext
527*a1bf3f78SToomas Soome {
528*a1bf3f78SToomas Soome 	ficlVm *vm;
529*a1bf3f78SToomas Soome 	ficlDictionary *dictionary;
530*a1bf3f78SToomas Soome 	int count;
531*a1bf3f78SToomas Soome };
532*a1bf3f78SToomas Soome 
533*a1bf3f78SToomas Soome static ficlInteger
534*a1bf3f78SToomas Soome ficlStackDisplayCallback(void *c, ficlCell *cell)
535*a1bf3f78SToomas Soome {
536*a1bf3f78SToomas Soome 	struct stackContext *context = (struct stackContext *)c;
537*a1bf3f78SToomas Soome 	char buffer[80];
538*a1bf3f78SToomas Soome 
539*a1bf3f78SToomas Soome #ifdef _LP64
540*a1bf3f78SToomas Soome 	snprintf(buffer, sizeof (buffer), "[0x%016lx %3d]: %20ld (0x%016lx)\n",
541*a1bf3f78SToomas Soome 	    (unsigned long)cell, context->count++, (long)cell->i,
542*a1bf3f78SToomas Soome 	    (unsigned long)cell->u);
543*a1bf3f78SToomas Soome #else
544*a1bf3f78SToomas Soome 	snprintf(buffer, sizeof (buffer), "[0x%08x %3d]: %12d (0x%08x)\n",
545*a1bf3f78SToomas Soome 	    (unsigned)cell, context->count++, cell->i, cell->u);
546*a1bf3f78SToomas Soome #endif
547*a1bf3f78SToomas Soome 
548*a1bf3f78SToomas Soome 	ficlVmTextOut(context->vm, buffer);
549*a1bf3f78SToomas Soome 	return (FICL_TRUE);
550*a1bf3f78SToomas Soome }
551*a1bf3f78SToomas Soome 
552*a1bf3f78SToomas Soome void
553*a1bf3f78SToomas Soome ficlStackDisplay(ficlStack *stack, ficlStackWalkFunction callback,
554*a1bf3f78SToomas Soome     void *context)
555*a1bf3f78SToomas Soome {
556*a1bf3f78SToomas Soome 	ficlVm *vm = stack->vm;
557*a1bf3f78SToomas Soome 	char buffer[128];
558*a1bf3f78SToomas Soome 	struct stackContext myContext;
559*a1bf3f78SToomas Soome 
560*a1bf3f78SToomas Soome 	FICL_STACK_CHECK(stack, 0, 0);
561*a1bf3f78SToomas Soome 
562*a1bf3f78SToomas Soome #ifdef _LP64
563*a1bf3f78SToomas Soome 	sprintf(buffer, "[%s stack has %d entries, top at 0x%016lx]\n",
564*a1bf3f78SToomas Soome 	    stack->name, ficlStackDepth(stack), (unsigned long)stack->top);
565*a1bf3f78SToomas Soome #else
566*a1bf3f78SToomas Soome 	sprintf(buffer, "[%s stack has %d entries, top at 0x%08x]\n",
567*a1bf3f78SToomas Soome 	    stack->name, ficlStackDepth(stack), (unsigned)stack->top);
568*a1bf3f78SToomas Soome #endif
569*a1bf3f78SToomas Soome 	ficlVmTextOut(vm, buffer);
570*a1bf3f78SToomas Soome 
571*a1bf3f78SToomas Soome 	if (callback == NULL) {
572*a1bf3f78SToomas Soome 		myContext.vm = vm;
573*a1bf3f78SToomas Soome 		myContext.count = 0;
574*a1bf3f78SToomas Soome 		context = &myContext;
575*a1bf3f78SToomas Soome 		callback = ficlStackDisplayCallback;
576*a1bf3f78SToomas Soome 	}
577*a1bf3f78SToomas Soome 	ficlStackWalk(stack, callback, context, FICL_FALSE);
578*a1bf3f78SToomas Soome 
579*a1bf3f78SToomas Soome #ifdef _LP64
580*a1bf3f78SToomas Soome 	sprintf(buffer, "[%s stack base at 0x%016lx]\n", stack->name,
581*a1bf3f78SToomas Soome 	    (unsigned long)stack->base);
582*a1bf3f78SToomas Soome #else
583*a1bf3f78SToomas Soome 	sprintf(buffer, "[%s stack base at 0x%08x]\n", stack->name,
584*a1bf3f78SToomas Soome 	    (unsigned)stack->base);
585*a1bf3f78SToomas Soome #endif
586*a1bf3f78SToomas Soome 	ficlVmTextOut(vm, buffer);
587*a1bf3f78SToomas Soome }
588*a1bf3f78SToomas Soome 
589*a1bf3f78SToomas Soome void
590*a1bf3f78SToomas Soome ficlVmDisplayDataStack(ficlVm *vm)
591*a1bf3f78SToomas Soome {
592*a1bf3f78SToomas Soome 	ficlStackDisplay(vm->dataStack, NULL, NULL);
593*a1bf3f78SToomas Soome }
594*a1bf3f78SToomas Soome 
595*a1bf3f78SToomas Soome static ficlInteger
596*a1bf3f78SToomas Soome ficlStackDisplaySimpleCallback(void *c, ficlCell *cell)
597*a1bf3f78SToomas Soome {
598*a1bf3f78SToomas Soome 	struct stackContext *context = (struct stackContext *)c;
599*a1bf3f78SToomas Soome 	char buffer[32];
600*a1bf3f78SToomas Soome 
601*a1bf3f78SToomas Soome 	sprintf(buffer, "%s%ld", context->count ? " " : "", (long)cell->i);
602*a1bf3f78SToomas Soome 	context->count++;
603*a1bf3f78SToomas Soome 	ficlVmTextOut(context->vm, buffer);
604*a1bf3f78SToomas Soome 	return (FICL_TRUE);
605*a1bf3f78SToomas Soome }
606*a1bf3f78SToomas Soome 
607*a1bf3f78SToomas Soome void
608*a1bf3f78SToomas Soome ficlVmDisplayDataStackSimple(ficlVm *vm)
609*a1bf3f78SToomas Soome {
610*a1bf3f78SToomas Soome 	ficlStack *stack = vm->dataStack;
611*a1bf3f78SToomas Soome 	char buffer[32];
612*a1bf3f78SToomas Soome 	struct stackContext context;
613*a1bf3f78SToomas Soome 
614*a1bf3f78SToomas Soome 	FICL_STACK_CHECK(stack, 0, 0);
615*a1bf3f78SToomas Soome 
616*a1bf3f78SToomas Soome 	sprintf(buffer, "[%d] ", ficlStackDepth(stack));
617*a1bf3f78SToomas Soome 	ficlVmTextOut(vm, buffer);
618*a1bf3f78SToomas Soome 
619*a1bf3f78SToomas Soome 	context.vm = vm;
620*a1bf3f78SToomas Soome 	context.count = 0;
621*a1bf3f78SToomas Soome 	ficlStackWalk(stack, ficlStackDisplaySimpleCallback, &context,
622*a1bf3f78SToomas Soome 	    FICL_TRUE);
623*a1bf3f78SToomas Soome }
624*a1bf3f78SToomas Soome 
625*a1bf3f78SToomas Soome static ficlInteger
626*a1bf3f78SToomas Soome ficlReturnStackDisplayCallback(void *c, ficlCell *cell)
627*a1bf3f78SToomas Soome {
628*a1bf3f78SToomas Soome 	struct stackContext *context = (struct stackContext *)c;
629*a1bf3f78SToomas Soome 	char buffer[128];
630*a1bf3f78SToomas Soome 
631*a1bf3f78SToomas Soome #ifdef _LP64
632*a1bf3f78SToomas Soome 	sprintf(buffer, "[0x%016lx %3d] %20ld (0x%016lx)", (unsigned long)cell,
633*a1bf3f78SToomas Soome 	    context->count++, cell->i, cell->u);
634*a1bf3f78SToomas Soome #else
635*a1bf3f78SToomas Soome 	sprintf(buffer, "[0x%08x %3d] %12d (0x%08x)", (unsigned)cell,
636*a1bf3f78SToomas Soome 	    context->count++, cell->i, cell->u);
637*a1bf3f78SToomas Soome #endif
638*a1bf3f78SToomas Soome 
639*a1bf3f78SToomas Soome 	/*
640*a1bf3f78SToomas Soome 	 * Attempt to find the word that contains the return
641*a1bf3f78SToomas Soome 	 * stack address (as if it is part of a colon definition).
642*a1bf3f78SToomas Soome 	 * If this works, also print the name of the word.
643*a1bf3f78SToomas Soome 	 */
644*a1bf3f78SToomas Soome 	if (ficlDictionaryIncludes(context->dictionary, cell->p)) {
645*a1bf3f78SToomas Soome 		ficlWord *word;
646*a1bf3f78SToomas Soome 		word = ficlDictionaryFindEnclosingWord(context->dictionary,
647*a1bf3f78SToomas Soome 		    cell->p);
648*a1bf3f78SToomas Soome 		if (word) {
649*a1bf3f78SToomas Soome 			int offset = (ficlCell *)cell->p - &word->param[0];
650*a1bf3f78SToomas Soome 			sprintf(buffer + strlen(buffer), ", %s + %d ",
651*a1bf3f78SToomas Soome 			    word->name, offset);
652*a1bf3f78SToomas Soome 		}
653*a1bf3f78SToomas Soome 	}
654*a1bf3f78SToomas Soome 	strcat(buffer, "\n");
655*a1bf3f78SToomas Soome 	ficlVmTextOut(context->vm, buffer);
656*a1bf3f78SToomas Soome 	return (FICL_TRUE);
657*a1bf3f78SToomas Soome }
658*a1bf3f78SToomas Soome 
659*a1bf3f78SToomas Soome void
660*a1bf3f78SToomas Soome ficlVmDisplayReturnStack(ficlVm *vm)
661*a1bf3f78SToomas Soome {
662*a1bf3f78SToomas Soome 	struct stackContext context;
663*a1bf3f78SToomas Soome 	context.vm = vm;
664*a1bf3f78SToomas Soome 	context.count = 0;
665*a1bf3f78SToomas Soome 	context.dictionary = ficlVmGetDictionary(vm);
666*a1bf3f78SToomas Soome 	ficlStackDisplay(vm->returnStack, ficlReturnStackDisplayCallback,
667*a1bf3f78SToomas Soome 	    &context);
668*a1bf3f78SToomas Soome }
669*a1bf3f78SToomas Soome 
670*a1bf3f78SToomas Soome /*
671*a1bf3f78SToomas Soome  * f o r g e t - w i d
672*a1bf3f78SToomas Soome  */
673*a1bf3f78SToomas Soome static void
674*a1bf3f78SToomas Soome ficlPrimitiveForgetWid(ficlVm *vm)
675*a1bf3f78SToomas Soome {
676*a1bf3f78SToomas Soome 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
677*a1bf3f78SToomas Soome 	ficlHash *hash;
678*a1bf3f78SToomas Soome 
679*a1bf3f78SToomas Soome 	hash = (ficlHash *)ficlStackPopPointer(vm->dataStack);
680*a1bf3f78SToomas Soome 	ficlHashForget(hash, dictionary->here);
681*a1bf3f78SToomas Soome }
682*a1bf3f78SToomas Soome 
683*a1bf3f78SToomas Soome /*
684*a1bf3f78SToomas Soome  * f o r g e t
685*a1bf3f78SToomas Soome  * TOOLS EXT  ( "<spaces>name" -- )
686*a1bf3f78SToomas Soome  * Skip leading space delimiters. Parse name delimited by a space.
687*a1bf3f78SToomas Soome  * Find name, then delete name from the dictionary along with all
688*a1bf3f78SToomas Soome  * words added to the dictionary after name. An ambiguous
689*a1bf3f78SToomas Soome  * condition exists if name cannot be found.
690*a1bf3f78SToomas Soome  *
691*a1bf3f78SToomas Soome  * If the Search-Order word set is present, FORGET searches the
692*a1bf3f78SToomas Soome  * compilation word list. An ambiguous condition exists if the
693*a1bf3f78SToomas Soome  * compilation word list is deleted.
694*a1bf3f78SToomas Soome  */
695*a1bf3f78SToomas Soome static void
696*a1bf3f78SToomas Soome ficlPrimitiveForget(ficlVm *vm)
697*a1bf3f78SToomas Soome {
698*a1bf3f78SToomas Soome 	void *where;
699*a1bf3f78SToomas Soome 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
700*a1bf3f78SToomas Soome 	ficlHash *hash = dictionary->compilationWordlist;
701*a1bf3f78SToomas Soome 
702*a1bf3f78SToomas Soome 	ficlPrimitiveTick(vm);
703*a1bf3f78SToomas Soome 	where = ((ficlWord *)ficlStackPopPointer(vm->dataStack))->name;
704*a1bf3f78SToomas Soome 	ficlHashForget(hash, where);
705*a1bf3f78SToomas Soome 	dictionary->here = FICL_POINTER_TO_CELL(where);
706*a1bf3f78SToomas Soome }
707*a1bf3f78SToomas Soome 
708*a1bf3f78SToomas Soome /*
709*a1bf3f78SToomas Soome  * w o r d s
710*a1bf3f78SToomas Soome  */
711*a1bf3f78SToomas Soome #define	nCOLWIDTH	8
712*a1bf3f78SToomas Soome 
713*a1bf3f78SToomas Soome static void
714*a1bf3f78SToomas Soome ficlPrimitiveWords(ficlVm *vm)
715*a1bf3f78SToomas Soome {
716*a1bf3f78SToomas Soome 	ficlDictionary *dictionary = ficlVmGetDictionary(vm);
717*a1bf3f78SToomas Soome 	ficlHash *hash = dictionary->wordlists[dictionary->wordlistCount - 1];
718*a1bf3f78SToomas Soome 	ficlWord *wp;
719*a1bf3f78SToomas Soome 	int nChars = 0;
720*a1bf3f78SToomas Soome 	int len;
721*a1bf3f78SToomas Soome 	unsigned i;
722*a1bf3f78SToomas Soome 	int nWords = 0;
723*a1bf3f78SToomas Soome 	char *cp;
724*a1bf3f78SToomas Soome 	char *pPad;
725*a1bf3f78SToomas Soome 	int columns;
726*a1bf3f78SToomas Soome 
727*a1bf3f78SToomas Soome 	cp = getenv("COLUMNS");
728*a1bf3f78SToomas Soome 	/*
729*a1bf3f78SToomas Soome 	 * using strtol for now. TODO: refactor number conversion from
730*a1bf3f78SToomas Soome 	 * ficlPrimitiveToNumber() and use it instead.
731*a1bf3f78SToomas Soome 	 */
732*a1bf3f78SToomas Soome 	if (cp == NULL)
733*a1bf3f78SToomas Soome 		columns = 80;
734*a1bf3f78SToomas Soome 	else
735*a1bf3f78SToomas Soome 		columns = strtol(cp, NULL, 0);
736*a1bf3f78SToomas Soome 
737*a1bf3f78SToomas Soome 	/*
738*a1bf3f78SToomas Soome 	 * the pad is fixed size area, it's better to allocate
739*a1bf3f78SToomas Soome 	 * dedicated buffer space to deal with custom terminal sizes.
740*a1bf3f78SToomas Soome 	 */
741*a1bf3f78SToomas Soome 	pPad = malloc(columns + 1);
742*a1bf3f78SToomas Soome 	if (pPad == NULL)
743*a1bf3f78SToomas Soome 		ficlVmThrowError(vm, "Error: out of memory");
744*a1bf3f78SToomas Soome 
745*a1bf3f78SToomas Soome 	pager_open();
746*a1bf3f78SToomas Soome 	for (i = 0; i < hash->size; i++) {
747*a1bf3f78SToomas Soome 		for (wp = hash->table[i]; wp != NULL; wp = wp->link, nWords++) {
748*a1bf3f78SToomas Soome 			if (wp->length == 0) /* ignore :noname defs */
749*a1bf3f78SToomas Soome 				continue;
750*a1bf3f78SToomas Soome 
751*a1bf3f78SToomas Soome 			/* prevent line wrap due to long words */
752*a1bf3f78SToomas Soome 			if (nChars + wp->length >= columns) {
753*a1bf3f78SToomas Soome 				pPad[nChars++] = '\n';
754*a1bf3f78SToomas Soome 				pPad[nChars] = '\0';
755*a1bf3f78SToomas Soome 				nChars = 0;
756*a1bf3f78SToomas Soome 				if (pager_output(pPad))
757*a1bf3f78SToomas Soome 					goto pager_done;
758*a1bf3f78SToomas Soome 			}
759*a1bf3f78SToomas Soome 
760*a1bf3f78SToomas Soome 			cp = wp->name;
761*a1bf3f78SToomas Soome 			nChars += sprintf(pPad + nChars, "%s", cp);
762*a1bf3f78SToomas Soome 
763*a1bf3f78SToomas Soome 			if (nChars > columns - 10) {
764*a1bf3f78SToomas Soome 				pPad[nChars++] = '\n';
765*a1bf3f78SToomas Soome 				pPad[nChars] = '\0';
766*a1bf3f78SToomas Soome 				nChars = 0;
767*a1bf3f78SToomas Soome 				if (pager_output(pPad))
768*a1bf3f78SToomas Soome 					goto pager_done;
769*a1bf3f78SToomas Soome 			} else {
770*a1bf3f78SToomas Soome 				len = nCOLWIDTH - nChars % nCOLWIDTH;
771*a1bf3f78SToomas Soome 				while (len-- > 0)
772*a1bf3f78SToomas Soome 					pPad[nChars++] = ' ';
773*a1bf3f78SToomas Soome 			}
774*a1bf3f78SToomas Soome 
775*a1bf3f78SToomas Soome 			if (nChars > columns - 10) {
776*a1bf3f78SToomas Soome 				pPad[nChars++] = '\n';
777*a1bf3f78SToomas Soome 				pPad[nChars] = '\0';
778*a1bf3f78SToomas Soome 				nChars = 0;
779*a1bf3f78SToomas Soome 				if (pager_output(pPad))
780*a1bf3f78SToomas Soome 					goto pager_done;
781*a1bf3f78SToomas Soome 			}
782*a1bf3f78SToomas Soome 		}
783*a1bf3f78SToomas Soome 	}
784*a1bf3f78SToomas Soome 
785*a1bf3f78SToomas Soome 	if (nChars > 0) {
786*a1bf3f78SToomas Soome 		pPad[nChars++] = '\n';
787*a1bf3f78SToomas Soome 		pPad[nChars] = '\0';
788*a1bf3f78SToomas Soome 		nChars = 0;
789*a1bf3f78SToomas Soome 		ficlVmTextOut(vm, pPad);
790*a1bf3f78SToomas Soome 	}
791*a1bf3f78SToomas Soome 
792*a1bf3f78SToomas Soome 	sprintf(pPad, "Dictionary: %d words, %ld cells used of %u total\n",
793*a1bf3f78SToomas Soome 	    nWords, (long)(dictionary->here - dictionary->base),
794*a1bf3f78SToomas Soome 	    dictionary->size);
795*a1bf3f78SToomas Soome 	pager_output(pPad);
796*a1bf3f78SToomas Soome 
797*a1bf3f78SToomas Soome pager_done:
798*a1bf3f78SToomas Soome 	free(pPad);
799*a1bf3f78SToomas Soome 	pager_close();
800*a1bf3f78SToomas Soome }
801*a1bf3f78SToomas Soome 
802*a1bf3f78SToomas Soome /*
803*a1bf3f78SToomas Soome  * l i s t E n v
804*a1bf3f78SToomas Soome  * Print symbols defined in the environment
805*a1bf3f78SToomas Soome  */
806*a1bf3f78SToomas Soome static void
807*a1bf3f78SToomas Soome ficlPrimitiveListEnv(ficlVm *vm)
808*a1bf3f78SToomas Soome {
809*a1bf3f78SToomas Soome 	ficlDictionary *dictionary = vm->callback.system->environment;
810*a1bf3f78SToomas Soome 	ficlHash *hash = dictionary->forthWordlist;
811*a1bf3f78SToomas Soome 	ficlWord *word;
812*a1bf3f78SToomas Soome 	unsigned i;
813*a1bf3f78SToomas Soome 	int counter = 0;
814*a1bf3f78SToomas Soome 
815*a1bf3f78SToomas Soome 	pager_open();
816*a1bf3f78SToomas Soome 	for (i = 0; i < hash->size; i++) {
817*a1bf3f78SToomas Soome 		for (word = hash->table[i]; word != NULL;
818*a1bf3f78SToomas Soome 		    word = word->link, counter++) {
819*a1bf3f78SToomas Soome 			sprintf(vm->pad, "%s\n", word->name);
820*a1bf3f78SToomas Soome 			if (pager_output(vm->pad))
821*a1bf3f78SToomas Soome 				goto pager_done;
822*a1bf3f78SToomas Soome 		}
823*a1bf3f78SToomas Soome 	}
824*a1bf3f78SToomas Soome 
825*a1bf3f78SToomas Soome 	sprintf(vm->pad, "Environment: %d words, %ld cells used of %u total\n",
826*a1bf3f78SToomas Soome 	    counter, (long)(dictionary->here - dictionary->base),
827*a1bf3f78SToomas Soome 	    dictionary->size);
828*a1bf3f78SToomas Soome 	pager_output(vm->pad);
829*a1bf3f78SToomas Soome 
830*a1bf3f78SToomas Soome pager_done:
831*a1bf3f78SToomas Soome 	pager_close();
832*a1bf3f78SToomas Soome }
833*a1bf3f78SToomas Soome 
834*a1bf3f78SToomas Soome /*
835*a1bf3f78SToomas Soome  * This word lists the parse steps in order
836*a1bf3f78SToomas Soome  */
837*a1bf3f78SToomas Soome void
838*a1bf3f78SToomas Soome ficlPrimitiveParseStepList(ficlVm *vm)
839*a1bf3f78SToomas Soome {
840*a1bf3f78SToomas Soome 	int i;
841*a1bf3f78SToomas Soome 	ficlSystem *system = vm->callback.system;
842*a1bf3f78SToomas Soome 	FICL_VM_ASSERT(vm, system);
843*a1bf3f78SToomas Soome 
844*a1bf3f78SToomas Soome 	ficlVmTextOut(vm, "Parse steps:\n");
845*a1bf3f78SToomas Soome 	ficlVmTextOut(vm, "lookup\n");
846*a1bf3f78SToomas Soome 
847*a1bf3f78SToomas Soome 	for (i = 0; i < FICL_MAX_PARSE_STEPS; i++) {
848*a1bf3f78SToomas Soome 		if (system->parseList[i] != NULL) {
849*a1bf3f78SToomas Soome 			ficlVmTextOut(vm, system->parseList[i]->name);
850*a1bf3f78SToomas Soome 			ficlVmTextOut(vm, "\n");
851*a1bf3f78SToomas Soome 		} else
852*a1bf3f78SToomas Soome 			break;
853*a1bf3f78SToomas Soome 	}
854*a1bf3f78SToomas Soome }
855*a1bf3f78SToomas Soome 
856*a1bf3f78SToomas Soome /*
857*a1bf3f78SToomas Soome  * e n v C o n s t a n t
858*a1bf3f78SToomas Soome  * Ficl interface to ficlSystemSetEnvironment and ficlSetEnvD - allow Ficl
859*a1bf3f78SToomas Soome  * code to set environment constants...
860*a1bf3f78SToomas Soome  */
861*a1bf3f78SToomas Soome static void
862*a1bf3f78SToomas Soome ficlPrimitiveEnvConstant(ficlVm *vm)
863*a1bf3f78SToomas Soome {
864*a1bf3f78SToomas Soome 	unsigned value;
865*a1bf3f78SToomas Soome 	FICL_STACK_CHECK(vm->dataStack, 1, 0);
866*a1bf3f78SToomas Soome 
867*a1bf3f78SToomas Soome 	ficlVmGetWordToPad(vm);
868*a1bf3f78SToomas Soome 	value = ficlStackPopUnsigned(vm->dataStack);
869*a1bf3f78SToomas Soome 	ficlDictionarySetConstant(ficlSystemGetEnvironment(vm->callback.system),
870*a1bf3f78SToomas Soome 	    vm->pad, (ficlUnsigned)value);
871*a1bf3f78SToomas Soome }
872*a1bf3f78SToomas Soome 
873*a1bf3f78SToomas Soome static void
874*a1bf3f78SToomas Soome ficlPrimitiveEnv2Constant(ficlVm *vm)
875*a1bf3f78SToomas Soome {
876*a1bf3f78SToomas Soome 	ficl2Integer value;
877*a1bf3f78SToomas Soome 
878*a1bf3f78SToomas Soome 	FICL_STACK_CHECK(vm->dataStack, 2, 0);
879*a1bf3f78SToomas Soome 
880*a1bf3f78SToomas Soome 	ficlVmGetWordToPad(vm);
881*a1bf3f78SToomas Soome 	value = ficlStackPop2Integer(vm->dataStack);
882*a1bf3f78SToomas Soome 	ficlDictionarySet2Constant(
883*a1bf3f78SToomas Soome 	    ficlSystemGetEnvironment(vm->callback.system), vm->pad, value);
884*a1bf3f78SToomas Soome }
885*a1bf3f78SToomas Soome 
886*a1bf3f78SToomas Soome 
887*a1bf3f78SToomas Soome /*
888*a1bf3f78SToomas Soome  * f i c l C o m p i l e T o o l s
889*a1bf3f78SToomas Soome  * Builds wordset for debugger and TOOLS optional word set
890*a1bf3f78SToomas Soome  */
891*a1bf3f78SToomas Soome void
892*a1bf3f78SToomas Soome ficlSystemCompileTools(ficlSystem *system)
893*a1bf3f78SToomas Soome {
894*a1bf3f78SToomas Soome 	ficlDictionary *dictionary = ficlSystemGetDictionary(system);
895*a1bf3f78SToomas Soome 	ficlDictionary *environment = ficlSystemGetEnvironment(system);
896*a1bf3f78SToomas Soome 
897*a1bf3f78SToomas Soome 	FICL_SYSTEM_ASSERT(system, dictionary);
898*a1bf3f78SToomas Soome 	FICL_SYSTEM_ASSERT(system, environment);
899*a1bf3f78SToomas Soome 
900*a1bf3f78SToomas Soome 
901*a1bf3f78SToomas Soome 	/*
902*a1bf3f78SToomas Soome 	 * TOOLS and TOOLS EXT
903*a1bf3f78SToomas Soome 	 */
904*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, ".s", ficlVmDisplayDataStack,
905*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
906*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, ".s-simple",
907*a1bf3f78SToomas Soome 	    ficlVmDisplayDataStackSimple,  FICL_WORD_DEFAULT);
908*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "bye", ficlPrimitiveBye,
909*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
910*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "forget", ficlPrimitiveForget,
911*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
912*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "see", ficlPrimitiveSee,
913*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
914*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "words", ficlPrimitiveWords,
915*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
916*a1bf3f78SToomas Soome 
917*a1bf3f78SToomas Soome 	/*
918*a1bf3f78SToomas Soome 	 * Set TOOLS environment query values
919*a1bf3f78SToomas Soome 	 */
920*a1bf3f78SToomas Soome 	ficlDictionarySetConstant(environment, "tools", FICL_TRUE);
921*a1bf3f78SToomas Soome 	ficlDictionarySetConstant(environment, "tools-ext", FICL_FALSE);
922*a1bf3f78SToomas Soome 
923*a1bf3f78SToomas Soome 	/*
924*a1bf3f78SToomas Soome 	 * Ficl extras
925*a1bf3f78SToomas Soome 	 */
926*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "r.s", ficlVmDisplayReturnStack,
927*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
928*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, ".env", ficlPrimitiveListEnv,
929*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
930*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "env-constant",
931*a1bf3f78SToomas Soome 	    ficlPrimitiveEnvConstant, FICL_WORD_DEFAULT);
932*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "env-2constant",
933*a1bf3f78SToomas Soome 	    ficlPrimitiveEnv2Constant, FICL_WORD_DEFAULT);
934*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "debug-xt", ficlPrimitiveDebugXT,
935*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
936*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "parse-order",
937*a1bf3f78SToomas Soome 	    ficlPrimitiveParseStepList, FICL_WORD_DEFAULT);
938*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "step-break",
939*a1bf3f78SToomas Soome 	    ficlPrimitiveStepBreak, FICL_WORD_DEFAULT);
940*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "forget-wid",
941*a1bf3f78SToomas Soome 	    ficlPrimitiveForgetWid, FICL_WORD_DEFAULT);
942*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, "see-xt", ficlPrimitiveSeeXT,
943*a1bf3f78SToomas Soome 	    FICL_WORD_DEFAULT);
944*a1bf3f78SToomas Soome 
945*a1bf3f78SToomas Soome #if FICL_WANT_FLOAT
946*a1bf3f78SToomas Soome 	ficlDictionarySetPrimitive(dictionary, ".hash",
947*a1bf3f78SToomas Soome 	    ficlPrimitiveHashSummary, FICL_WORD_DEFAULT);
948*a1bf3f78SToomas Soome #endif
949*a1bf3f78SToomas Soome }
950