1 /* 2 * v m . c 3 * Forth Inspired Command Language - virtual machine methods 4 * Author: John Sadler (john_sadler@alum.mit.edu) 5 * Created: 19 July 1997 6 * $Id: vm.c,v 1.17 2010/09/13 18:43:04 asau Exp $ 7 */ 8 /* 9 * This file implements the virtual machine of Ficl. Each virtual 10 * machine retains the state of an interpreter. A virtual machine 11 * owns a pair of stacks for parameters and return addresses, as 12 * well as a pile of state variables and the two dedicated registers 13 * of the interpreter. 14 */ 15 /* 16 * Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu) 17 * All rights reserved. 18 * 19 * Get the latest Ficl release at http://ficl.sourceforge.net 20 * 21 * I am interested in hearing from anyone who uses Ficl. If you have 22 * a problem, a success story, a defect, an enhancement request, or 23 * if you would like to contribute to the Ficl release, please 24 * contact me by email at the address above. 25 * 26 * L I C E N S E and D I S C L A I M E R 27 * 28 * Redistribution and use in source and binary forms, with or without 29 * modification, are permitted provided that the following conditions 30 * are met: 31 * 1. Redistributions of source code must retain the above copyright 32 * notice, this list of conditions and the following disclaimer. 33 * 2. Redistributions in binary form must reproduce the above copyright 34 * notice, this list of conditions and the following disclaimer in the 35 * documentation and/or other materials provided with the distribution. 36 * 37 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 47 * SUCH DAMAGE. 48 */ 49 50 #include "ficl.h" 51 52 #if FICL_ROBUST >= 2 53 #define FICL_VM_CHECK(vm) \ 54 FICL_VM_ASSERT(vm, (*(vm->ip - 1)) == vm->runningWord) 55 #else 56 #define FICL_VM_CHECK(vm) 57 #endif 58 59 /* 60 * v m B r a n c h R e l a t i v e 61 */ 62 void 63 ficlVmBranchRelative(ficlVm *vm, int offset) 64 { 65 vm->ip += offset; 66 } 67 68 /* 69 * v m C r e a t e 70 * Creates a virtual machine either from scratch (if vm is NULL on entry) 71 * or by resizing and reinitializing an existing VM to the specified stack 72 * sizes. 73 */ 74 ficlVm * 75 ficlVmCreate(ficlVm *vm, unsigned nPStack, unsigned nRStack) 76 { 77 if (vm == NULL) { 78 vm = (ficlVm *)ficlMalloc(sizeof (ficlVm)); 79 FICL_ASSERT(NULL, vm); 80 memset(vm, 0, sizeof (ficlVm)); 81 } 82 83 if (vm->dataStack) 84 ficlStackDestroy(vm->dataStack); 85 vm->dataStack = ficlStackCreate(vm, "data", nPStack); 86 87 if (vm->returnStack) 88 ficlStackDestroy(vm->returnStack); 89 vm->returnStack = ficlStackCreate(vm, "return", nRStack); 90 91 #if FICL_WANT_FLOAT 92 if (vm->floatStack) 93 ficlStackDestroy(vm->floatStack); 94 vm->floatStack = ficlStackCreate(vm, "float", nPStack); 95 #endif 96 97 ficlVmReset(vm); 98 return (vm); 99 } 100 101 /* 102 * v m D e l e t e 103 * Free all memory allocated to the specified VM and its subordinate 104 * structures. 105 */ 106 void 107 ficlVmDestroy(ficlVm *vm) 108 { 109 if (vm) { 110 ficlFree(vm->dataStack); 111 ficlFree(vm->returnStack); 112 #if FICL_WANT_FLOAT 113 ficlFree(vm->floatStack); 114 #endif 115 ficlFree(vm); 116 } 117 } 118 119 /* 120 * v m E x e c u t e 121 * Sets up the specified word to be run by the inner interpreter. 122 * Executes the word's code part immediately, but in the case of 123 * colon definition, the definition itself needs the inner interpreter 124 * to complete. This does not happen until control reaches ficlExec 125 */ 126 void 127 ficlVmExecuteWord(ficlVm *vm, ficlWord *pWord) 128 { 129 ficlVmInnerLoop(vm, pWord); 130 } 131 132 static void 133 ficlVmOptimizeJumpToJump(ficlVm *vm, ficlIp ip) 134 { 135 ficlIp destination; 136 switch ((ficlInstruction)(*ip)) { 137 case ficlInstructionBranchParenWithCheck: 138 *ip = (ficlWord *)ficlInstructionBranchParen; 139 goto RUNTIME_FIXUP; 140 141 case ficlInstructionBranch0ParenWithCheck: 142 *ip = (ficlWord *)ficlInstructionBranch0Paren; 143 RUNTIME_FIXUP: 144 ip++; 145 destination = ip + *(ficlInteger *)ip; 146 switch ((ficlInstruction)*destination) { 147 case ficlInstructionBranchParenWithCheck: 148 /* preoptimize where we're jumping to */ 149 ficlVmOptimizeJumpToJump(vm, destination); 150 /* FALLTHROUGH */ 151 case ficlInstructionBranchParen: 152 destination++; 153 destination += *(ficlInteger *)destination; 154 *ip = (ficlWord *)(destination - ip); 155 break; 156 } 157 } 158 } 159 160 /* 161 * v m I n n e r L o o p 162 * the mysterious inner interpreter... 163 * This loop is the address interpreter that makes colon definitions 164 * work. Upon entry, it assumes that the IP points to an entry in 165 * a definition (the body of a colon word). It runs one word at a time 166 * until something does vmThrow. The catcher for this is expected to exist 167 * in the calling code. 168 * vmThrow gets you out of this loop with a longjmp() 169 */ 170 171 #if FICL_ROBUST <= 1 172 /* turn off stack checking for primitives */ 173 #define _CHECK_STACK(stack, top, pop, push) 174 #else 175 176 #define _CHECK_STACK(stack, top, pop, push) \ 177 ficlStackCheckNospill(stack, top, pop, push) 178 179 static FICL_PLATFORM_INLINE void 180 ficlStackCheckNospill(ficlStack *stack, ficlCell *top, int popCells, 181 int pushCells) 182 { 183 /* 184 * Why save and restore stack->top? 185 * So the simple act of stack checking doesn't force a "register" spill, 186 * which might mask bugs (places where we needed to spill but didn't). 187 * --lch 188 */ 189 ficlCell *oldTop = stack->top; 190 stack->top = top; 191 ficlStackCheck(stack, popCells, pushCells); 192 stack->top = oldTop; 193 } 194 195 #endif /* FICL_ROBUST <= 1 */ 196 197 #define CHECK_STACK(pop, push) \ 198 _CHECK_STACK(vm->dataStack, dataTop, pop, push) 199 #define CHECK_FLOAT_STACK(pop, push) \ 200 _CHECK_STACK(vm->floatStack, floatTop, pop, push) 201 #define CHECK_RETURN_STACK(pop, push) \ 202 _CHECK_STACK(vm->returnStack, returnTop, pop, push) 203 204 #if FICL_WANT_FLOAT 205 #define FLOAT_LOCAL_VARIABLE_SPILL \ 206 vm->floatStack->top = floatTop; 207 #define FLOAT_LOCAL_VARIABLE_REFILL \ 208 floatTop = vm->floatStack->top; 209 #else 210 #define FLOAT_LOCAL_VARIABLE_SPILL 211 #define FLOAT_LOCAL_VARIABLE_REFILL 212 #endif /* FICL_WANT_FLOAT */ 213 214 #if FICL_WANT_LOCALS 215 #define LOCALS_LOCAL_VARIABLE_SPILL \ 216 vm->returnStack->frame = frame; 217 #define LOCALS_LOCAL_VARIABLE_REFILL \ 218 frame = vm->returnStack->frame; 219 #else 220 #define LOCALS_LOCAL_VARIABLE_SPILL 221 #define LOCALS_LOCAL_VARIABLE_REFILL 222 #endif /* FICL_WANT_FLOAT */ 223 224 #define LOCAL_VARIABLE_SPILL \ 225 vm->ip = (ficlIp)ip; \ 226 vm->dataStack->top = dataTop; \ 227 vm->returnStack->top = returnTop; \ 228 FLOAT_LOCAL_VARIABLE_SPILL \ 229 LOCALS_LOCAL_VARIABLE_SPILL 230 231 #define LOCAL_VARIABLE_REFILL \ 232 ip = (ficlInstruction *)vm->ip; \ 233 dataTop = vm->dataStack->top; \ 234 returnTop = vm->returnStack->top; \ 235 FLOAT_LOCAL_VARIABLE_REFILL \ 236 LOCALS_LOCAL_VARIABLE_REFILL 237 238 void 239 ficlVmInnerLoop(ficlVm *vm, ficlWord *fw) 240 { 241 register ficlInstruction *ip; 242 register ficlCell *dataTop; 243 register ficlCell *returnTop; 244 #if FICL_WANT_FLOAT 245 register ficlCell *floatTop; 246 ficlFloat f; 247 #endif /* FICL_WANT_FLOAT */ 248 #if FICL_WANT_LOCALS 249 register ficlCell *frame; 250 #endif /* FICL_WANT_LOCALS */ 251 jmp_buf *oldExceptionHandler; 252 jmp_buf exceptionHandler; 253 int except; 254 int once; 255 int count; 256 ficlInstruction instruction; 257 ficlInteger i; 258 ficlUnsigned u; 259 ficlCell c; 260 ficlCountedString *s; 261 ficlCell *cell; 262 char *cp; 263 264 once = (fw != NULL); 265 if (once) 266 count = 1; 267 268 oldExceptionHandler = vm->exceptionHandler; 269 /* This has to come before the setjmp! */ 270 vm->exceptionHandler = &exceptionHandler; 271 except = setjmp(exceptionHandler); 272 273 LOCAL_VARIABLE_REFILL; 274 275 if (except) { 276 LOCAL_VARIABLE_SPILL; 277 vm->exceptionHandler = oldExceptionHandler; 278 ficlVmThrow(vm, except); 279 } 280 281 for (;;) { 282 if (once) { 283 if (!count--) 284 break; 285 instruction = (ficlInstruction)((void *)fw); 286 } else { 287 instruction = *ip++; 288 fw = (ficlWord *)instruction; 289 } 290 291 AGAIN: 292 switch (instruction) { 293 case ficlInstructionInvalid: 294 ficlVmThrowError(vm, 295 "Error: NULL instruction executed!"); 296 return; 297 298 case ficlInstruction1: 299 case ficlInstruction2: 300 case ficlInstruction3: 301 case ficlInstruction4: 302 case ficlInstruction5: 303 case ficlInstruction6: 304 case ficlInstruction7: 305 case ficlInstruction8: 306 case ficlInstruction9: 307 case ficlInstruction10: 308 case ficlInstruction11: 309 case ficlInstruction12: 310 case ficlInstruction13: 311 case ficlInstruction14: 312 case ficlInstruction15: 313 case ficlInstruction16: 314 CHECK_STACK(0, 1); 315 (++dataTop)->i = instruction; 316 continue; 317 318 case ficlInstruction0: 319 case ficlInstructionNeg1: 320 case ficlInstructionNeg2: 321 case ficlInstructionNeg3: 322 case ficlInstructionNeg4: 323 case ficlInstructionNeg5: 324 case ficlInstructionNeg6: 325 case ficlInstructionNeg7: 326 case ficlInstructionNeg8: 327 case ficlInstructionNeg9: 328 case ficlInstructionNeg10: 329 case ficlInstructionNeg11: 330 case ficlInstructionNeg12: 331 case ficlInstructionNeg13: 332 case ficlInstructionNeg14: 333 case ficlInstructionNeg15: 334 case ficlInstructionNeg16: 335 CHECK_STACK(0, 1); 336 (++dataTop)->i = ficlInstruction0 - instruction; 337 continue; 338 339 /* 340 * stringlit: Fetch the count from the dictionary, then push 341 * the address and count on the stack. Finally, update ip to 342 * point to the first aligned address after the string text. 343 */ 344 case ficlInstructionStringLiteralParen: { 345 ficlUnsigned8 length; 346 CHECK_STACK(0, 2); 347 348 s = (ficlCountedString *)(ip); 349 length = s->length; 350 cp = s->text; 351 (++dataTop)->p = cp; 352 (++dataTop)->i = length; 353 354 cp += length + 1; 355 cp = ficlAlignPointer(cp); 356 ip = (void *)cp; 357 continue; 358 } 359 360 case ficlInstructionCStringLiteralParen: 361 CHECK_STACK(0, 1); 362 363 s = (ficlCountedString *)(ip); 364 cp = s->text + s->length + 1; 365 cp = ficlAlignPointer(cp); 366 ip = (void *)cp; 367 (++dataTop)->p = s; 368 continue; 369 370 #if FICL_WANT_OPTIMIZE == FICL_OPTIMIZE_FOR_SIZE 371 #if FICL_WANT_FLOAT 372 FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC: 373 *++floatTop = cell[1]; 374 /* intentional fall-through */ 375 FLOAT_PUSH_CELL_POINTER_MINIPROC: 376 *++floatTop = cell[0]; 377 continue; 378 379 FLOAT_POP_CELL_POINTER_MINIPROC: 380 cell[0] = *floatTop--; 381 continue; 382 383 FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC: 384 cell[0] = *floatTop--; 385 cell[1] = *floatTop--; 386 continue; 387 388 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \ 389 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC 390 #define FLOAT_PUSH_CELL_POINTER(cp) \ 391 cell = (cp); goto FLOAT_PUSH_CELL_POINTER_MINIPROC 392 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \ 393 cell = (cp); goto FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC 394 #define FLOAT_POP_CELL_POINTER(cp) \ 395 cell = (cp); goto FLOAT_POP_CELL_POINTER_MINIPROC 396 #endif /* FICL_WANT_FLOAT */ 397 398 /* 399 * Think of these as little mini-procedures. 400 * --lch 401 */ 402 PUSH_CELL_POINTER_DOUBLE_MINIPROC: 403 *++dataTop = cell[1]; 404 /* intentional fall-through */ 405 PUSH_CELL_POINTER_MINIPROC: 406 *++dataTop = cell[0]; 407 continue; 408 409 POP_CELL_POINTER_MINIPROC: 410 cell[0] = *dataTop--; 411 continue; 412 POP_CELL_POINTER_DOUBLE_MINIPROC: 413 cell[0] = *dataTop--; 414 cell[1] = *dataTop--; 415 continue; 416 417 #define PUSH_CELL_POINTER_DOUBLE(cp) \ 418 cell = (cp); goto PUSH_CELL_POINTER_DOUBLE_MINIPROC 419 #define PUSH_CELL_POINTER(cp) \ 420 cell = (cp); goto PUSH_CELL_POINTER_MINIPROC 421 #define POP_CELL_POINTER_DOUBLE(cp) \ 422 cell = (cp); goto POP_CELL_POINTER_DOUBLE_MINIPROC 423 #define POP_CELL_POINTER(cp) \ 424 cell = (cp); goto POP_CELL_POINTER_MINIPROC 425 426 BRANCH_MINIPROC: 427 ip += *(ficlInteger *)ip; 428 continue; 429 430 #define BRANCH() goto BRANCH_MINIPROC 431 432 EXIT_FUNCTION_MINIPROC: 433 ip = (ficlInstruction *)((returnTop--)->p); 434 continue; 435 436 #define EXIT_FUNCTION goto EXIT_FUNCTION_MINIPROC 437 438 #else /* FICL_WANT_SIZE */ 439 440 #if FICL_WANT_FLOAT 441 #define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) \ 442 cell = (cp); *++floatTop = cell[1]; *++floatTop = *cell; continue 443 #define FLOAT_PUSH_CELL_POINTER(cp) \ 444 cell = (cp); *++floatTop = *cell; continue 445 #define FLOAT_POP_CELL_POINTER_DOUBLE(cp) \ 446 cell = (cp); *cell = *floatTop--; cell[1] = *floatTop--; continue 447 #define FLOAT_POP_CELL_POINTER(cp) \ 448 cell = (cp); *cell = *floatTop--; continue 449 #endif /* FICL_WANT_FLOAT */ 450 451 #define PUSH_CELL_POINTER_DOUBLE(cp) \ 452 cell = (cp); *++dataTop = cell[1]; *++dataTop = *cell; continue 453 #define PUSH_CELL_POINTER(cp) \ 454 cell = (cp); *++dataTop = *cell; continue 455 #define POP_CELL_POINTER_DOUBLE(cp) \ 456 cell = (cp); *cell = *dataTop--; cell[1] = *dataTop--; continue 457 #define POP_CELL_POINTER(cp) \ 458 cell = (cp); *cell = *dataTop--; continue 459 460 #define BRANCH() ip += *(ficlInteger *)ip; continue 461 #define EXIT_FUNCTION() ip = (ficlInstruction *)((returnTop--)->p); continue 462 463 #endif /* FICL_WANT_SIZE */ 464 465 466 /* 467 * This is the runtime for (literal). It assumes that it is 468 * part of a colon definition, and that the next ficlCell 469 * contains a value to be pushed on the parameter stack at 470 * runtime. This code is compiled by "literal". 471 */ 472 473 case ficlInstructionLiteralParen: 474 CHECK_STACK(0, 1); 475 (++dataTop)->i = *ip++; 476 continue; 477 478 case ficlInstruction2LiteralParen: 479 CHECK_STACK(0, 2); 480 (++dataTop)->i = ip[1]; 481 (++dataTop)->i = ip[0]; 482 ip += 2; 483 continue; 484 485 #if FICL_WANT_LOCALS 486 /* 487 * Link a frame on the return stack, reserving nCells of space 488 * for locals - the value of nCells is the next ficlCell in 489 * the instruction stream. 490 * 1) Push frame onto returnTop 491 * 2) frame = returnTop 492 * 3) returnTop += nCells 493 */ 494 case ficlInstructionLinkParen: { 495 ficlInteger nCells = *ip++; 496 (++returnTop)->p = frame; 497 frame = returnTop + 1; 498 returnTop += nCells; 499 continue; 500 } 501 502 /* 503 * Unink a stack frame previously created by stackLink 504 * 1) dataTop = frame 505 * 2) frame = pop() 506 */ 507 case ficlInstructionUnlinkParen: 508 returnTop = frame - 1; 509 frame = (returnTop--)->p; 510 continue; 511 512 /* 513 * Immediate - cfa of a local while compiling - when executed, 514 * compiles code to fetch the value of a local given the 515 * local's index in the word's pfa 516 */ 517 #if FICL_WANT_FLOAT 518 case ficlInstructionGetF2LocalParen: 519 FLOAT_PUSH_CELL_POINTER_DOUBLE(frame + *ip++); 520 521 case ficlInstructionGetFLocalParen: 522 FLOAT_PUSH_CELL_POINTER(frame + *ip++); 523 524 case ficlInstructionToF2LocalParen: 525 FLOAT_POP_CELL_POINTER_DOUBLE(frame + *ip++); 526 527 case ficlInstructionToFLocalParen: 528 FLOAT_POP_CELL_POINTER(frame + *ip++); 529 #endif /* FICL_WANT_FLOAT */ 530 531 case ficlInstructionGet2LocalParen: 532 PUSH_CELL_POINTER_DOUBLE(frame + *ip++); 533 534 case ficlInstructionGetLocalParen: 535 PUSH_CELL_POINTER(frame + *ip++); 536 537 /* 538 * Immediate - cfa of a local while compiling - when executed, 539 * compiles code to store the value of a local given the 540 * local's index in the word's pfa 541 */ 542 543 case ficlInstructionTo2LocalParen: 544 POP_CELL_POINTER_DOUBLE(frame + *ip++); 545 546 case ficlInstructionToLocalParen: 547 POP_CELL_POINTER(frame + *ip++); 548 549 /* 550 * Silly little minor optimizations. 551 * --lch 552 */ 553 case ficlInstructionGetLocal0: 554 PUSH_CELL_POINTER(frame); 555 556 case ficlInstructionGetLocal1: 557 PUSH_CELL_POINTER(frame + 1); 558 559 case ficlInstructionGet2Local0: 560 PUSH_CELL_POINTER_DOUBLE(frame); 561 562 case ficlInstructionToLocal0: 563 POP_CELL_POINTER(frame); 564 565 case ficlInstructionToLocal1: 566 POP_CELL_POINTER(frame + 1); 567 568 case ficlInstructionTo2Local0: 569 POP_CELL_POINTER_DOUBLE(frame); 570 571 #endif /* FICL_WANT_LOCALS */ 572 573 case ficlInstructionPlus: 574 CHECK_STACK(2, 1); 575 i = (dataTop--)->i; 576 dataTop->i += i; 577 continue; 578 579 case ficlInstructionMinus: 580 CHECK_STACK(2, 1); 581 i = (dataTop--)->i; 582 dataTop->i -= i; 583 continue; 584 585 case ficlInstruction1Plus: 586 CHECK_STACK(1, 1); 587 dataTop->i++; 588 continue; 589 590 case ficlInstruction1Minus: 591 CHECK_STACK(1, 1); 592 dataTop->i--; 593 continue; 594 595 case ficlInstruction2Plus: 596 CHECK_STACK(1, 1); 597 dataTop->i += 2; 598 continue; 599 600 case ficlInstruction2Minus: 601 CHECK_STACK(1, 1); 602 dataTop->i -= 2; 603 continue; 604 605 case ficlInstructionDup: { 606 ficlInteger i = dataTop->i; 607 CHECK_STACK(0, 1); 608 (++dataTop)->i = i; 609 continue; 610 } 611 612 case ficlInstructionQuestionDup: 613 CHECK_STACK(1, 2); 614 615 if (dataTop->i != 0) { 616 dataTop[1] = dataTop[0]; 617 dataTop++; 618 } 619 620 continue; 621 622 case ficlInstructionSwap: { 623 ficlCell swap; 624 CHECK_STACK(2, 2); 625 swap = dataTop[0]; 626 dataTop[0] = dataTop[-1]; 627 dataTop[-1] = swap; 628 } 629 continue; 630 631 case ficlInstructionDrop: 632 CHECK_STACK(1, 0); 633 dataTop--; 634 continue; 635 636 case ficlInstruction2Drop: 637 CHECK_STACK(2, 0); 638 dataTop -= 2; 639 continue; 640 641 case ficlInstruction2Dup: 642 CHECK_STACK(2, 4); 643 dataTop[1] = dataTop[-1]; 644 dataTop[2] = *dataTop; 645 dataTop += 2; 646 continue; 647 648 case ficlInstructionOver: 649 CHECK_STACK(2, 3); 650 dataTop[1] = dataTop[-1]; 651 dataTop++; 652 continue; 653 654 case ficlInstruction2Over: 655 CHECK_STACK(4, 6); 656 dataTop[1] = dataTop[-3]; 657 dataTop[2] = dataTop[-2]; 658 dataTop += 2; 659 continue; 660 661 case ficlInstructionPick: 662 CHECK_STACK(1, 0); 663 i = dataTop->i; 664 if (i < 0) 665 continue; 666 CHECK_STACK(i + 2, i + 3); 667 *dataTop = dataTop[-i - 1]; 668 continue; 669 670 /* 671 * Do stack rot. 672 * rot ( 1 2 3 -- 2 3 1 ) 673 */ 674 case ficlInstructionRot: 675 i = 2; 676 goto ROLL; 677 678 /* 679 * Do stack roll. 680 * roll ( n -- ) 681 */ 682 case ficlInstructionRoll: 683 CHECK_STACK(1, 0); 684 i = (dataTop--)->i; 685 686 if (i < 1) 687 continue; 688 689 ROLL: 690 CHECK_STACK(i+1, i+2); 691 c = dataTop[-i]; 692 memmove(dataTop - i, dataTop - (i - 1), 693 i * sizeof (ficlCell)); 694 *dataTop = c; 695 continue; 696 697 /* 698 * Do stack -rot. 699 * -rot ( 1 2 3 -- 3 1 2 ) 700 */ 701 case ficlInstructionMinusRot: 702 i = 2; 703 goto MINUSROLL; 704 705 /* 706 * Do stack -roll. 707 * -roll ( n -- ) 708 */ 709 case ficlInstructionMinusRoll: 710 CHECK_STACK(1, 0); 711 i = (dataTop--)->i; 712 713 if (i < 1) 714 continue; 715 716 MINUSROLL: 717 CHECK_STACK(i+1, i+2); 718 c = *dataTop; 719 memmove(dataTop - (i - 1), dataTop - i, 720 i * sizeof (ficlCell)); 721 dataTop[-i] = c; 722 723 continue; 724 725 /* 726 * Do stack 2swap 727 * 2swap ( 1 2 3 4 -- 3 4 1 2 ) 728 */ 729 case ficlInstruction2Swap: { 730 ficlCell c2; 731 CHECK_STACK(4, 4); 732 733 c = *dataTop; 734 c2 = dataTop[-1]; 735 736 *dataTop = dataTop[-2]; 737 dataTop[-1] = dataTop[-3]; 738 739 dataTop[-2] = c; 740 dataTop[-3] = c2; 741 continue; 742 } 743 744 case ficlInstructionPlusStore: { 745 ficlCell *cell; 746 CHECK_STACK(2, 0); 747 cell = (ficlCell *)(dataTop--)->p; 748 cell->i += (dataTop--)->i; 749 continue; 750 } 751 752 case ficlInstructionQuadFetch: { 753 ficlUnsigned32 *integer32; 754 CHECK_STACK(1, 1); 755 integer32 = (ficlUnsigned32 *)dataTop->i; 756 dataTop->u = (ficlUnsigned)*integer32; 757 continue; 758 } 759 760 case ficlInstructionQuadStore: { 761 ficlUnsigned32 *integer32; 762 CHECK_STACK(2, 0); 763 integer32 = (ficlUnsigned32 *)(dataTop--)->p; 764 *integer32 = (ficlUnsigned32)((dataTop--)->u); 765 continue; 766 } 767 768 case ficlInstructionWFetch: { 769 ficlUnsigned16 *integer16; 770 CHECK_STACK(1, 1); 771 integer16 = (ficlUnsigned16 *)dataTop->p; 772 dataTop->u = ((ficlUnsigned)*integer16); 773 continue; 774 } 775 776 case ficlInstructionWStore: { 777 ficlUnsigned16 *integer16; 778 CHECK_STACK(2, 0); 779 integer16 = (ficlUnsigned16 *)(dataTop--)->p; 780 *integer16 = (ficlUnsigned16)((dataTop--)->u); 781 continue; 782 } 783 784 case ficlInstructionCFetch: { 785 ficlUnsigned8 *integer8; 786 CHECK_STACK(1, 1); 787 integer8 = (ficlUnsigned8 *)dataTop->p; 788 dataTop->u = ((ficlUnsigned)*integer8); 789 continue; 790 } 791 792 case ficlInstructionCStore: { 793 ficlUnsigned8 *integer8; 794 CHECK_STACK(2, 0); 795 integer8 = (ficlUnsigned8 *)(dataTop--)->p; 796 *integer8 = (ficlUnsigned8)((dataTop--)->u); 797 continue; 798 } 799 800 801 /* 802 * l o g i c a n d c o m p a r i s o n s 803 */ 804 805 case ficlInstruction0Equals: 806 CHECK_STACK(1, 1); 807 dataTop->i = FICL_BOOL(dataTop->i == 0); 808 continue; 809 810 case ficlInstruction0Less: 811 CHECK_STACK(1, 1); 812 dataTop->i = FICL_BOOL(dataTop->i < 0); 813 continue; 814 815 case ficlInstruction0Greater: 816 CHECK_STACK(1, 1); 817 dataTop->i = FICL_BOOL(dataTop->i > 0); 818 continue; 819 820 case ficlInstructionEquals: 821 CHECK_STACK(2, 1); 822 i = (dataTop--)->i; 823 dataTop->i = FICL_BOOL(dataTop->i == i); 824 continue; 825 826 case ficlInstructionLess: 827 CHECK_STACK(2, 1); 828 i = (dataTop--)->i; 829 dataTop->i = FICL_BOOL(dataTop->i < i); 830 continue; 831 832 case ficlInstructionULess: 833 CHECK_STACK(2, 1); 834 u = (dataTop--)->u; 835 dataTop->i = FICL_BOOL(dataTop->u < u); 836 continue; 837 838 case ficlInstructionAnd: 839 CHECK_STACK(2, 1); 840 i = (dataTop--)->i; 841 dataTop->i = dataTop->i & i; 842 continue; 843 844 case ficlInstructionOr: 845 CHECK_STACK(2, 1); 846 i = (dataTop--)->i; 847 dataTop->i = dataTop->i | i; 848 continue; 849 850 case ficlInstructionXor: 851 CHECK_STACK(2, 1); 852 i = (dataTop--)->i; 853 dataTop->i = dataTop->i ^ i; 854 continue; 855 856 case ficlInstructionInvert: 857 CHECK_STACK(1, 1); 858 dataTop->i = ~dataTop->i; 859 continue; 860 861 /* 862 * r e t u r n s t a c k 863 */ 864 case ficlInstructionToRStack: 865 CHECK_STACK(1, 0); 866 CHECK_RETURN_STACK(0, 1); 867 *++returnTop = *dataTop--; 868 continue; 869 870 case ficlInstructionFromRStack: 871 CHECK_STACK(0, 1); 872 CHECK_RETURN_STACK(1, 0); 873 *++dataTop = *returnTop--; 874 continue; 875 876 case ficlInstructionFetchRStack: 877 CHECK_STACK(0, 1); 878 CHECK_RETURN_STACK(1, 1); 879 *++dataTop = *returnTop; 880 continue; 881 882 case ficlInstruction2ToR: 883 CHECK_STACK(2, 0); 884 CHECK_RETURN_STACK(0, 2); 885 *++returnTop = dataTop[-1]; 886 *++returnTop = dataTop[0]; 887 dataTop -= 2; 888 continue; 889 890 case ficlInstruction2RFrom: 891 CHECK_STACK(0, 2); 892 CHECK_RETURN_STACK(2, 0); 893 *++dataTop = returnTop[-1]; 894 *++dataTop = returnTop[0]; 895 returnTop -= 2; 896 continue; 897 898 case ficlInstruction2RFetch: 899 CHECK_STACK(0, 2); 900 CHECK_RETURN_STACK(2, 2); 901 *++dataTop = returnTop[-1]; 902 *++dataTop = returnTop[0]; 903 continue; 904 905 /* 906 * f i l l 907 * CORE ( c-addr u char -- ) 908 * If u is greater than zero, store char in each of u 909 * consecutive characters of memory beginning at c-addr. 910 */ 911 case ficlInstructionFill: { 912 char c; 913 char *memory; 914 CHECK_STACK(3, 0); 915 c = (char)(dataTop--)->i; 916 u = (dataTop--)->u; 917 memory = (char *)(dataTop--)->p; 918 919 /* 920 * memset() is faster than the previous hand-rolled 921 * solution. --lch 922 */ 923 memset(memory, c, u); 924 continue; 925 } 926 927 /* 928 * l s h i f t 929 * l-shift CORE ( x1 u -- x2 ) 930 * Perform a logical left shift of u bit-places on x1, 931 * giving x2. Put zeroes into the least significant bits 932 * vacated by the shift. An ambiguous condition exists if 933 * u is greater than or equal to the number of bits in a 934 * ficlCell. 935 * 936 * r-shift CORE ( x1 u -- x2 ) 937 * Perform a logical right shift of u bit-places on x1, 938 * giving x2. Put zeroes into the most significant bits 939 * vacated by the shift. An ambiguous condition exists 940 * if u is greater than or equal to the number of bits 941 * in a ficlCell. 942 */ 943 case ficlInstructionLShift: { 944 ficlUnsigned nBits; 945 ficlUnsigned x1; 946 CHECK_STACK(2, 1); 947 948 nBits = (dataTop--)->u; 949 x1 = dataTop->u; 950 dataTop->u = x1 << nBits; 951 continue; 952 } 953 954 case ficlInstructionRShift: { 955 ficlUnsigned nBits; 956 ficlUnsigned x1; 957 CHECK_STACK(2, 1); 958 959 nBits = (dataTop--)->u; 960 x1 = dataTop->u; 961 dataTop->u = x1 >> nBits; 962 continue; 963 } 964 965 /* 966 * m a x & m i n 967 */ 968 case ficlInstructionMax: { 969 ficlInteger n2; 970 ficlInteger n1; 971 CHECK_STACK(2, 1); 972 973 n2 = (dataTop--)->i; 974 n1 = dataTop->i; 975 976 dataTop->i = ((n1 > n2) ? n1 : n2); 977 continue; 978 } 979 980 case ficlInstructionMin: { 981 ficlInteger n2; 982 ficlInteger n1; 983 CHECK_STACK(2, 1); 984 985 n2 = (dataTop--)->i; 986 n1 = dataTop->i; 987 988 dataTop->i = ((n1 < n2) ? n1 : n2); 989 continue; 990 } 991 992 /* 993 * m o v e 994 * CORE ( addr1 addr2 u -- ) 995 * If u is greater than zero, copy the contents of u 996 * consecutive address units at addr1 to the u consecutive 997 * address units at addr2. After MOVE completes, the u 998 * consecutive address units at addr2 contain exactly 999 * what the u consecutive address units at addr1 contained 1000 * before the move. 1001 * NOTE! This implementation assumes that a char is the same 1002 * size as an address unit. 1003 */ 1004 case ficlInstructionMove: { 1005 ficlUnsigned u; 1006 char *addr2; 1007 char *addr1; 1008 CHECK_STACK(3, 0); 1009 1010 u = (dataTop--)->u; 1011 addr2 = (dataTop--)->p; 1012 addr1 = (dataTop--)->p; 1013 1014 if (u == 0) 1015 continue; 1016 /* 1017 * Do the copy carefully, so as to be 1018 * correct even if the two ranges overlap 1019 */ 1020 /* Which ANSI C's memmove() does for you! Yay! --lch */ 1021 memmove(addr2, addr1, u); 1022 continue; 1023 } 1024 1025 /* 1026 * s t o d 1027 * s-to-d CORE ( n -- d ) 1028 * Convert the number n to the double-ficlCell number d with 1029 * the same numerical value. 1030 */ 1031 case ficlInstructionSToD: { 1032 ficlInteger s; 1033 CHECK_STACK(1, 2); 1034 1035 s = dataTop->i; 1036 1037 /* sign extend to 64 bits.. */ 1038 (++dataTop)->i = (s < 0) ? -1 : 0; 1039 continue; 1040 } 1041 1042 /* 1043 * c o m p a r e 1044 * STRING ( c-addr1 u1 c-addr2 u2 -- n ) 1045 * Compare the string specified by c-addr1 u1 to the string 1046 * specified by c-addr2 u2. The strings are compared, beginning 1047 * at the given addresses, character by character, up to the 1048 * length of the shorter string or until a difference is found. 1049 * If the two strings are identical, n is zero. If the two 1050 * strings are identical up to the length of the shorter string, 1051 * n is minus-one (-1) if u1 is less than u2 and one (1) 1052 * otherwise. If the two strings are not identical up to the 1053 * length of the shorter string, n is minus-one (-1) if the 1054 * first non-matching character in the string specified by 1055 * c-addr1 u1 has a lesser numeric value than the corresponding 1056 * character in the string specified by c-addr2 u2 and 1057 * one (1) otherwise. 1058 */ 1059 case ficlInstructionCompare: 1060 i = FICL_FALSE; 1061 goto COMPARE; 1062 1063 1064 case ficlInstructionCompareInsensitive: 1065 i = FICL_TRUE; 1066 goto COMPARE; 1067 1068 COMPARE: 1069 { 1070 char *cp1, *cp2; 1071 ficlUnsigned u1, u2, uMin; 1072 int n = 0; 1073 1074 CHECK_STACK(4, 1); 1075 u2 = (dataTop--)->u; 1076 cp2 = (char *)(dataTop--)->p; 1077 u1 = (dataTop--)->u; 1078 cp1 = (char *)(dataTop--)->p; 1079 1080 uMin = (u1 < u2)? u1 : u2; 1081 for (; (uMin > 0) && (n == 0); uMin--) { 1082 int c1 = (unsigned char)*cp1++; 1083 int c2 = (unsigned char)*cp2++; 1084 1085 if (i) { 1086 c1 = tolower(c1); 1087 c2 = tolower(c2); 1088 } 1089 n = (c1 - c2); 1090 } 1091 1092 if (n == 0) 1093 n = (int)(u1 - u2); 1094 1095 if (n < 0) 1096 n = -1; 1097 else if (n > 0) 1098 n = 1; 1099 1100 (++dataTop)->i = n; 1101 continue; 1102 } 1103 1104 /* 1105 * r a n d o m 1106 * Ficl-specific 1107 */ 1108 case ficlInstructionRandom: 1109 (++dataTop)->i = random(); 1110 continue; 1111 1112 /* 1113 * s e e d - r a n d o m 1114 * Ficl-specific 1115 */ 1116 case ficlInstructionSeedRandom: 1117 srandom((dataTop--)->i); 1118 continue; 1119 1120 case ficlInstructionGreaterThan: { 1121 ficlInteger x, y; 1122 CHECK_STACK(2, 1); 1123 y = (dataTop--)->i; 1124 x = dataTop->i; 1125 dataTop->i = FICL_BOOL(x > y); 1126 continue; 1127 1128 case ficlInstructionUGreaterThan: 1129 CHECK_STACK(2, 1); 1130 u = (dataTop--)->u; 1131 dataTop->i = FICL_BOOL(dataTop->u > u); 1132 continue; 1133 1134 } 1135 1136 /* 1137 * This function simply pops the previous instruction 1138 * pointer and returns to the "next" loop. Used for exiting 1139 * from within a definition. Note that exitParen is identical 1140 * to semiParen - they are in two different functions so that 1141 * "see" can correctly identify the end of a colon definition, 1142 * even if it uses "exit". 1143 */ 1144 case ficlInstructionExitParen: 1145 case ficlInstructionSemiParen: 1146 EXIT_FUNCTION(); 1147 1148 /* 1149 * The first time we run "(branch)", perform a "peephole 1150 * optimization" to see if we're jumping to another 1151 * unconditional jump. If so, just jump directly there. 1152 */ 1153 case ficlInstructionBranchParenWithCheck: 1154 LOCAL_VARIABLE_SPILL; 1155 ficlVmOptimizeJumpToJump(vm, vm->ip - 1); 1156 LOCAL_VARIABLE_REFILL; 1157 goto BRANCH_PAREN; 1158 1159 /* 1160 * Same deal with branch0. 1161 */ 1162 case ficlInstructionBranch0ParenWithCheck: 1163 LOCAL_VARIABLE_SPILL; 1164 ficlVmOptimizeJumpToJump(vm, vm->ip - 1); 1165 LOCAL_VARIABLE_REFILL; 1166 /* intentional fall-through */ 1167 1168 /* 1169 * Runtime code for "(branch0)"; pop a flag from the stack, 1170 * branch if 0. fall through otherwise. 1171 * The heart of "if" and "until". 1172 */ 1173 case ficlInstructionBranch0Paren: 1174 CHECK_STACK(1, 0); 1175 1176 if ((dataTop--)->i) { 1177 /* 1178 * don't branch, but skip over branch 1179 * relative address 1180 */ 1181 ip += 1; 1182 continue; 1183 } 1184 /* otherwise, take branch (to else/endif/begin) */ 1185 /* intentional fall-through! */ 1186 1187 /* 1188 * Runtime for "(branch)" -- expects a literal offset in the 1189 * next compilation address, and branches to that location. 1190 */ 1191 case ficlInstructionBranchParen: 1192 BRANCH_PAREN: 1193 BRANCH(); 1194 1195 case ficlInstructionOfParen: { 1196 ficlUnsigned a, b; 1197 1198 CHECK_STACK(2, 1); 1199 1200 a = (dataTop--)->u; 1201 b = dataTop->u; 1202 1203 if (a == b) { 1204 /* fall through */ 1205 ip++; 1206 /* remove CASE argument */ 1207 dataTop--; 1208 } else { 1209 /* take branch to next of or endcase */ 1210 BRANCH(); 1211 } 1212 1213 continue; 1214 } 1215 1216 case ficlInstructionDoParen: { 1217 ficlCell index, limit; 1218 1219 CHECK_STACK(2, 0); 1220 1221 index = *dataTop--; 1222 limit = *dataTop--; 1223 1224 /* copy "leave" target addr to stack */ 1225 (++returnTop)->i = *(ip++); 1226 *++returnTop = limit; 1227 *++returnTop = index; 1228 1229 continue; 1230 } 1231 1232 case ficlInstructionQDoParen: { 1233 ficlCell index, limit, leave; 1234 1235 CHECK_STACK(2, 0); 1236 1237 index = *dataTop--; 1238 limit = *dataTop--; 1239 1240 leave.i = *ip; 1241 1242 if (limit.u == index.u) { 1243 ip = leave.p; 1244 } else { 1245 ip++; 1246 *++returnTop = leave; 1247 *++returnTop = limit; 1248 *++returnTop = index; 1249 } 1250 1251 continue; 1252 } 1253 1254 case ficlInstructionLoopParen: 1255 case ficlInstructionPlusLoopParen: { 1256 ficlInteger index; 1257 ficlInteger limit; 1258 int direction = 0; 1259 1260 index = returnTop->i; 1261 limit = returnTop[-1].i; 1262 1263 if (instruction == ficlInstructionLoopParen) 1264 index++; 1265 else { 1266 ficlInteger increment; 1267 CHECK_STACK(1, 0); 1268 increment = (dataTop--)->i; 1269 index += increment; 1270 direction = (increment < 0); 1271 } 1272 1273 if (direction ^ (index >= limit)) { 1274 /* nuke the loop indices & "leave" addr */ 1275 returnTop -= 3; 1276 ip++; /* fall through the loop */ 1277 } else { /* update index, branch to loop head */ 1278 returnTop->i = index; 1279 BRANCH(); 1280 } 1281 1282 continue; 1283 } 1284 1285 1286 /* 1287 * Runtime code to break out of a do..loop construct 1288 * Drop the loop control variables; the branch address 1289 * past "loop" is next on the return stack. 1290 */ 1291 case ficlInstructionLeave: 1292 /* almost unloop */ 1293 returnTop -= 2; 1294 /* exit */ 1295 EXIT_FUNCTION(); 1296 1297 case ficlInstructionUnloop: 1298 returnTop -= 3; 1299 continue; 1300 1301 case ficlInstructionI: 1302 *++dataTop = *returnTop; 1303 continue; 1304 1305 case ficlInstructionJ: 1306 *++dataTop = returnTop[-3]; 1307 continue; 1308 1309 case ficlInstructionK: 1310 *++dataTop = returnTop[-6]; 1311 continue; 1312 1313 case ficlInstructionDoesParen: { 1314 ficlDictionary *dictionary = ficlVmGetDictionary(vm); 1315 dictionary->smudge->code = 1316 (ficlPrimitive)ficlInstructionDoDoes; 1317 dictionary->smudge->param[0].p = ip; 1318 ip = (ficlInstruction *)((returnTop--)->p); 1319 continue; 1320 } 1321 1322 case ficlInstructionDoDoes: { 1323 ficlCell *cell; 1324 ficlIp tempIP; 1325 1326 CHECK_STACK(0, 1); 1327 1328 cell = fw->param; 1329 tempIP = (ficlIp)((*cell).p); 1330 (++dataTop)->p = (cell + 1); 1331 (++returnTop)->p = (void *)ip; 1332 ip = (ficlInstruction *)tempIP; 1333 continue; 1334 } 1335 1336 #if FICL_WANT_FLOAT 1337 case ficlInstructionF2Fetch: 1338 CHECK_FLOAT_STACK(0, 2); 1339 CHECK_STACK(1, 0); 1340 FLOAT_PUSH_CELL_POINTER_DOUBLE((dataTop--)->p); 1341 1342 case ficlInstructionFFetch: 1343 CHECK_FLOAT_STACK(0, 1); 1344 CHECK_STACK(1, 0); 1345 FLOAT_PUSH_CELL_POINTER((dataTop--)->p); 1346 1347 case ficlInstructionF2Store: 1348 CHECK_FLOAT_STACK(2, 0); 1349 CHECK_STACK(1, 0); 1350 FLOAT_POP_CELL_POINTER_DOUBLE((dataTop--)->p); 1351 1352 case ficlInstructionFStore: 1353 CHECK_FLOAT_STACK(1, 0); 1354 CHECK_STACK(1, 0); 1355 FLOAT_POP_CELL_POINTER((dataTop--)->p); 1356 #endif /* FICL_WANT_FLOAT */ 1357 1358 /* 1359 * two-fetch CORE ( a-addr -- x1 x2 ) 1360 * 1361 * Fetch the ficlCell pair x1 x2 stored at a-addr. 1362 * x2 is stored at a-addr and x1 at the next consecutive 1363 * ficlCell. It is equivalent to the sequence 1364 * DUP ficlCell+ @ SWAP @ . 1365 */ 1366 case ficlInstruction2Fetch: 1367 CHECK_STACK(1, 2); 1368 PUSH_CELL_POINTER_DOUBLE((dataTop--)->p); 1369 1370 /* 1371 * fetch CORE ( a-addr -- x ) 1372 * 1373 * x is the value stored at a-addr. 1374 */ 1375 case ficlInstructionFetch: 1376 CHECK_STACK(1, 1); 1377 PUSH_CELL_POINTER((dataTop--)->p); 1378 1379 /* 1380 * two-store CORE ( x1 x2 a-addr -- ) 1381 * Store the ficlCell pair x1 x2 at a-addr, with x2 at a-addr 1382 * and x1 at the next consecutive ficlCell. It is equivalent 1383 * to the sequence SWAP OVER ! ficlCell+ ! 1384 */ 1385 case ficlInstruction2Store: 1386 CHECK_STACK(3, 0); 1387 POP_CELL_POINTER_DOUBLE((dataTop--)->p); 1388 1389 /* 1390 * store CORE ( x a-addr -- ) 1391 * Store x at a-addr. 1392 */ 1393 case ficlInstructionStore: 1394 CHECK_STACK(2, 0); 1395 POP_CELL_POINTER((dataTop--)->p); 1396 1397 case ficlInstructionComma: { 1398 ficlDictionary *dictionary; 1399 CHECK_STACK(1, 0); 1400 1401 dictionary = ficlVmGetDictionary(vm); 1402 ficlDictionaryAppendCell(dictionary, *dataTop--); 1403 continue; 1404 } 1405 1406 case ficlInstructionCComma: { 1407 ficlDictionary *dictionary; 1408 char c; 1409 CHECK_STACK(1, 0); 1410 1411 dictionary = ficlVmGetDictionary(vm); 1412 c = (char)(dataTop--)->i; 1413 ficlDictionaryAppendCharacter(dictionary, c); 1414 continue; 1415 } 1416 1417 case ficlInstructionCells: 1418 CHECK_STACK(1, 1); 1419 dataTop->i *= sizeof (ficlCell); 1420 continue; 1421 1422 case ficlInstructionCellPlus: 1423 CHECK_STACK(1, 1); 1424 dataTop->i += sizeof (ficlCell); 1425 continue; 1426 1427 case ficlInstructionStar: 1428 CHECK_STACK(2, 1); 1429 i = (dataTop--)->i; 1430 dataTop->i *= i; 1431 continue; 1432 1433 case ficlInstructionNegate: 1434 CHECK_STACK(1, 1); 1435 dataTop->i = - dataTop->i; 1436 continue; 1437 1438 case ficlInstructionSlash: 1439 CHECK_STACK(2, 1); 1440 i = (dataTop--)->i; 1441 dataTop->i /= i; 1442 continue; 1443 1444 /* 1445 * slash-mod CORE ( n1 n2 -- n3 n4 ) 1446 * Divide n1 by n2, giving the single-ficlCell remainder n3 1447 * and the single-ficlCell quotient n4. An ambiguous condition 1448 * exists if n2 is zero. If n1 and n2 differ in sign, the 1449 * implementation-defined result returned will be the 1450 * same as that returned by either the phrase 1451 * >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM. 1452 * NOTE: Ficl complies with the second phrase 1453 * (symmetric division) 1454 */ 1455 case ficlInstructionSlashMod: { 1456 ficl2Integer n1; 1457 ficlInteger n2; 1458 ficl2IntegerQR qr; 1459 1460 CHECK_STACK(2, 2); 1461 n2 = dataTop[0].i; 1462 FICL_INTEGER_TO_2INTEGER(dataTop[-1].i, n1); 1463 1464 qr = ficl2IntegerDivideSymmetric(n1, n2); 1465 dataTop[-1].i = qr.remainder; 1466 dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient); 1467 continue; 1468 } 1469 1470 case ficlInstruction2Star: 1471 CHECK_STACK(1, 1); 1472 dataTop->i <<= 1; 1473 continue; 1474 1475 case ficlInstruction2Slash: 1476 CHECK_STACK(1, 1); 1477 dataTop->i >>= 1; 1478 continue; 1479 1480 case ficlInstructionStarSlash: { 1481 ficlInteger x, y, z; 1482 ficl2Integer prod; 1483 CHECK_STACK(3, 1); 1484 1485 z = (dataTop--)->i; 1486 y = (dataTop--)->i; 1487 x = dataTop->i; 1488 1489 prod = ficl2IntegerMultiply(x, y); 1490 dataTop->i = FICL_2UNSIGNED_GET_LOW( 1491 ficl2IntegerDivideSymmetric(prod, z).quotient); 1492 continue; 1493 } 1494 1495 case ficlInstructionStarSlashMod: { 1496 ficlInteger x, y, z; 1497 ficl2Integer prod; 1498 ficl2IntegerQR qr; 1499 1500 CHECK_STACK(3, 2); 1501 1502 z = (dataTop--)->i; 1503 y = dataTop[0].i; 1504 x = dataTop[-1].i; 1505 1506 prod = ficl2IntegerMultiply(x, y); 1507 qr = ficl2IntegerDivideSymmetric(prod, z); 1508 1509 dataTop[-1].i = qr.remainder; 1510 dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient); 1511 continue; 1512 } 1513 1514 #if FICL_WANT_FLOAT 1515 case ficlInstructionF0: 1516 CHECK_FLOAT_STACK(0, 1); 1517 (++floatTop)->f = 0.0f; 1518 continue; 1519 1520 case ficlInstructionF1: 1521 CHECK_FLOAT_STACK(0, 1); 1522 (++floatTop)->f = 1.0f; 1523 continue; 1524 1525 case ficlInstructionFNeg1: 1526 CHECK_FLOAT_STACK(0, 1); 1527 (++floatTop)->f = -1.0f; 1528 continue; 1529 1530 /* 1531 * Floating point literal execution word. 1532 */ 1533 case ficlInstructionFLiteralParen: 1534 CHECK_FLOAT_STACK(0, 1); 1535 1536 /* 1537 * Yes, I'm using ->i here, 1538 * but it's really a float. --lch 1539 */ 1540 (++floatTop)->i = *ip++; 1541 continue; 1542 1543 /* 1544 * Do float addition r1 + r2. 1545 * f+ ( r1 r2 -- r ) 1546 */ 1547 case ficlInstructionFPlus: 1548 CHECK_FLOAT_STACK(2, 1); 1549 1550 f = (floatTop--)->f; 1551 floatTop->f += f; 1552 continue; 1553 1554 /* 1555 * Do float subtraction r1 - r2. 1556 * f- ( r1 r2 -- r ) 1557 */ 1558 case ficlInstructionFMinus: 1559 CHECK_FLOAT_STACK(2, 1); 1560 1561 f = (floatTop--)->f; 1562 floatTop->f -= f; 1563 continue; 1564 1565 /* 1566 * Do float multiplication r1 * r2. 1567 * f* ( r1 r2 -- r ) 1568 */ 1569 case ficlInstructionFStar: 1570 CHECK_FLOAT_STACK(2, 1); 1571 1572 f = (floatTop--)->f; 1573 floatTop->f *= f; 1574 continue; 1575 1576 /* 1577 * Do float negation. 1578 * fnegate ( r -- r ) 1579 */ 1580 case ficlInstructionFNegate: 1581 CHECK_FLOAT_STACK(1, 1); 1582 1583 floatTop->f = -(floatTop->f); 1584 continue; 1585 1586 /* 1587 * Do float division r1 / r2. 1588 * f/ ( r1 r2 -- r ) 1589 */ 1590 case ficlInstructionFSlash: 1591 CHECK_FLOAT_STACK(2, 1); 1592 1593 f = (floatTop--)->f; 1594 floatTop->f /= f; 1595 continue; 1596 1597 /* 1598 * Do float + integer r + n. 1599 * f+i ( r n -- r ) 1600 */ 1601 case ficlInstructionFPlusI: 1602 CHECK_FLOAT_STACK(1, 1); 1603 CHECK_STACK(1, 0); 1604 1605 f = (ficlFloat)(dataTop--)->f; 1606 floatTop->f += f; 1607 continue; 1608 1609 /* 1610 * Do float - integer r - n. 1611 * f-i ( r n -- r ) 1612 */ 1613 case ficlInstructionFMinusI: 1614 CHECK_FLOAT_STACK(1, 1); 1615 CHECK_STACK(1, 0); 1616 1617 f = (ficlFloat)(dataTop--)->f; 1618 floatTop->f -= f; 1619 continue; 1620 1621 /* 1622 * Do float * integer r * n. 1623 * f*i ( r n -- r ) 1624 */ 1625 case ficlInstructionFStarI: 1626 CHECK_FLOAT_STACK(1, 1); 1627 CHECK_STACK(1, 0); 1628 1629 f = (ficlFloat)(dataTop--)->f; 1630 floatTop->f *= f; 1631 continue; 1632 1633 /* 1634 * Do float / integer r / n. 1635 * f/i ( r n -- r ) 1636 */ 1637 case ficlInstructionFSlashI: 1638 CHECK_FLOAT_STACK(1, 1); 1639 CHECK_STACK(1, 0); 1640 1641 f = (ficlFloat)(dataTop--)->f; 1642 floatTop->f /= f; 1643 continue; 1644 1645 /* 1646 * Do integer - float n - r. 1647 * i-f ( n r -- r ) 1648 */ 1649 case ficlInstructionIMinusF: 1650 CHECK_FLOAT_STACK(1, 1); 1651 CHECK_STACK(1, 0); 1652 1653 f = (ficlFloat)(dataTop--)->f; 1654 floatTop->f = f - floatTop->f; 1655 continue; 1656 1657 /* 1658 * Do integer / float n / r. 1659 * i/f ( n r -- r ) 1660 */ 1661 case ficlInstructionISlashF: 1662 CHECK_FLOAT_STACK(1, 1); 1663 CHECK_STACK(1, 0); 1664 1665 f = (ficlFloat)(dataTop--)->f; 1666 floatTop->f = f / floatTop->f; 1667 continue; 1668 1669 /* 1670 * Do integer to float conversion. 1671 * int>float ( n -- r ) 1672 */ 1673 case ficlInstructionIntToFloat: 1674 CHECK_STACK(1, 0); 1675 CHECK_FLOAT_STACK(0, 1); 1676 1677 (++floatTop)->f = ((dataTop--)->f); 1678 continue; 1679 1680 /* 1681 * Do float to integer conversion. 1682 * float>int ( r -- n ) 1683 */ 1684 case ficlInstructionFloatToInt: 1685 CHECK_STACK(0, 1); 1686 CHECK_FLOAT_STACK(1, 0); 1687 1688 (++dataTop)->i = ((floatTop--)->i); 1689 continue; 1690 1691 /* 1692 * Add a floating point number to contents of a variable. 1693 * f+! ( r n -- ) 1694 */ 1695 case ficlInstructionFPlusStore: { 1696 ficlCell *cell; 1697 1698 CHECK_STACK(1, 0); 1699 CHECK_FLOAT_STACK(1, 0); 1700 1701 cell = (ficlCell *)(dataTop--)->p; 1702 cell->f += (floatTop--)->f; 1703 continue; 1704 } 1705 1706 /* 1707 * Do float stack drop. 1708 * fdrop ( r -- ) 1709 */ 1710 case ficlInstructionFDrop: 1711 CHECK_FLOAT_STACK(1, 0); 1712 floatTop--; 1713 continue; 1714 1715 /* 1716 * Do float stack ?dup. 1717 * f?dup ( r -- r ) 1718 */ 1719 case ficlInstructionFQuestionDup: 1720 CHECK_FLOAT_STACK(1, 2); 1721 1722 if (floatTop->f != 0) 1723 goto FDUP; 1724 1725 continue; 1726 1727 /* 1728 * Do float stack dup. 1729 * fdup ( r -- r r ) 1730 */ 1731 case ficlInstructionFDup: 1732 CHECK_FLOAT_STACK(1, 2); 1733 1734 FDUP: 1735 floatTop[1] = floatTop[0]; 1736 floatTop++; 1737 continue; 1738 1739 /* 1740 * Do float stack swap. 1741 * fswap ( r1 r2 -- r2 r1 ) 1742 */ 1743 case ficlInstructionFSwap: 1744 CHECK_FLOAT_STACK(2, 2); 1745 1746 c = floatTop[0]; 1747 floatTop[0] = floatTop[-1]; 1748 floatTop[-1] = c; 1749 continue; 1750 1751 /* 1752 * Do float stack 2drop. 1753 * f2drop ( r r -- ) 1754 */ 1755 case ficlInstructionF2Drop: 1756 CHECK_FLOAT_STACK(2, 0); 1757 1758 floatTop -= 2; 1759 continue; 1760 1761 /* 1762 * Do float stack 2dup. 1763 * f2dup ( r1 r2 -- r1 r2 r1 r2 ) 1764 */ 1765 case ficlInstructionF2Dup: 1766 CHECK_FLOAT_STACK(2, 4); 1767 1768 floatTop[1] = floatTop[-1]; 1769 floatTop[2] = *floatTop; 1770 floatTop += 2; 1771 continue; 1772 1773 /* 1774 * Do float stack over. 1775 * fover ( r1 r2 -- r1 r2 r1 ) 1776 */ 1777 case ficlInstructionFOver: 1778 CHECK_FLOAT_STACK(2, 3); 1779 1780 floatTop[1] = floatTop[-1]; 1781 floatTop++; 1782 continue; 1783 1784 /* 1785 * Do float stack 2over. 1786 * f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 ) 1787 */ 1788 case ficlInstructionF2Over: 1789 CHECK_FLOAT_STACK(4, 6); 1790 1791 floatTop[1] = floatTop[-2]; 1792 floatTop[2] = floatTop[-1]; 1793 floatTop += 2; 1794 continue; 1795 1796 /* 1797 * Do float stack pick. 1798 * fpick ( n -- r ) 1799 */ 1800 case ficlInstructionFPick: 1801 CHECK_STACK(1, 0); 1802 c = *dataTop--; 1803 CHECK_FLOAT_STACK(c.i+2, c.i+3); 1804 1805 floatTop[1] = floatTop[- c.i - 1]; 1806 continue; 1807 1808 /* 1809 * Do float stack rot. 1810 * frot ( r1 r2 r3 -- r2 r3 r1 ) 1811 */ 1812 case ficlInstructionFRot: 1813 i = 2; 1814 goto FROLL; 1815 1816 /* 1817 * Do float stack roll. 1818 * froll ( n -- ) 1819 */ 1820 case ficlInstructionFRoll: 1821 CHECK_STACK(1, 0); 1822 i = (dataTop--)->i; 1823 1824 if (i < 1) 1825 continue; 1826 1827 FROLL: 1828 CHECK_FLOAT_STACK(i+1, i+2); 1829 c = floatTop[-i]; 1830 memmove(floatTop - i, floatTop - (i - 1), 1831 i * sizeof (ficlCell)); 1832 *floatTop = c; 1833 1834 continue; 1835 1836 /* 1837 * Do float stack -rot. 1838 * f-rot ( r1 r2 r3 -- r3 r1 r2 ) 1839 */ 1840 case ficlInstructionFMinusRot: 1841 i = 2; 1842 goto FMINUSROLL; 1843 1844 1845 /* 1846 * Do float stack -roll. 1847 * f-roll ( n -- ) 1848 */ 1849 case ficlInstructionFMinusRoll: 1850 CHECK_STACK(1, 0); 1851 i = (dataTop--)->i; 1852 1853 if (i < 1) 1854 continue; 1855 1856 FMINUSROLL: 1857 CHECK_FLOAT_STACK(i+1, i+2); 1858 c = *floatTop; 1859 memmove(floatTop - (i - 1), floatTop - i, 1860 i * sizeof (ficlCell)); 1861 floatTop[-i] = c; 1862 1863 continue; 1864 1865 /* 1866 * Do float stack 2swap 1867 * f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 ) 1868 */ 1869 case ficlInstructionF2Swap: { 1870 ficlCell c2; 1871 CHECK_FLOAT_STACK(4, 4); 1872 1873 c = *floatTop; 1874 c2 = floatTop[-1]; 1875 1876 *floatTop = floatTop[-2]; 1877 floatTop[-1] = floatTop[-3]; 1878 1879 floatTop[-2] = c; 1880 floatTop[-3] = c2; 1881 continue; 1882 } 1883 1884 /* 1885 * Do float 0= comparison r = 0.0. 1886 * f0= ( r -- T/F ) 1887 */ 1888 case ficlInstructionF0Equals: 1889 CHECK_FLOAT_STACK(1, 0); 1890 CHECK_STACK(0, 1); 1891 1892 (++dataTop)->i = FICL_BOOL((floatTop--)->f != 0.0f); 1893 continue; 1894 1895 /* 1896 * Do float 0< comparison r < 0.0. 1897 * f0< ( r -- T/F ) 1898 */ 1899 case ficlInstructionF0Less: 1900 CHECK_FLOAT_STACK(1, 0); 1901 CHECK_STACK(0, 1); 1902 1903 (++dataTop)->i = FICL_BOOL((floatTop--)->f < 0.0f); 1904 continue; 1905 1906 /* 1907 * Do float 0> comparison r > 0.0. 1908 * f0> ( r -- T/F ) 1909 */ 1910 case ficlInstructionF0Greater: 1911 CHECK_FLOAT_STACK(1, 0); 1912 CHECK_STACK(0, 1); 1913 1914 (++dataTop)->i = FICL_BOOL((floatTop--)->f > 0.0f); 1915 continue; 1916 1917 /* 1918 * Do float = comparison r1 = r2. 1919 * f= ( r1 r2 -- T/F ) 1920 */ 1921 case ficlInstructionFEquals: 1922 CHECK_FLOAT_STACK(2, 0); 1923 CHECK_STACK(0, 1); 1924 1925 f = (floatTop--)->f; 1926 (++dataTop)->i = FICL_BOOL((floatTop--)->f == f); 1927 continue; 1928 1929 /* 1930 * Do float < comparison r1 < r2. 1931 * f< ( r1 r2 -- T/F ) 1932 */ 1933 case ficlInstructionFLess: 1934 CHECK_FLOAT_STACK(2, 0); 1935 CHECK_STACK(0, 1); 1936 1937 f = (floatTop--)->f; 1938 (++dataTop)->i = FICL_BOOL((floatTop--)->f < f); 1939 continue; 1940 1941 /* 1942 * Do float > comparison r1 > r2. 1943 * f> ( r1 r2 -- T/F ) 1944 */ 1945 case ficlInstructionFGreater: 1946 CHECK_FLOAT_STACK(2, 0); 1947 CHECK_STACK(0, 1); 1948 1949 f = (floatTop--)->f; 1950 (++dataTop)->i = FICL_BOOL((floatTop--)->f > f); 1951 continue; 1952 1953 1954 /* 1955 * Move float to param stack (assumes they both fit in a 1956 * single ficlCell) f>s 1957 */ 1958 case ficlInstructionFFrom: 1959 CHECK_FLOAT_STACK(1, 0); 1960 CHECK_STACK(0, 1); 1961 1962 *++dataTop = *floatTop--; 1963 continue; 1964 1965 case ficlInstructionToF: 1966 CHECK_FLOAT_STACK(0, 1); 1967 CHECK_STACK(1, 0); 1968 1969 *++floatTop = *dataTop--; 1970 continue; 1971 1972 #endif /* FICL_WANT_FLOAT */ 1973 1974 /* 1975 * c o l o n P a r e n 1976 * This is the code that executes a colon definition. It 1977 * assumes that the virtual machine is running a "next" loop 1978 * (See the vm.c for its implementation of member function 1979 * vmExecute()). The colon code simply copies the address of 1980 * the first word in the list of words to interpret into IP 1981 * after saving its old value. When we return to the "next" 1982 * loop, the virtual machine will call the code for each 1983 * word in turn. 1984 */ 1985 case ficlInstructionColonParen: 1986 (++returnTop)->p = (void *)ip; 1987 ip = (ficlInstruction *)(fw->param); 1988 continue; 1989 1990 case ficlInstructionCreateParen: 1991 CHECK_STACK(0, 1); 1992 (++dataTop)->p = (fw->param + 1); 1993 continue; 1994 1995 case ficlInstructionVariableParen: 1996 CHECK_STACK(0, 1); 1997 (++dataTop)->p = fw->param; 1998 continue; 1999 2000 /* 2001 * c o n s t a n t P a r e n 2002 * This is the run-time code for "constant". It simply returns 2003 * the contents of its word's first data ficlCell. 2004 */ 2005 2006 #if FICL_WANT_FLOAT 2007 case ficlInstructionF2ConstantParen: 2008 CHECK_FLOAT_STACK(0, 2); 2009 FLOAT_PUSH_CELL_POINTER_DOUBLE(fw->param); 2010 2011 case ficlInstructionFConstantParen: 2012 CHECK_FLOAT_STACK(0, 1); 2013 FLOAT_PUSH_CELL_POINTER(fw->param); 2014 #endif /* FICL_WANT_FLOAT */ 2015 2016 case ficlInstruction2ConstantParen: 2017 CHECK_STACK(0, 2); 2018 PUSH_CELL_POINTER_DOUBLE(fw->param); 2019 2020 case ficlInstructionConstantParen: 2021 CHECK_STACK(0, 1); 2022 PUSH_CELL_POINTER(fw->param); 2023 2024 #if FICL_WANT_USER 2025 case ficlInstructionUserParen: { 2026 ficlInteger i = fw->param[0].i; 2027 (++dataTop)->p = &vm->user[i]; 2028 continue; 2029 } 2030 #endif 2031 2032 default: 2033 /* 2034 * Clever hack, or evil coding? You be the judge. 2035 * 2036 * If the word we've been asked to execute is in fact 2037 * an *instruction*, we grab the instruction, stow it 2038 * in "i" (our local cache of *ip), and *jump* to the 2039 * top of the switch statement. --lch 2040 */ 2041 if (((ficlInstruction)fw->code > 2042 ficlInstructionInvalid) && 2043 ((ficlInstruction)fw->code < ficlInstructionLast)) { 2044 instruction = (ficlInstruction)fw->code; 2045 goto AGAIN; 2046 } 2047 2048 LOCAL_VARIABLE_SPILL; 2049 (vm)->runningWord = fw; 2050 fw->code(vm); 2051 LOCAL_VARIABLE_REFILL; 2052 continue; 2053 } 2054 } 2055 2056 LOCAL_VARIABLE_SPILL; 2057 vm->exceptionHandler = oldExceptionHandler; 2058 } 2059 2060 /* 2061 * v m G e t D i c t 2062 * Returns the address dictionary for this VM's system 2063 */ 2064 ficlDictionary * 2065 ficlVmGetDictionary(ficlVm *vm) 2066 { 2067 FICL_VM_ASSERT(vm, vm); 2068 return (vm->callback.system->dictionary); 2069 } 2070 2071 /* 2072 * v m G e t S t r i n g 2073 * Parses a string out of the VM input buffer and copies up to the first 2074 * FICL_COUNTED_STRING_MAX characters to the supplied destination buffer, a 2075 * ficlCountedString. The destination string is NULL terminated. 2076 * 2077 * Returns the address of the first unused character in the dest buffer. 2078 */ 2079 char * 2080 ficlVmGetString(ficlVm *vm, ficlCountedString *counted, char delimiter) 2081 { 2082 ficlString s = ficlVmParseStringEx(vm, delimiter, 0); 2083 2084 if (FICL_STRING_GET_LENGTH(s) > FICL_COUNTED_STRING_MAX) { 2085 FICL_STRING_SET_LENGTH(s, FICL_COUNTED_STRING_MAX); 2086 } 2087 2088 strncpy(counted->text, FICL_STRING_GET_POINTER(s), 2089 FICL_STRING_GET_LENGTH(s)); 2090 counted->text[FICL_STRING_GET_LENGTH(s)] = '\0'; 2091 counted->length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s); 2092 2093 return (counted->text + FICL_STRING_GET_LENGTH(s) + 1); 2094 } 2095 2096 /* 2097 * v m G e t W o r d 2098 * vmGetWord calls vmGetWord0 repeatedly until it gets a string with 2099 * non-zero length. 2100 */ 2101 ficlString 2102 ficlVmGetWord(ficlVm *vm) 2103 { 2104 ficlString s = ficlVmGetWord0(vm); 2105 2106 if (FICL_STRING_GET_LENGTH(s) == 0) { 2107 ficlVmThrow(vm, FICL_VM_STATUS_RESTART); 2108 } 2109 2110 return (s); 2111 } 2112 2113 /* 2114 * v m G e t W o r d 0 2115 * Skip leading whitespace and parse a space delimited word from the tib. 2116 * Returns the start address and length of the word. Updates the tib 2117 * to reflect characters consumed, including the trailing delimiter. 2118 * If there's nothing of interest in the tib, returns zero. This function 2119 * does not use vmParseString because it uses isspace() rather than a 2120 * single delimiter character. 2121 */ 2122 ficlString 2123 ficlVmGetWord0(ficlVm *vm) 2124 { 2125 char *trace = ficlVmGetInBuf(vm); 2126 char *stop = ficlVmGetInBufEnd(vm); 2127 ficlString s; 2128 ficlUnsigned length = 0; 2129 char c = 0; 2130 2131 trace = ficlStringSkipSpace(trace, stop); 2132 FICL_STRING_SET_POINTER(s, trace); 2133 2134 /* Please leave this loop this way; it makes Purify happier. --lch */ 2135 for (;;) { 2136 if (trace == stop) 2137 break; 2138 c = *trace; 2139 if (isspace((unsigned char)c)) 2140 break; 2141 length++; 2142 trace++; 2143 } 2144 2145 FICL_STRING_SET_LENGTH(s, length); 2146 2147 /* skip one trailing delimiter */ 2148 if ((trace != stop) && isspace((unsigned char)c)) 2149 trace++; 2150 2151 ficlVmUpdateTib(vm, trace); 2152 2153 return (s); 2154 } 2155 2156 /* 2157 * v m G e t W o r d T o P a d 2158 * Does vmGetWord and copies the result to the pad as a NULL terminated 2159 * string. Returns the length of the string. If the string is too long 2160 * to fit in the pad, it is truncated. 2161 */ 2162 int 2163 ficlVmGetWordToPad(ficlVm *vm) 2164 { 2165 ficlString s; 2166 char *pad = (char *)vm->pad; 2167 s = ficlVmGetWord(vm); 2168 2169 if (FICL_STRING_GET_LENGTH(s) > FICL_PAD_SIZE) 2170 FICL_STRING_SET_LENGTH(s, FICL_PAD_SIZE); 2171 2172 strncpy(pad, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s)); 2173 pad[FICL_STRING_GET_LENGTH(s)] = '\0'; 2174 return ((int)(FICL_STRING_GET_LENGTH(s))); 2175 } 2176 2177 /* 2178 * v m P a r s e S t r i n g 2179 * Parses a string out of the input buffer using the delimiter 2180 * specified. Skips leading delimiters, marks the start of the string, 2181 * and counts characters to the next delimiter it encounters. It then 2182 * updates the vm input buffer to consume all these chars, including the 2183 * trailing delimiter. 2184 * Returns the address and length of the parsed string, not including the 2185 * trailing delimiter. 2186 */ 2187 ficlString 2188 ficlVmParseString(ficlVm *vm, char delimiter) 2189 { 2190 return (ficlVmParseStringEx(vm, delimiter, 1)); 2191 } 2192 2193 ficlString 2194 ficlVmParseStringEx(ficlVm *vm, char delimiter, char skipLeadingDelimiters) 2195 { 2196 ficlString s; 2197 char *trace = ficlVmGetInBuf(vm); 2198 char *stop = ficlVmGetInBufEnd(vm); 2199 char c; 2200 2201 if (skipLeadingDelimiters) { 2202 while ((trace != stop) && (*trace == delimiter)) 2203 trace++; 2204 } 2205 2206 FICL_STRING_SET_POINTER(s, trace); /* mark start of text */ 2207 2208 /* find next delimiter or end of line */ 2209 for (c = *trace; 2210 (trace != stop) && (c != delimiter) && (c != '\r') && (c != '\n'); 2211 c = *++trace) { 2212 ; 2213 } 2214 2215 /* set length of result */ 2216 FICL_STRING_SET_LENGTH(s, trace - FICL_STRING_GET_POINTER(s)); 2217 2218 /* gobble trailing delimiter */ 2219 if ((trace != stop) && (*trace == delimiter)) 2220 trace++; 2221 2222 ficlVmUpdateTib(vm, trace); 2223 return (s); 2224 } 2225 2226 2227 /* 2228 * v m P o p 2229 */ 2230 ficlCell 2231 ficlVmPop(ficlVm *vm) 2232 { 2233 return (ficlStackPop(vm->dataStack)); 2234 } 2235 2236 /* 2237 * v m P u s h 2238 */ 2239 void 2240 ficlVmPush(ficlVm *vm, ficlCell c) 2241 { 2242 ficlStackPush(vm->dataStack, c); 2243 } 2244 2245 /* 2246 * v m P o p I P 2247 */ 2248 void 2249 ficlVmPopIP(ficlVm *vm) 2250 { 2251 vm->ip = (ficlIp)(ficlStackPopPointer(vm->returnStack)); 2252 } 2253 2254 /* 2255 * v m P u s h I P 2256 */ 2257 void 2258 ficlVmPushIP(ficlVm *vm, ficlIp newIP) 2259 { 2260 ficlStackPushPointer(vm->returnStack, (void *)vm->ip); 2261 vm->ip = newIP; 2262 } 2263 2264 /* 2265 * v m P u s h T i b 2266 * Binds the specified input string to the VM and clears >IN (the index) 2267 */ 2268 void 2269 ficlVmPushTib(ficlVm *vm, char *text, ficlInteger nChars, ficlTIB *pSaveTib) 2270 { 2271 if (pSaveTib) { 2272 *pSaveTib = vm->tib; 2273 } 2274 vm->tib.text = text; 2275 vm->tib.end = text + nChars; 2276 vm->tib.index = 0; 2277 } 2278 2279 void 2280 ficlVmPopTib(ficlVm *vm, ficlTIB *pTib) 2281 { 2282 if (pTib) { 2283 vm->tib = *pTib; 2284 } 2285 } 2286 2287 /* 2288 * v m Q u i t 2289 */ 2290 void 2291 ficlVmQuit(ficlVm *vm) 2292 { 2293 ficlStackReset(vm->returnStack); 2294 vm->restart = 0; 2295 vm->ip = NULL; 2296 vm->runningWord = NULL; 2297 vm->state = FICL_VM_STATE_INTERPRET; 2298 vm->tib.text = NULL; 2299 vm->tib.end = NULL; 2300 vm->tib.index = 0; 2301 vm->pad[0] = '\0'; 2302 vm->sourceId.i = 0; 2303 } 2304 2305 /* 2306 * v m R e s e t 2307 */ 2308 void 2309 ficlVmReset(ficlVm *vm) 2310 { 2311 ficlVmQuit(vm); 2312 ficlStackReset(vm->dataStack); 2313 #if FICL_WANT_FLOAT 2314 ficlStackReset(vm->floatStack); 2315 #endif 2316 vm->base = 10; 2317 } 2318 2319 /* 2320 * v m S e t T e x t O u t 2321 * Binds the specified output callback to the vm. If you pass NULL, 2322 * binds the default output function (ficlTextOut) 2323 */ 2324 void 2325 ficlVmSetTextOut(ficlVm *vm, ficlOutputFunction textOut) 2326 { 2327 vm->callback.textOut = textOut; 2328 } 2329 2330 void 2331 ficlVmTextOut(ficlVm *vm, char *text) 2332 { 2333 ficlCallbackTextOut((ficlCallback *)vm, text); 2334 } 2335 2336 2337 void 2338 ficlVmErrorOut(ficlVm *vm, char *text) 2339 { 2340 ficlCallbackErrorOut((ficlCallback *)vm, text); 2341 } 2342 2343 2344 /* 2345 * v m T h r o w 2346 */ 2347 void 2348 ficlVmThrow(ficlVm *vm, int except) 2349 { 2350 if (vm->exceptionHandler) 2351 longjmp(*(vm->exceptionHandler), except); 2352 } 2353 2354 void 2355 ficlVmThrowError(ficlVm *vm, char *fmt, ...) 2356 { 2357 va_list list; 2358 2359 va_start(list, fmt); 2360 vsprintf(vm->pad, fmt, list); 2361 va_end(list); 2362 strcat(vm->pad, "\n"); 2363 2364 ficlVmErrorOut(vm, vm->pad); 2365 longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT); 2366 } 2367 2368 void 2369 ficlVmThrowErrorVararg(ficlVm *vm, char *fmt, va_list list) 2370 { 2371 vsprintf(vm->pad, fmt, list); 2372 /* 2373 * well, we can try anyway, we're certainly not 2374 * returning to our caller! 2375 */ 2376 va_end(list); 2377 strcat(vm->pad, "\n"); 2378 2379 ficlVmErrorOut(vm, vm->pad); 2380 longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT); 2381 } 2382 2383 /* 2384 * f i c l E v a l u a t e 2385 * Wrapper for ficlExec() which sets SOURCE-ID to -1. 2386 */ 2387 int 2388 ficlVmEvaluate(ficlVm *vm, char *s) 2389 { 2390 int returnValue; 2391 ficlCell id = vm->sourceId; 2392 ficlString string; 2393 vm->sourceId.i = -1; 2394 FICL_STRING_SET_FROM_CSTRING(string, s); 2395 returnValue = ficlVmExecuteString(vm, string); 2396 vm->sourceId = id; 2397 return (returnValue); 2398 } 2399 2400 /* 2401 * f i c l E x e c 2402 * Evaluates a block of input text in the context of the 2403 * specified interpreter. Emits any requested output to the 2404 * interpreter's output function. 2405 * 2406 * Contains the "inner interpreter" code in a tight loop 2407 * 2408 * Returns one of the VM_XXXX codes defined in ficl.h: 2409 * VM_OUTOFTEXT is the normal exit condition 2410 * VM_ERREXIT means that the interpreter encountered a syntax error 2411 * and the vm has been reset to recover (some or all 2412 * of the text block got ignored 2413 * VM_USEREXIT means that the user executed the "bye" command 2414 * to shut down the interpreter. This would be a good 2415 * time to delete the vm, etc -- or you can ignore this 2416 * signal. 2417 */ 2418 int 2419 ficlVmExecuteString(ficlVm *vm, ficlString s) 2420 { 2421 ficlSystem *system = vm->callback.system; 2422 ficlDictionary *dictionary = system->dictionary; 2423 2424 int except; 2425 jmp_buf vmState; 2426 jmp_buf *oldState; 2427 ficlTIB saveficlTIB; 2428 2429 FICL_VM_ASSERT(vm, vm); 2430 FICL_VM_ASSERT(vm, system->interpreterLoop[0]); 2431 2432 ficlVmPushTib(vm, FICL_STRING_GET_POINTER(s), 2433 FICL_STRING_GET_LENGTH(s), &saveficlTIB); 2434 2435 /* 2436 * Save and restore VM's jmp_buf to enable nested calls to ficlExec 2437 */ 2438 oldState = vm->exceptionHandler; 2439 2440 /* This has to come before the setjmp! */ 2441 vm->exceptionHandler = &vmState; 2442 except = setjmp(vmState); 2443 2444 switch (except) { 2445 case 0: 2446 if (vm->restart) { 2447 vm->runningWord->code(vm); 2448 vm->restart = 0; 2449 } else { /* set VM up to interpret text */ 2450 ficlVmPushIP(vm, &(system->interpreterLoop[0])); 2451 } 2452 2453 ficlVmInnerLoop(vm, 0); 2454 break; 2455 2456 case FICL_VM_STATUS_RESTART: 2457 vm->restart = 1; 2458 except = FICL_VM_STATUS_OUT_OF_TEXT; 2459 break; 2460 2461 case FICL_VM_STATUS_OUT_OF_TEXT: 2462 ficlVmPopIP(vm); 2463 #if 0 /* we dont output prompt in loader */ 2464 if ((vm->state != FICL_VM_STATE_COMPILE) && 2465 (vm->sourceId.i == 0)) 2466 ficlVmTextOut(vm, FICL_PROMPT); 2467 #endif 2468 break; 2469 2470 case FICL_VM_STATUS_USER_EXIT: 2471 case FICL_VM_STATUS_INNER_EXIT: 2472 case FICL_VM_STATUS_BREAK: 2473 break; 2474 2475 case FICL_VM_STATUS_QUIT: 2476 if (vm->state == FICL_VM_STATE_COMPILE) { 2477 ficlDictionaryAbortDefinition(dictionary); 2478 #if FICL_WANT_LOCALS 2479 ficlDictionaryEmpty(system->locals, 2480 system->locals->forthWordlist->size); 2481 #endif 2482 } 2483 ficlVmQuit(vm); 2484 break; 2485 2486 case FICL_VM_STATUS_ERROR_EXIT: 2487 case FICL_VM_STATUS_ABORT: 2488 case FICL_VM_STATUS_ABORTQ: 2489 default: /* user defined exit code?? */ 2490 if (vm->state == FICL_VM_STATE_COMPILE) { 2491 ficlDictionaryAbortDefinition(dictionary); 2492 #if FICL_WANT_LOCALS 2493 ficlDictionaryEmpty(system->locals, 2494 system->locals->forthWordlist->size); 2495 #endif 2496 } 2497 ficlDictionaryResetSearchOrder(dictionary); 2498 ficlVmReset(vm); 2499 break; 2500 } 2501 2502 vm->exceptionHandler = oldState; 2503 ficlVmPopTib(vm, &saveficlTIB); 2504 return (except); 2505 } 2506 2507 /* 2508 * f i c l E x e c X T 2509 * Given a pointer to a ficlWord, push an inner interpreter and 2510 * execute the word to completion. This is in contrast with vmExecute, 2511 * which does not guarantee that the word will have completed when 2512 * the function returns (ie in the case of colon definitions, which 2513 * need an inner interpreter to finish) 2514 * 2515 * Returns one of the VM_XXXX exception codes listed in ficl.h. Normal 2516 * exit condition is VM_INNEREXIT, Ficl's private signal to exit the 2517 * inner loop under normal circumstances. If another code is thrown to 2518 * exit the loop, this function will re-throw it if it's nested under 2519 * itself or ficlExec. 2520 * 2521 * NOTE: this function is intended so that C code can execute ficlWords 2522 * given their address in the dictionary (xt). 2523 */ 2524 int 2525 ficlVmExecuteXT(ficlVm *vm, ficlWord *pWord) 2526 { 2527 int except; 2528 jmp_buf vmState; 2529 jmp_buf *oldState; 2530 ficlWord *oldRunningWord; 2531 2532 FICL_VM_ASSERT(vm, vm); 2533 FICL_VM_ASSERT(vm, vm->callback.system->exitInnerWord); 2534 2535 /* 2536 * Save the runningword so that RESTART behaves correctly 2537 * over nested calls. 2538 */ 2539 oldRunningWord = vm->runningWord; 2540 /* 2541 * Save and restore VM's jmp_buf to enable nested calls 2542 */ 2543 oldState = vm->exceptionHandler; 2544 /* This has to come before the setjmp! */ 2545 vm->exceptionHandler = &vmState; 2546 except = setjmp(vmState); 2547 2548 if (except) 2549 ficlVmPopIP(vm); 2550 else 2551 ficlVmPushIP(vm, &(vm->callback.system->exitInnerWord)); 2552 2553 switch (except) { 2554 case 0: 2555 ficlVmExecuteWord(vm, pWord); 2556 ficlVmInnerLoop(vm, 0); 2557 break; 2558 2559 case FICL_VM_STATUS_INNER_EXIT: 2560 case FICL_VM_STATUS_BREAK: 2561 break; 2562 2563 case FICL_VM_STATUS_RESTART: 2564 case FICL_VM_STATUS_OUT_OF_TEXT: 2565 case FICL_VM_STATUS_USER_EXIT: 2566 case FICL_VM_STATUS_QUIT: 2567 case FICL_VM_STATUS_ERROR_EXIT: 2568 case FICL_VM_STATUS_ABORT: 2569 case FICL_VM_STATUS_ABORTQ: 2570 default: /* user defined exit code?? */ 2571 if (oldState) { 2572 vm->exceptionHandler = oldState; 2573 ficlVmThrow(vm, except); 2574 } 2575 break; 2576 } 2577 2578 vm->exceptionHandler = oldState; 2579 vm->runningWord = oldRunningWord; 2580 return (except); 2581 } 2582 2583 /* 2584 * f i c l P a r s e N u m b e r 2585 * Attempts to convert the NULL terminated string in the VM's pad to 2586 * a number using the VM's current base. If successful, pushes the number 2587 * onto the param stack and returns FICL_TRUE. Otherwise, returns FICL_FALSE. 2588 * (jws 8/01) Trailing decimal point causes a zero ficlCell to be pushed. (See 2589 * the standard for DOUBLE wordset. 2590 */ 2591 int 2592 ficlVmParseNumber(ficlVm *vm, ficlString s) 2593 { 2594 ficlInteger accumulator = 0; 2595 char isNegative = 0; 2596 char isDouble = 0; 2597 unsigned base = vm->base; 2598 char *trace = FICL_STRING_GET_POINTER(s); 2599 ficlUnsigned8 length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s); 2600 unsigned c; 2601 unsigned digit; 2602 2603 if (length > 1) { 2604 switch (*trace) { 2605 case '-': 2606 trace++; 2607 length--; 2608 isNegative = 1; 2609 break; 2610 case '+': 2611 trace++; 2612 length--; 2613 isNegative = 0; 2614 break; 2615 default: 2616 break; 2617 } 2618 } 2619 2620 /* detect & remove trailing decimal */ 2621 if ((length > 0) && (trace[length - 1] == '.')) { 2622 isDouble = 1; 2623 length--; 2624 } 2625 2626 if (length == 0) /* detect "+", "-", ".", "+." etc */ 2627 return (0); /* false */ 2628 2629 while ((length--) && ((c = *trace++) != '\0')) { 2630 if (!isalnum(c)) 2631 return (0); /* false */ 2632 2633 digit = c - '0'; 2634 2635 if (digit > 9) 2636 digit = tolower(c) - 'a' + 10; 2637 2638 if (digit >= base) 2639 return (0); /* false */ 2640 2641 accumulator = accumulator * base + digit; 2642 } 2643 2644 if (isNegative) 2645 accumulator = -accumulator; 2646 2647 ficlStackPushInteger(vm->dataStack, accumulator); 2648 if (vm->state == FICL_VM_STATE_COMPILE) 2649 ficlPrimitiveLiteralIm(vm); 2650 2651 if (isDouble) { /* simple (required) DOUBLE support */ 2652 if (isNegative) 2653 ficlStackPushInteger(vm->dataStack, -1); 2654 else 2655 ficlStackPushInteger(vm->dataStack, 0); 2656 if (vm->state == FICL_VM_STATE_COMPILE) 2657 ficlPrimitiveLiteralIm(vm); 2658 } 2659 2660 return (1); /* true */ 2661 } 2662 2663 /* 2664 * d i c t C h e c k 2665 * Checks the dictionary for corruption and throws appropriate 2666 * errors. 2667 * Input: +n number of ADDRESS UNITS (not ficlCells) proposed to allot 2668 * -n number of ADDRESS UNITS proposed to de-allot 2669 * 0 just do a consistency check 2670 */ 2671 void 2672 ficlVmDictionarySimpleCheck(ficlVm *vm, ficlDictionary *dictionary, int cells) 2673 { 2674 #if FICL_ROBUST >= 1 2675 if ((cells >= 0) && 2676 (ficlDictionaryCellsAvailable(dictionary) * 2677 (int)sizeof (ficlCell) < cells)) { 2678 ficlVmThrowError(vm, "Error: dictionary full"); 2679 } 2680 2681 if ((cells <= 0) && 2682 (ficlDictionaryCellsUsed(dictionary) * 2683 (int)sizeof (ficlCell) < -cells)) { 2684 ficlVmThrowError(vm, "Error: dictionary underflow"); 2685 } 2686 #else /* FICL_ROBUST >= 1 */ 2687 FICL_IGNORE(vm); 2688 FICL_IGNORE(dictionary); 2689 FICL_IGNORE(cells); 2690 #endif /* FICL_ROBUST >= 1 */ 2691 } 2692 2693 void 2694 ficlVmDictionaryCheck(ficlVm *vm, ficlDictionary *dictionary, int cells) 2695 { 2696 #if FICL_ROBUST >= 1 2697 ficlVmDictionarySimpleCheck(vm, dictionary, cells); 2698 2699 if (dictionary->wordlistCount > FICL_MAX_WORDLISTS) { 2700 ficlDictionaryResetSearchOrder(dictionary); 2701 ficlVmThrowError(vm, "Error: search order overflow"); 2702 } else if (dictionary->wordlistCount < 0) { 2703 ficlDictionaryResetSearchOrder(dictionary); 2704 ficlVmThrowError(vm, "Error: search order underflow"); 2705 } 2706 #else /* FICL_ROBUST >= 1 */ 2707 FICL_IGNORE(vm); 2708 FICL_IGNORE(dictionary); 2709 FICL_IGNORE(cells); 2710 #endif /* FICL_ROBUST >= 1 */ 2711 } 2712 2713 void 2714 ficlVmDictionaryAllot(ficlVm *vm, ficlDictionary *dictionary, int n) 2715 { 2716 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, n); 2717 FICL_IGNORE(vm); 2718 ficlDictionaryAllot(dictionary, n); 2719 } 2720 2721 void 2722 ficlVmDictionaryAllotCells(ficlVm *vm, ficlDictionary *dictionary, int cells) 2723 { 2724 FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, cells); 2725 FICL_IGNORE(vm); 2726 ficlDictionaryAllotCells(dictionary, cells); 2727 } 2728 2729 /* 2730 * f i c l P a r s e W o r d 2731 * From the standard, section 3.4 2732 * b) Search the dictionary name space (see 3.4.2). If a definition name 2733 * matching the string is found: 2734 * 1.if interpreting, perform the interpretation semantics of the definition 2735 * (see 3.4.3.2), and continue at a); 2736 * 2.if compiling, perform the compilation semantics of the definition 2737 * (see 3.4.3.3), and continue at a). 2738 * 2739 * c) If a definition name matching the string is not found, attempt to 2740 * convert the string to a number (see 3.4.1.3). If successful: 2741 * 1.if interpreting, place the number on the data stack, and continue at a); 2742 * 2.if compiling, FICL_VM_STATE_COMPILE code that when executed will place 2743 * the number on the stack (see 6.1.1780 LITERAL), and continue at a); 2744 * 2745 * d) If unsuccessful, an ambiguous condition exists (see 3.4.4). 2746 * 2747 * (jws 4/01) Modified to be a ficlParseStep 2748 */ 2749 int 2750 ficlVmParseWord(ficlVm *vm, ficlString name) 2751 { 2752 ficlDictionary *dictionary = ficlVmGetDictionary(vm); 2753 ficlWord *tempFW; 2754 2755 FICL_VM_DICTIONARY_CHECK(vm, dictionary, 0); 2756 FICL_STACK_CHECK(vm->dataStack, 0, 0); 2757 2758 #if FICL_WANT_LOCALS 2759 if (vm->callback.system->localsCount > 0) { 2760 tempFW = ficlSystemLookupLocal(vm->callback.system, name); 2761 } else 2762 #endif 2763 tempFW = ficlDictionaryLookup(dictionary, name); 2764 2765 if (vm->state == FICL_VM_STATE_INTERPRET) { 2766 if (tempFW != NULL) { 2767 if (ficlWordIsCompileOnly(tempFW)) { 2768 ficlVmThrowError(vm, 2769 "Error: FICL_VM_STATE_COMPILE only!"); 2770 } 2771 2772 ficlVmExecuteWord(vm, tempFW); 2773 return (1); /* true */ 2774 } 2775 } else { /* (vm->state == FICL_VM_STATE_COMPILE) */ 2776 if (tempFW != NULL) { 2777 if (ficlWordIsImmediate(tempFW)) { 2778 ficlVmExecuteWord(vm, tempFW); 2779 } else { 2780 ficlCell c; 2781 c.p = tempFW; 2782 if (tempFW->flags & FICL_WORD_INSTRUCTION) 2783 ficlDictionaryAppendUnsigned(dictionary, 2784 (ficlInteger)tempFW->code); 2785 else 2786 ficlDictionaryAppendCell(dictionary, c); 2787 } 2788 return (1); /* true */ 2789 } 2790 } 2791 2792 return (0); /* false */ 2793 } 2794