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$ */ 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 **************************************************************************/ 85 static 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 **************************************************************************/ 99 static 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 **************************************************************************/ 113 int 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 136 static 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 154 static 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 */ 187 static 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 */ 302 static 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 366 static 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 **************************************************************************/ 382 void 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 **************************************************************************/ 416 void 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 **************************************************************************/ 441 void 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 **************************************************************************/ 489 void 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 **************************************************************************/ 611 static 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 **************************************************************************/ 623 static 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 647 static 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 **************************************************************************/ 695 static 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 **************************************************************************/ 719 static 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 739 static 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 **************************************************************************/ 816 static 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 **************************************************************************/ 844 static 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 858 static 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 879 void 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