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