ZMachine.java (42553B)
1 // Z Machine V3/V4/V5 Runtime 2 // 3 // Copyright 2002, Brian J. Swetland <swetland@frotz.net> 4 // Available under a BSD-Style License. Share and Enjoy. 5 6 package net.frotz.zruntime; 7 8 import java.io.PrintStream; 9 10 class ZFrame 11 { 12 ZFrame prev; 13 int pc; 14 int sp; 15 int bp; 16 int res; 17 int locals[]; 18 int llen; 19 20 ZFrame(int pc, int sp, int locals) { 21 this.pc = pc; 22 this.sp = sp; 23 this.bp = sp; 24 this.llen = locals + 1; 25 if(ZMachine.CONSERVE_MEMORY){ 26 this.locals = new int[17]; 27 } else { 28 this.locals = new int[locals + 1]; 29 } 30 } 31 } 32 33 public class ZMachine 34 { 35 class ZObject { 36 int Size; 37 int PropMax; 38 39 ZObject() { 40 Size = kObjSize; 41 PropMax = 31; 42 } 43 44 private static final int kObjParent = 4; 45 private static final int kObjSibling = 5; 46 private static final int kObjChild = 6; 47 private static final int kObjProps = 7; 48 private static final int kObjSize = 9; 49 50 boolean attr_test(int obj, int attr) { 51 return (U8(object_ptr + obj * kObjSize + (attr >> 3)) & (1 << (7 - (attr & 7)))) != 0; 52 } 53 void attr_set(int obj, int attr) { 54 mem[object_ptr + obj * kObjSize + (attr >> 3)] |= (1 << (7 - (attr & 7))); 55 } 56 void attr_clear(int obj, int attr){ 57 mem[object_ptr + obj * kObjSize + (attr >> 3)] &= ~(1 << (7 - (attr & 7))); 58 } 59 60 boolean inside(int a, int b) { 61 return U8(object_ptr + a * kObjSize + kObjParent) == b; 62 } 63 64 int sibling(int obj) { 65 return U8(object_ptr + obj * kObjSize + kObjSibling); 66 } 67 68 int parent(int obj) { 69 return U8(object_ptr + obj * kObjSize + kObjParent); 70 } 71 72 int child(int obj) { 73 return U8(object_ptr + obj * kObjSize + kObjChild); 74 } 75 76 void remove(int obj) { 77 int ptr = object_ptr + obj * kObjSize; 78 79 int x = U8(ptr + kObjParent); 80 81 if(x == 0) return; /* no parent */ 82 83 int n = U8(ptr + kObjSibling); 84 int c = U8(object_ptr + x * kObjSize + kObjChild); 85 if(c == obj){ 86 /* immediate child -- simple case */ 87 W8(object_ptr + x * kObjSize + kObjChild, n); 88 } else { 89 while(c != 0) { 90 c = object_ptr + c * kObjSize; 91 x = U8(c + kObjSibling); 92 if(x == obj) { 93 W8(c + kObjSibling, n); 94 break; 95 } else { 96 c = x; 97 } 98 } 99 } 100 W8(ptr + kObjSibling, 0); 101 W8(ptr + kObjParent, 0); 102 } 103 104 void insert(int obj, int dest) { 105 int objptr = object_ptr + obj * kObjSize; 106 int dstptr = object_ptr + dest * kObjSize; 107 108 if(U8(objptr + kObjParent) != 0) remove(obj); 109 110 /* the old child (if any) becomes our sibling */ 111 W8(objptr + kObjSibling, U8(dstptr + kObjChild)); 112 /* we become the new eldest child */ 113 W8(dstptr + kObjChild, obj); 114 /* and update our parent ptr */ 115 W8(objptr + kObjParent, dest); 116 } 117 118 void print(int obj) { 119 obj = U16(object_ptr + obj * kObjSize + kObjProps); 120 if(U8(obj) != 0){ 121 ZSCII(obj + 1); 122 Print(zscii_buf,zscii_ptr); 123 } 124 } 125 126 String name(int _obj) { 127 int obj = U16(object_ptr + _obj * kObjSize + kObjProps); 128 if(U8(obj) != 0){ 129 ZSCII(obj + 1); 130 return new String(zscii_buf,0,zscii_ptr); 131 } else { 132 return new String("#"+_obj); 133 } 134 } 135 136 int status(){ 137 return U16(object_ptr + Load(16) * kObjSize + kObjProps); 138 } 139 140 141 142 int get_prop_len(int addr) { 143 if(addr == 0){ 144 return 0; 145 } else { 146 return (U8(addr - 1) >> 5) + 1; 147 } 148 } 149 150 int get_prop_addr(int obj, int prop_id) { 151 int i, prop,sz; 152 153 obj = object_ptr + kObjSize * obj; 154 prop = U16(obj + kObjProps); 155 prop += U8(prop) * 2 + 1; /* skip name */ 156 157 while((sz = U8(prop)) != 0){ 158 if((sz & 0x1f) == prop_id) { 159 return prop + 1; 160 } else { 161 prop += (sz >> 5) + 2; 162 } 163 } 164 165 return 0; 166 } 167 168 int get_prop_next(int obj, int prop_id) { 169 int i, prop,sz; 170 171 obj = object_ptr + kObjSize * obj; 172 prop = U16(obj + kObjProps); 173 prop += U8(prop) * 2 + 1; /* skip name */ 174 175 if(LOGGING) LOG("get prop next " + obj + " " + prop_id); 176 177 if(prop_id == 0) { 178 /* get first prop */ 179 return U8(prop) & 0x1f; 180 } else { 181 while((sz = U8(prop)) != 0){ 182 if((sz & 0x1f) == prop_id) { 183 return U8(prop + (sz >> 5) + 2) & 0x1f; 184 } else { 185 prop += (sz >> 5) + 2; 186 } 187 } 188 } 189 190 return 0; 191 } 192 193 int get_prop(int obj, int prop_id) { 194 int i, prop,sz; 195 196 if(LOGGING) LOG("get_prop "+obj+" #"+prop_id); 197 198 obj = object_ptr + kObjSize * obj; 199 prop = U16(obj + kObjProps); 200 prop += U8(prop) * 2 + 1; /* skip name */ 201 202 while((sz = U8(prop)) != 0){ 203 if((sz & 0x1f) == prop_id) { 204 switch(sz >> 5){ 205 case 0: 206 return U8(prop + 1); 207 case 1: 208 return U16(prop + 1); 209 default: 210 DIE("property not byte or word sized"); 211 } 212 } else { 213 prop += (sz >> 5) + 2; 214 } 215 } 216 217 return U16(default_prop_ptr + prop_id * 2); 218 } 219 220 void put_prop(int obj, int prop_id, int val) { 221 int i, prop,sz; 222 223 if(LOGGING) LOG("put_prop "+obj+" #"+prop_id+" = 0x"+HEX(val)); 224 225 obj = object_ptr + kObjSize * obj; 226 prop = U16(obj + kObjProps); 227 prop += U8(prop) * 2 + 1; /* skip name */ 228 229 while((sz = U8(prop)) != 0){ 230 if((sz & 0x1f) == prop_id) { 231 switch(sz >> 5){ 232 case 0: 233 W8(prop + 1, val); 234 return; 235 case 1: 236 W16(prop + 1, val); 237 return; 238 default: 239 DIE("property not byte or word sized"); 240 } 241 } else { 242 prop += (sz >> 5) + 2; 243 } 244 } 245 246 DIE("property not found"); 247 } 248 249 /* void dump(int obj) { 250 LOG("dumpobj("+obj+")"); 251 obj = object_ptr + kObjSize * obj; 252 int props = U16(obj+kObjProps); 253 int sz; 254 String s; 255 int i; 256 257 LOG("@"+HEX(obj)+":"+ 258 " p:"+U8(obj+kObjParent)+" s:"+U8(obj+kObjSibling)+" c:"+U8(obj+kObjChild)+ 259 " pr:"+HEX(props)); 260 LOG("name: "+(U8(props) == 0 ? "*none*" : ZSTRING(props + 1))); 261 262 s = "attr:"; 263 for(i = 0; i < 32; i++){ 264 if(Attr(obj, i)) s = s + " A"+i; 265 } 266 LOG(s); 267 props += U8(props) * 2 + 1; 268 269 while((sz = U8(props)) != 0) { 270 int id = sz & 0x1f; 271 sz = (sz >> 5) + 1; 272 s = "P"+id+":"; 273 for(i = 0; i < sz; i++) s = s + " " + HEX(U8(props + i + 1)); 274 LOG(s); 275 props += sz + 1; 276 } 277 } 278 */ 279 } 280 281 class ZObjectWide extends ZObject { 282 ZObjectWide() { 283 Size = kObjSize; 284 PropMax = 63; 285 } 286 287 private static final int kObjParent = 6; 288 private static final int kObjSibling = 8; 289 private static final int kObjChild = 10; 290 private static final int kObjProps = 12; 291 private static final int kObjSize = 14; 292 293 boolean attr_test(int obj, int attr) { 294 return (U8(object_ptr + obj * kObjSize + (attr >> 3)) & (1 << (7 - (attr & 7)))) != 0; 295 } 296 void attr_set(int obj, int attr) { 297 mem[object_ptr + obj * kObjSize + (attr >> 3)] |= (1 << (7 - (attr & 7))); 298 } 299 void attr_clear(int obj, int attr){ 300 mem[object_ptr + obj * kObjSize + (attr >> 3)] &= ~(1 << (7 - (attr & 7))); 301 } 302 303 boolean inside(int a, int b) { 304 return U16(object_ptr + a * kObjSize + kObjParent) == b; 305 } 306 307 int sibling(int obj) { 308 return U16(object_ptr + obj * kObjSize + kObjSibling); 309 } 310 311 int parent(int obj) { 312 return U16(object_ptr + obj * kObjSize + kObjParent); 313 } 314 315 int child(int obj) { 316 return U16(object_ptr + obj * kObjSize + kObjChild); 317 } 318 319 void remove(int obj) { 320 int ptr = object_ptr + obj * kObjSize; 321 322 int x = U16(ptr + kObjParent); 323 324 if(x == 0) return; /* no parent */ 325 326 int n = U16(ptr + kObjSibling); 327 int c = U16(object_ptr + x * kObjSize + kObjChild); 328 if(c == obj){ 329 /* immediate child -- simple case */ 330 W16(object_ptr + x * kObjSize + kObjChild, n); 331 } else { 332 while(c != 0) { 333 c = object_ptr + c * kObjSize; 334 x = U16(c + kObjSibling); 335 if(x == obj) { 336 W16(c + kObjSibling, n); 337 break; 338 } else { 339 c = x; 340 } 341 } 342 } 343 W16(ptr + kObjSibling, 0); 344 W16(ptr + kObjParent, 0); 345 } 346 347 void insert(int obj, int dest) { 348 int objptr = object_ptr + obj * kObjSize; 349 int dstptr = object_ptr + dest * kObjSize; 350 351 if(U16(objptr + kObjParent) != 0) remove(obj); 352 353 /* the old child (if any) becomes our sibling */ 354 W16(objptr + kObjSibling, U16(dstptr + kObjChild)); 355 /* we become the new eldest child */ 356 W16(dstptr + kObjChild, obj); 357 /* and update our parent ptr */ 358 W16(objptr + kObjParent, dest); 359 } 360 361 void print(int obj) { 362 obj = U16(object_ptr + obj * kObjSize + kObjProps); 363 if(U8(obj) != 0){ 364 ZSCII(obj + 1); 365 Print(zscii_buf,zscii_ptr); 366 } 367 } 368 369 String name(int _obj) { 370 int obj = U16(object_ptr + _obj * kObjSize + kObjProps); 371 if(U8(obj) != 0){ 372 ZSCII(obj + 1); 373 return new String(zscii_buf,0,zscii_ptr); 374 } else { 375 return new String("#"+_obj); 376 } 377 } 378 379 int status(){ 380 //XXX ??? 381 return U16(object_ptr + Load(16) * kObjSize + kObjProps); 382 } 383 384 385 // 1xnnnnnn 1xssssss 386 // 0snnnnnn 387 int get_prop_len(int addr) { 388 if(addr == 0){ 389 return 0; 390 } else { 391 int sz = U8(addr - 1); 392 if((sz & 0x80) == 0){ // one-byte of size/number data 393 if((sz & 0x40) == 0){ 394 return 1; 395 } else { 396 return 2; 397 } 398 } else { // two bytes of size/number data 399 sz = sz & 63; 400 if(sz == 0){ 401 return 64; 402 } else { 403 return sz; 404 } 405 } 406 } 407 } 408 409 int last_sz; 410 411 int get_prop_addr(int obj, int prop_id) { 412 int id, prop,sz; 413 414 obj = object_ptr + kObjSize * obj; 415 prop = U16(obj + kObjProps); 416 prop += U8(prop) * 2 + 1; /* skip name */ 417 418 for(;;){ 419 sz = U8(prop); 420 id = sz & 0x3f; 421 if(sz == 0) break; 422 if(id == prop_id) { 423 if((sz & 0x80) == 0){ 424 last_sz = sz; 425 return prop + 1; 426 } else { 427 return prop + 2; 428 } 429 } else { 430 if((sz & 0x80) == 0){ 431 if((sz & 0x40) == 0){ 432 prop += 2; /* 1 hdr, 1 data */ 433 } else { 434 prop += 3; /* 1 hdr, 2 data */ 435 } 436 } else { 437 sz = U8(prop + 1) & 0x3f; 438 if(sz == 0) { 439 prop += (64 + 2); 440 } else { 441 prop += (sz + 2); 442 } 443 } 444 } 445 } 446 447 return 0; 448 } 449 450 int get_prop(int obj, int prop_id) { 451 if(LOGGING) LOG("get_prop "+obj+" #"+prop_id); 452 453 int addr = get_prop_addr(obj, prop_id); 454 int sz = last_sz; 455 456 if(addr == 0){ 457 return U16(default_prop_ptr + prop_id * 2); 458 } 459 460 if((sz & 0x80) == 0){ 461 if((sz & 0x40) == 0){ 462 return U8(addr); 463 } else { 464 return U16(addr); 465 } 466 } else { 467 DIE("property not byte or word sized"); 468 return 0; 469 } 470 471 } 472 473 void put_prop(int obj, int prop_id, int val) { 474 if(LOGGING) LOG("put_prop "+obj+" #"+prop_id+" = 0x"+HEX(val)); 475 476 int addr = get_prop_addr(obj, prop_id); 477 int sz = last_sz; 478 479 if(addr == 0){ 480 DIE("property not byte or word sized"); 481 } 482 483 if((sz & 0x80) == 0){ 484 if((sz & 0x40) == 0){ 485 W8(addr, val); 486 return; 487 } else { 488 W16(addr, val); 489 return; 490 } 491 } 492 } 493 494 int get_prop_next(int obj, int prop_id) { 495 if(LOGGING) LOG("get prop next " + obj + " #" + prop_id); 496 497 if(prop_id == 0){ 498 obj = object_ptr + kObjSize * obj; 499 int prop = U16(obj + kObjProps); 500 prop += U8(prop) * 2 + 1; /* skip name */ 501 return U8(prop) & 0x3f; 502 } 503 504 int addr = get_prop_addr(obj, prop_id); 505 int sz = last_sz; 506 507 if(addr == 0) DIE("get_prop_next on nonexistant property"); 508 509 if((sz & 0x80) == 0){ 510 if((sz & 0x40) == 0){ 511 addr += 1; 512 } else { 513 addr += 2; 514 } 515 } else { 516 sz = U8(addr-1) & 0x3f; 517 if(sz == 0) { 518 addr += 64; 519 } else { 520 addr += sz; 521 } 522 } 523 return U8(addr) & 0x3f; 524 } 525 } 526 527 String HEX(int n) { 528 return Integer.toHexString(n); 529 } 530 531 int U16(int addr) { 532 return ((mem[addr] & 0xff) << 8) | (mem[addr + 1] & 0xff); 533 } 534 535 int U32(int addr) { 536 return ((mem[addr] & 0xff) << 24) | 537 ((mem[addr + 1] & 0xff) << 16) | 538 ((mem[addr + 2] & 0xff) << 8) | 539 (mem[addr + 3] & 0xff); 540 } 541 542 void W16(int addr, int val) { 543 mem[addr] = (byte) (val >> 8); 544 mem[addr + 1] = (byte) val; 545 } 546 547 void W8(int addr, int val) { 548 mem[addr] = (byte) val; 549 } 550 551 int U8(int addr) { 552 return mem[addr] & 0xff; 553 } 554 555 int V8(int addr) { 556 int x = mem[addr] & 0xff; 557 558 switch(x){ 559 case 0x00: /* pop from stack */ 560 frame.sp--; 561 if(frame.sp < frame.bp) DIE("sp underflow"); 562 return stack[frame.sp]; 563 case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: 564 case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: 565 case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: 566 return frame.locals[x]; 567 568 default: 569 return U16(global_ptr + x * 2); 570 } 571 } 572 573 /* load from stack (0), locals (1-15), or globals (16-255) */ 574 int Load(int id) { 575 switch(id){ 576 case 0x00: /* pop from stack */ 577 frame.sp--; 578 if(frame.sp < frame.bp) DIE("sp underflow"); 579 return stack[frame.sp]; 580 case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: 581 case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: 582 case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: 583 return frame.locals[id]; 584 585 default: 586 return U16(global_ptr + id * 2); 587 } 588 } 589 590 String Name(int id) { 591 switch(id){ 592 case 0x00: 593 return "sp"; 594 case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: 595 case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: 596 case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: 597 return "l"+HEX(id-1); 598 default: 599 return "g"+HEX(id-16); 600 } 601 602 } 603 604 /* store to stack (0), locals (1-15), or globals (16-255) */ 605 void Store(int id, int val) { 606 if(LOGGING) LOG("store " + Name(id) + " (" + val + ")"); 607 if(false) if(TRACING && (id > 0)) { 608 System.out.println("[> 0x" + HEX(id) + " = " + (val&0xffff)); 609 } 610 611 switch(id){ 612 case 0x00: /* pop from stack */ 613 stack[frame.sp++] = val & 0xffff; 614 return; 615 case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: 616 case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: 617 case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: 618 frame.locals[id] = val & 0xffff; 619 return; 620 default: 621 if((id < 0) || (id > 255)) DIE("store out of range! " + id); 622 id = global_ptr + id * 2; 623 mem[id] = (byte) (val >> 8); 624 mem[id + 1] = (byte) val; 625 } 626 } 627 628 void Branch(boolean cond) { 629 int x = U8(frame.pc++); 630 631 /* branch on !cond bit */ 632 if((x & 0x80) == 0) cond = !cond; 633 634 if(cond) { 635 /* short branch bit */ 636 if((x & 0x40) != 0) { 637 x = x & 0x3f; 638 } else { 639 if((x & 0x20) == 0) { 640 x = x & 0x3f; 641 } else { 642 x = x | 0xffffffc0; 643 } 644 x = (x << 8) | U8(frame.pc++); 645 } 646 647 if(x == 0) { 648 Return(0); 649 } else if(x == 1) { 650 Return(1); 651 } else { 652 if(LOGGING) LOG("branch to 0x" + HEX(frame.pc + x - 2) + " by " + x); 653 frame.pc = frame.pc + x - 2; 654 } 655 } else { 656 /* gotta bump the pc if the short branch bit is clear 657 and we didn't take the branch... */ 658 if((x & 0x40) == 0) frame.pc++; 659 } 660 } 661 662 663 void LOG(String s) { 664 System.out.println(s); 665 } 666 667 void DIE(String s) { 668 int i; 669 Println("*** oops ***"); 670 for(i = 1; i < frame.llen; i++){ 671 Println("L"+i+"="+frame.locals[i]+" (0x"+HEX(frame.locals[i])+")"); 672 } 673 Println("Z: HALTED @"+HEX(frame.pc)+": "+s); 674 scr.exit(); 675 } 676 677 void BADOP(int op) { 678 DIE("unknown opcode 0x"+HEX(op)); 679 } 680 681 void BADOP(int op, int ex) { 682 DIE("unknown opcode 0x"+HEX(op)+", 0x"+HEX(ex)); 683 } 684 685 686 public int execute(ZFrame f) { 687 int op, num, t, x; 688 int a,b,c,d; 689 690 num = 0; 691 t = 0; 692 693 frame = f; 694 695 a = 0; 696 b = 0; 697 c = 0; 698 d = 0; 699 700 for(;;){ 701 f = frame; 702 703 int pc = f.pc; 704 op = U8(pc++); 705 706 /* decode arguments */ 707 switch(op >> 4){ 708 case 0x00: 709 case 0x01: /* 2OP, sconst, sconst */ 710 a = U8(pc++); 711 b = U8(pc++); 712 num = 2; 713 break; 714 case 0x02: 715 case 0x03: /* 2OP, sconst, var */ 716 a = U8(pc++); 717 b = V8(pc++); 718 num = 2; 719 break; 720 case 0x04: 721 case 0x05: /* 2OP, var, sconst */ 722 a = V8(pc++); 723 b = U8(pc++); 724 num = 2; 725 break; 726 case 0x06: 727 case 0x07: /* 2OP, var, var */ 728 a = V8(pc++); 729 b = V8(pc++); 730 num = 2; 731 break; 732 case 0x08: /* 1OP, lconst */ 733 a = U16(pc); 734 pc += 2; 735 num = 1; 736 break; 737 case 0x09: /* 1OP, sconst */ 738 a = U8(pc++); 739 num = 1; 740 break; 741 case 0x0a: /* 1OP, var */ 742 a = V8(pc++); 743 num = 1; 744 break; 745 case 0x0b: /* 0OP */ 746 if(op != 0xbe) break; 747 op = U8(pc++) + 0x100; 748 // read extended opcode and fall through 749 case 0x0c: 750 case 0x0d: /* 2OP, types-in-next */ 751 case 0x0e: /* VAR, types-in-next */ 752 case 0x0f: 753 x = U8(pc++); /* arg descriptor */ 754 num = 0; 755 while(num < 4) { 756 switch((x >> (6 - num * 2)) & 3){ 757 case 0: /* lconst */ 758 t = U16(pc); 759 pc += 2; 760 break; 761 case 1: /* sconst */ 762 t = U8(pc++); 763 break; 764 case 2: /* var */ 765 t = V8(pc++); 766 break; 767 case 3: /* none */ 768 t = -1; 769 break; 770 } 771 if(t == -1) break; 772 switch(num++){ 773 case 0: 774 a = t; 775 break; 776 case 1: 777 b = t; 778 break; 779 case 2: 780 c = t; 781 break; 782 case 3: 783 d = t; 784 break; 785 } 786 } 787 //XXX check argcounts... 788 } 789 790 if(TRACING) { 791 String ss = "["+HEX(f.pc)+"] " + HEX(op); 792 if(num > 0) ss = ss + " " + a; 793 if(num > 1) ss = ss + " " + b; 794 if(num > 2) ss = ss + " " + c; 795 if(num > 3) ss = ss + " " + d; 796 System.out.println(ss); 797 } 798 f.pc = pc; 799 800 801 /* decode instruction */ 802 switch(op){ 803 /* 2OP instructions --------------- */ 804 case 0x01: case 0x21: case 0x41: case 0x61: case 0xc1: // je a b ?(label) 805 switch(num){ 806 case 0: 807 case 1: 808 Branch(true); 809 break; 810 case 2: 811 Branch(a == b); 812 break; 813 case 3: 814 Branch((a == b) || (a == c)); 815 break; 816 case 4: 817 Branch((a == b) || (a == c) || (a == d)); 818 break; 819 } 820 break; 821 case 0x02: case 0x22: case 0x42: case 0x62: case 0xc2: // jl a b ?(label) 822 Branch(((short)a) < ((short)b)); 823 break; 824 case 0x03: case 0x23: case 0x43: case 0x63: case 0xc3: // jg a b ?(label) 825 Branch(((short)a) > ((short)b)); 826 break; 827 case 0x04: case 0x24: case 0x44: case 0x64: case 0xc4: // dec_chk (var) val ?(label) 828 t = (Load(a) - 1) & 0xffff; 829 Store(a, t); 830 Branch(((short)t) < ((short)b)); 831 break; 832 case 0x05: case 0x25: case 0x45: case 0x65: case 0xc5: // inc_chk (var) val ?(label) 833 t = (Load(a) + 1) & 0xffff; 834 Store(a, t); 835 Branch(((short)t) > ((short)b)); 836 break; 837 case 0x06: case 0x26: case 0x46: case 0x66: case 0xc6: // jin obj1 obj2 ?(label) 838 Branch(obj.inside(a,b)); 839 break; 840 case 0x07: case 0x27: case 0x47: case 0x67: case 0xc7: // test bitmap flags ?(label) 841 Branch((a & b) == b); 842 break; 843 case 0x08: case 0x28: case 0x48: case 0x68: case 0xc8: // or a b -> (result) 844 Store(U8(f.pc++), a | b); 845 break; 846 case 0x09: case 0x29: case 0x49: case 0x69: case 0xc9: // and a b -> (result) 847 Store(U8(f.pc++), a & b); 848 break; 849 case 0x0a: case 0x2a: case 0x4a: case 0x6a: case 0xca: // test_attr obj attr ?(label) 850 Branch(obj.attr_test(a, b)); 851 break; 852 case 0x0b: case 0x2b: case 0x4b: case 0x6b: case 0xcb: // set_attr obj attr 853 obj.attr_set(a, b); 854 break; 855 case 0x0c: case 0x2c: case 0x4c: case 0x6c: case 0xcc: // clear_attr obj attr 856 obj.attr_clear(a, b); 857 break; 858 case 0x0d: case 0x2d: case 0x4d: case 0x6d: case 0xcd: // store (var) value 859 Store(a,b); 860 break; 861 case 0x0e: case 0x2e: case 0x4e: case 0x6e: case 0xce: // insert_obj object dest 862 obj.insert(a, b); 863 break; 864 case 0x0f: case 0x2f: case 0x4f: case 0x6f: case 0xcf: // loadw array word-idx -> (result) 865 Store(U8(f.pc++),U16(a + b * 2)); 866 break; 867 case 0x10: case 0x30: case 0x50: case 0x70: case 0xd0: // loadb array byte-idx -> (result) 868 Store(U8(f.pc++),U8(a + b)); 869 break; 870 case 0x11: case 0x31: case 0x51: case 0x71: case 0xd1: // get_prop obj prop -> (result) 871 Store(U8(f.pc++),obj.get_prop(a, b)); 872 break; 873 case 0x12: case 0x32: case 0x52: case 0x72: case 0xd2: // get_prop_addr obj prop -> (result) 874 Store(U8(f.pc++),obj.get_prop_addr(a, b)); 875 break; 876 case 0x13: case 0x33: case 0x53: case 0x73: case 0xd3: // get_next_prop obj prop -> (result) 877 Store(U8(f.pc++),obj.get_prop_next(a, b)); 878 break; 879 case 0x14: case 0x34: case 0x54: case 0x74: case 0xd4: // add a b -> (result) 880 Store(U8(f.pc++), ((short)a) + ((short)b)); 881 break; 882 case 0x15: case 0x35: case 0x55: case 0x75: case 0xd5: // sub a b -> (result) 883 Store(U8(f.pc++), ((short)a) - ((short)b)); 884 break; 885 case 0x16: case 0x36: case 0x56: case 0x76: case 0xd6: // mul a b -> (result) 886 Store(U8(f.pc++), ((short)a) * ((short)b)); 887 break; 888 case 0x17: case 0x37: case 0x57: case 0x77: case 0xd7: // div a b -> (result) 889 Store(U8(f.pc++), ((short)a) / ((short)b)); 890 break; 891 case 0x18: case 0x38: case 0x58: case 0x78: case 0xd8: // mod a b -> (result) 892 Store(U8(f.pc++), ((short)a) % ((short)b)); 893 break; 894 case 0x19: case 0x39: case 0x59: case 0x79: case 0xd9: // call_2s a b -> (result) V4 895 if(notV4) BADOP(op); 896 op_call(num, a, b, 0, 0, U8(f.pc++)); 897 break; 898 case 0x1a: case 0x3a: case 0x5a: case 0x7a: case 0xda: // call_2n a b V5 899 if(notV5) BADOP(op); 900 op_call(num, a, b, 0, 0, -1); 901 break; 902 case 0x1b: case 0x3b: case 0x5b: case 0x7b: case 0xdb: // set_color fg bg V5 903 if(notV5) BADOP(op); 904 break; //TODO 905 // case 0x1c: case 0x3c: case 0x5c: case 0x7c: case 0xdc: // throw value frame V5/6 906 907 /* 1OP instructions --------------- */ 908 case 0x80: case 0x90: case 0xa0: // jz a ?(label) 909 Branch(a == 0); 910 break; 911 case 0x81: case 0x91: case 0xa1: // get_sibling object -> (result) ?(label) 912 Store(U8(f.pc++), t = obj.sibling(a)); 913 Branch(t != 0); 914 break; 915 case 0x82: case 0x92: case 0xa2: // get_child object -> (result) ?(label) 916 Store(U8(f.pc++), t = obj.child(a)); 917 Branch(t != 0); 918 break; 919 case 0x83: case 0x93: case 0xa3: // get_parent object -> (result) 920 Store(U8(f.pc++), obj.parent(a)); 921 break; 922 case 0x84: case 0x94: case 0xa4: // get_prop_len prop-addr -> (result) 923 Store(U8(f.pc++), obj.get_prop_len(a)); 924 break; 925 case 0x85: case 0x95: case 0xa5: // inc (variable) 926 Store(a, Load(a) + 1); 927 break; 928 case 0x86: case 0x96: case 0xa6: // dec (variable) 929 Store(a, Load(a) - 1); 930 break; 931 case 0x87: case 0x97: case 0xa7: // print_addr byte-addr-of-str 932 ZSCII(a); 933 Print(zscii_buf,zscii_ptr); 934 break; 935 case 0x88: case 0x98: case 0xa8: // call_1s a -> (result) V4 936 if(notV4) BADOP(op); 937 op_call(1, a, 0, 0, 0, U8(f.pc++)); 938 break; 939 case 0x89: case 0x99: case 0xa9: // remove_obj object 940 obj.remove(a); 941 break; 942 case 0x8a: case 0x9a: case 0xaa: // print_obj object 943 obj.print(a); 944 break; 945 case 0x8b: case 0x9b: case 0xab: // ret value 946 Return(a); 947 break; 948 case 0x8c: case 0x9c: case 0xac: // jump ?(label) 949 f.pc = f.pc - 2 + ((short)a); 950 break; 951 case 0x8d: case 0x9d: case 0xad: // print_paddr pack-addr-of-str 952 ZSCII(a * PACK); 953 Print(zscii_buf,zscii_ptr); 954 break; 955 case 0x8e: case 0x9e: case 0xae: // load (variable) -> (result) 956 Store(U8(f.pc++), Load(a)); 957 break; 958 case 0x8f: case 0x9f: case 0xaf: // not value -> (result) 959 if(V5){ 960 // call func 961 op_call(1, a, 0, 0, 0, -1); 962 break; 963 } else { 964 // not value -> (result) 965 Store(U8(f.pc++), ~a); 966 } 967 break; 968 /* 0OP instructions --------------- */ 969 case 0xb0: // rtrue 970 Return(1); 971 break; 972 case 0xb1: // rfalse 973 Return(0); 974 break; 975 case 0xb2: // print (literal-string) 976 frame.pc = ZSCII(frame.pc); 977 Print(zscii_buf, zscii_ptr); 978 break; 979 case 0xb3: // print_ret (literal-string) 980 frame.pc = ZSCII(frame.pc); 981 Print(zscii_buf, zscii_ptr); 982 scr.NewLine(); 983 Return(1); 984 break; 985 case 0xb4: // nop 986 break; 987 case 0xb5: 988 if(V5) BADOP(op); // illegal 989 if(V4) BADOP(op); // save -> (result) 990 Branch(scr.Save(save())); // save ?(label) 991 break; 992 case 0xb6: 993 if(V5) BADOP(op); // illegal 994 if(V4) BADOP(op); // restore -> (result) 995 Branch(restore(scr.Restore())); // restore ?(label) 996 break; 997 case 0xb7: // restart 998 restart(); 999 break; 1000 case 0xb8: // ret_popped 1001 Return(Load(0)); 1002 break; 1003 case 0xb9: // pop 1004 if(V5) BADOP(op); // catch -> (result) 1005 frame.sp--; 1006 if(frame.sp < frame.bp) DIE("stack underrun"); 1007 break; 1008 case 0xba: // quit 1009 restart(); 1010 break; 1011 case 0xbb: // new_line 1012 scr.NewLine(); 1013 break; 1014 case 0xbc: // show_status 1015 if(V3) UpdateStatus(); 1016 // illegal after 3, but zmachine spec says ignore it 1017 break; 1018 case 0xbd: // verify ?(label) 1019 //XXX check checksum for real :-) 1020 Branch(true); 1021 break; 1022 case 0xbf: // piracy ?(label) 1023 if(notV5) BADOP(op); 1024 Branch(true); 1025 break; 1026 /* VAR instructions --------------- */ 1027 case 0xe0: // call routine, arg0-3 -> (result) 1028 op_call(num, a, b, c, d, U8(f.pc++)); 1029 break; 1030 case 0xe1: // storew array word-index value 1031 if(num != 3) DIE("storew needs 3 args"); 1032 W16(a + b * 2, c); 1033 break; 1034 case 0xe2: // storeb array byte-index value 1035 if(num != 3) DIE("storeb needs 3 args"); 1036 W8(a + b, c); 1037 break; 1038 case 0xe3: // put-prop object property value 1039 if(num != 3) DIE("putprop needs 3 args"); 1040 obj.put_prop(a,b,c); 1041 break; 1042 case 0xe4: 1043 if(V3) {// sread text parse 1044 UpdateStatus(); 1045 readline(a, b); 1046 } else { 1047 if(num < 4) d = 0; 1048 if(num < 3) c = 0; 1049 // if((c != 0) || (d != 0)) DIE("timed read unsupported"); 1050 readline(a,b); 1051 Store(U8(f.pc++),10); 1052 } 1053 break; 1054 case 0xe5: // print_char out-char-code 1055 PrintChar(a); 1056 break; 1057 case 0xe6: // print_num value 1058 PrintNumber(a); 1059 break; 1060 case 0xe7: // random range -> (result) 1061 Store(U8(f.pc++), scr.Random(a)); 1062 break; 1063 case 0xe8: // push value 1064 Store(0, a); 1065 break; 1066 case 0xe9: // pull (variable) 1067 Store(a, Load(0)); 1068 break; 1069 case 0xea: // split_window lines 1070 scr.SplitWindow(a); 1071 break; 1072 case 0xeb: // set_window window 1073 scr.SetWindow(a); 1074 break; 1075 case 0xec: // call_vs2 0..7 -> (result) 1076 DIE("call_vs2"); 1077 case 0xed: // erase_window win V4 1078 scr.EraseWindow((short)a); 1079 break; 1080 case 0xee: //XXX erase line value V4 1081 System.err.println("ERASE LINE: "+a+","+b); 1082 break; 1083 case 0xef: // set_cursor line column V4 1084 scr.MoveCursor((short)b,(short)a); 1085 break; 1086 case 0xf0: //XXX get_cursor array V4/6 1087 BADOP(op); 1088 break; 1089 case 0xf1: //XXX set_text_style style V4 1090 break; 1091 case 0xf2: //XXX buffer_mode flag V4 1092 // System.err.println("BUFFER? "+(short)a); 1093 break; 1094 case 0xf3: //XXX output_stream number 1095 a = (short) a; 1096 if(a == 1) str_display = true; 1097 if(a == -1) str_display = false; 1098 break; 1099 case 0xf4: //XXX input_stream number 1100 break; 1101 case 0xf5: //XXX soundfx V5 1102 break; 1103 case 0xf6: // read_char 1 time routine -> (result) V4 1104 if((b != 0) || (c != 0)) DIE("timed read not supported"); 1105 int ch = scr.Read(); 1106 Store(U8(f.pc++), ch); 1107 break; 1108 case 0xf7: //XXX scan_table x table len form -> (result) V4 1109 BADOP(op); break; 1110 case 0xf8: // not value -> (result) V5/6 1111 Store(U8(f.pc++), ~a); 1112 break; 1113 case 0xf9: // call_vn 0..3 1114 if(notV5) BADOP(op); 1115 op_call(num, a, b, c, d, -1); 1116 break; 1117 case 0xfa: // call_vn2 0..7 V5 1118 BADOP(op); break; 1119 case 0xfb: //XXX tokenize text parse dict flag V5 1120 BADOP(op); break; 1121 case 0xfc: //XXX encode_text zscii len from coded V5 1122 BADOP(op); break; 1123 case 0xfd: //XXX copy_table first second size V5 1124 BADOP(op); break; 1125 case 0xfe: //XXX print_table zscii width height skip V5 1126 BADOP(op); break; 1127 case 0xff: //XXX check_arg_count argument-number V5 1128 BADOP(op); break; 1129 case 0x100: //XXX save table bytes name -> (result) V5 1130 if(notV5) BADOP(op); 1131 if(num != 0) DIE("extended save unsupported"); 1132 if(scr.Save(save())){ 1133 Store(U8(f.pc++), 1); 1134 } else { 1135 Store(U8(f.pc++), 0); 1136 } 1137 break; 1138 case 0x101: //XXX restore table bytes name -> (result) V5 1139 if(notV5) BADOP(op); 1140 if(num != 0) DIE("extended restore unsupported"); 1141 if(restore(scr.Restore())){ 1142 f = frame; 1143 Store(U8(f.pc++), 2); 1144 } else { 1145 f = frame; 1146 Store(U8(f.pc++), 0); 1147 } 1148 break; 1149 case 0x102: //XXX log_shift number places -> (result) V5 1150 BADOP(op); break; 1151 case 0x103: //XXX arith_shift number places -> (result) V5 1152 BADOP(op); break; 1153 case 0x104: //XXX set_font font -> (result) V5 1154 BADOP(op); break; 1155 case 0x109: // save_undo -> (result) V5 1156 if(notV5) BADOP(op); 1157 Store(U8(f.pc++), -1); 1158 break; // unsupported 1159 case 0x10a: // restore_undo -> (result) V5 1160 if(notV5) BADOP(op); 1161 Store(U8(f.pc++), 0); 1162 break; // ignored as unsupported 1163 default: 1164 BADOP(op); 1165 } 1166 } 1167 } 1168 1169 void op_call(int argc, int pc, int a1, int a2, int a3, int res) { 1170 int l, i; 1171 ZFrame f; 1172 1173 if(argc < 1) DIE("call with no target"); 1174 1175 pc *= PACK; /* unpack addr */ 1176 if(pc == 0) { 1177 /* weird special case for calling 0 */ 1178 if(res != -1) Store(res,0); 1179 return; 1180 } 1181 1182 l = U8(pc++); 1183 if((l < 0) || (l > 15)) DIE("bad local count " + l); 1184 1185 if(V5){ 1186 i = pc; 1187 } else { 1188 /* adjust for default arguments */ 1189 i = pc + l * 2; 1190 } 1191 if(CONSERVE_MEMORY){ 1192 if(freelist != null) { 1193 f = freelist; 1194 freelist = f.prev; 1195 f.pc = i; 1196 f.sp = frame.sp; 1197 f.bp = frame.sp; 1198 f.llen = l + 1; 1199 } else { 1200 f = new ZFrame(i, frame.sp, l); 1201 } 1202 } else { 1203 f = new ZFrame(i, frame.sp, l); 1204 } 1205 1206 l++; 1207 if(V5){ 1208 /* zero locals */ 1209 for(i = 1; i < l; i++){ 1210 f.locals[i] = 0; 1211 } 1212 } else { 1213 /* preload locals with default values */ 1214 for(i = 1; i < l; i ++){ 1215 f.locals[i] = U16(pc); 1216 pc += 2; 1217 } 1218 } 1219 1220 /* load arguments into locals */ 1221 if(argc > l) argc = l; 1222 if(argc > 1) { 1223 f.locals[1] = a1; 1224 if(argc > 2) { 1225 f.locals[2] = a2; 1226 if(argc > 3) { 1227 f.locals[3] = a3; 1228 } 1229 } 1230 } 1231 1232 if(LOGGING) { 1233 String s = "locals #"+(l-1); 1234 for(i = 1; i < l; i++){ 1235 s = s + ", "+f.locals[i]; 1236 } 1237 LOG(s); 1238 } 1239 f.res = res; 1240 f.prev = frame; 1241 frame = f; 1242 } 1243 1244 void Return(int value) { 1245 if(LOGGING) LOG("return " + value + " -> 0x" + HEX(frame.prev.pc)); 1246 ZFrame f = frame; 1247 frame = f.prev; 1248 if(f.res != -1) Store(f.res,value); 1249 if(CONSERVE_MEMORY) { 1250 f.prev = freelist; 1251 freelist = f.prev; 1252 } else { 1253 f.prev = null; 1254 } 1255 } 1256 1257 1258 void UpdateStatus() { 1259 int a = obj.status(); 1260 if(U8(a) != 0){ 1261 ZSCII(a + 1); 1262 } else { 1263 zscii_ptr = 0; 1264 } 1265 scr.SetStatus(zscii_buf, zscii_ptr); 1266 } 1267 1268 char line[] = new char[256]; 1269 1270 /* parsebuf: max, count, [ w:addr, len, off ] * count */ 1271 void readline(int text, int parse) { 1272 int len = scr.ReadLine(line); 1273 int max = U8(text); 1274 int pmax = U8(parse); 1275 int i,j,ptr,start; 1276 text++; 1277 1278 if(V3) max -= 1; 1279 1280 /* workaround for buggy early games per zspec1.0 */ 1281 if(pmax > 59) pmax = 59; 1282 1283 if(LOGGING) LOG("readline " + max + " " + U8(parse)); 1284 parse += 2; 1285 1286 /* copy linebuffer to zmemory, terminating with nul and 1287 truncating if needed */ 1288 if(len < max) max = len; 1289 if(V3){ 1290 for(i = 0; i < max; i++){ 1291 W8(text + i, line[i]); 1292 } 1293 W8(text + i, 0); 1294 } else { 1295 W8(text, max); 1296 for(i = 0; i < max; i++){ 1297 W8(text + i + 1, line[i]); 1298 } 1299 } 1300 /* tokenize */ 1301 ptr = 0; 1302 start = 0; 1303 i = 0; 1304 1305 while(ptr < max) { 1306 if((ptr-start) == 0) { 1307 if(line[ptr] == ' ') { 1308 start = ptr = ptr + 1; 1309 continue; 1310 } 1311 } 1312 /* check for term char special case */ 1313 1314 for(j = 0; j < special_count; j++) { 1315 if(line[ptr] == special_chars[j]){ 1316 if((ptr - start) > 0){ 1317 find_token(line, start, ptr - start, parse + i * 4); 1318 i++; 1319 } 1320 if(j != 0) { 1321 /* special #0 (space) does not get its own token */ 1322 find_token(line, ptr, 1, parse + i * 4); 1323 i++; 1324 } 1325 start = ptr + 1; 1326 break; 1327 } 1328 } 1329 1330 ptr++; 1331 } 1332 1333 if((ptr - start) > 0){ 1334 find_token(line, start, ptr - start, parse + i * 4); 1335 i++; 1336 } 1337 1338 W8(parse - 1, i); 1339 } 1340 1341 int token_workbuf[] = new int[16]; 1342 1343 void encode(char in[], int off, int len, int out[], int max) { 1344 int i; 1345 for(i = 0; i < max; i++) { 1346 if(len-- > 0) { 1347 int x = in[off++]; 1348 if((x >= 'a') && (x <= 'z')){ 1349 out[i] = x - ('a' - 6); 1350 continue; 1351 } 1352 if((x >= 'A') && (x <= 'Z')){ 1353 out[i] = x - ('A' - 6); 1354 continue; 1355 } 1356 if((x >= '0') && (x <= '9')){ 1357 out[i++] = 5; 1358 out[i] = x - ('0' - 8); 1359 continue; 1360 } 1361 switch(x) { 1362 case '.': x = 18; break; 1363 case ',': x = 19; break; 1364 case '!': x = 20; break; 1365 case '?': x = 21; break; 1366 case '_': x = 22; break; 1367 case '#': x = 23; break; 1368 case '\'': x = 24; break; 1369 case '"': x = 25; break; 1370 case '/': x = 26; break; 1371 case '\\': x = 27; break; 1372 case '-': x = 28; break; 1373 case ':': x = 29; break; 1374 case '(': x = 30; break; 1375 case ')': x = 31; break; 1376 default: 1377 LOG("unknown inchar " + x); 1378 x = 0; 1379 } 1380 out[i++] = 5; 1381 out[i] = x; 1382 } else { 1383 out[i] = 5; 1384 } 1385 } 1386 } 1387 1388 1389 void find_token(char data[], int off, int len, int parse) { 1390 int token = 0; 1391 int t, low, high, pos, i, n, KEYSZ; 1392 int buf[] = token_workbuf; 1393 if(LOGGING) LOG("W: '"+new String(data,off,len)+"' @"+off+","+len); 1394 1395 if(V3) KEYSZ = 4; 1396 else KEYSZ = 6; 1397 1398 encode(data, off, len, buf, KEYSZ*3/2); 1399 1400 t = (buf[0] << 10) | (buf[1] << 5) | buf[2]; 1401 buf[0] = (byte) (t >> 8); 1402 buf[1] = (byte) t; 1403 t = (buf[3] << 10) | (buf[4] << 5) | buf[5]; 1404 buf[2] = (byte) (t >> 8); 1405 buf[3] = (byte) t; 1406 if(V3){ 1407 buf[2] |= 0x80; 1408 } else { 1409 t = (buf[6] << 10) | (buf[7] << 5) | buf[8]; 1410 buf[4] = (byte) ((t >> 8) | 0x80); 1411 buf[5] = (byte) t; 1412 } 1413 1414 low = -1; 1415 high = dent_count; 1416 token = 0; 1417 1418 while((high - low) > 1){ 1419 pos = (high + low) / 2; 1420 t = dict_ptr + dent_len * pos; 1421 1422 for(n = 0, i = 0; i < KEYSZ; i++){ 1423 n = (buf[i]&0xff) - (mem[t+i]&0xff); 1424 if(n != 0) break; 1425 } 1426 1427 if(n == 0) { 1428 token = t; 1429 break; 1430 } else if(n > 0){ 1431 low = pos; 1432 } else { 1433 high = pos; 1434 } 1435 } 1436 1437 W16(parse, token); 1438 W8(parse + 2, len); 1439 W8(parse + 3, off + 1); 1440 } 1441 1442 boolean Attr(int obj, int num) { 1443 if((U8(obj + (num >> 3)) & (1 << (7 - (num & 7)))) == 0){ 1444 return false; 1445 } else { 1446 return true; 1447 } 1448 } 1449 1450 1451 String ZSTRING(int ptr) { 1452 ZSCII(ptr); 1453 return new String(zscii_buf,0,zscii_ptr); 1454 } 1455 1456 int ZSCII(int ptr) { 1457 zscii_ptr = 0; // at the beginning 1458 zscii_mode = 0; // shift mode A0 1459 return _ZSCII(ptr); 1460 } 1461 1462 int _ZSCII(int ptr) { 1463 boolean done; 1464 int a,b; 1465 1466 do { 1467 /* XAAAAABB BBBCCCCC */ 1468 a = mem[ptr++] & 0xff; 1469 b = mem[ptr++] & 0xff; 1470 ZCHAR((a >> 2) & 0x1f); 1471 ZCHAR(((a & 3) << 3) | (b >> 5)); 1472 ZCHAR(b & 0x1f); 1473 } while((a & 0x80) == 0); 1474 return ptr; 1475 } 1476 1477 void ZCHAR(int c) { 1478 switch(zscii_mode){ 1479 case 0: // A0 mode 1480 switch(c) { 1481 case 1: case 2: case 3: // next char is an abbrev 1482 zscii_mode = c + 2; 1483 return; 1484 case 4: // shift to A1 1485 zscii_mode = 1; 1486 return; 1487 case 5: // shift to A2 1488 zscii_mode = 2; 1489 return; 1490 } 1491 zscii_buf[zscii_ptr++] = zscii_map[zscii_mode][c]; 1492 return; 1493 case 1: // A1 mode 1494 switch(c) { 1495 case 1: case 2: case 3: // next char is an abbrev 1496 zscii_mode = c + 2; 1497 return; 1498 case 4: // shift to A1 1499 zscii_mode = 1; 1500 return; 1501 case 5: // shift to A2 1502 zscii_mode = 2; 1503 return; 1504 } 1505 zscii_buf[zscii_ptr++] = zscii_map[zscii_mode][c]; 1506 zscii_mode = 0; 1507 return; 1508 case 2: // A2 mode 1509 switch(c) { 1510 case 1: case 2: case 3: // next char is an abbrev 1511 zscii_mode = c + 2; 1512 return; 1513 case 4: // shift to A1 1514 zscii_mode = 1; 1515 return; 1516 case 5: // shift to A2 1517 zscii_mode = 2; 1518 return; 1519 case 6: 1520 zscii_mode = 6; 1521 return; 1522 } 1523 zscii_buf[zscii_ptr++] = zscii_map[zscii_mode][c]; 1524 zscii_mode = 0; 1525 return; 1526 case 3: // Abbrev modes 1527 case 4: 1528 case 5: 1529 c = ((zscii_mode - 3) * 32 + c); 1530 zscii_mode = 0; 1531 _ZSCII(U16(abbr_ptr + 2 * c) * 2); 1532 zscii_mode = 0; 1533 break; 1534 case 6: 1535 zscii_tmp = c; 1536 zscii_mode = 7; 1537 break; 1538 case 7: 1539 zscii_buf[zscii_ptr++] = (char) (c | ( zscii_tmp << 5 )); 1540 zscii_mode = 0; 1541 break; 1542 1543 } 1544 1545 } 1546 1547 char zscii_map[][] = { 1548 { ' ' ,0 ,0 ,0 ,0 ,0 ,'a' ,'b' ,'c' ,'d' ,'e' ,'f' ,'g' ,'h' ,'i' ,'j' , 1549 'k' ,'l' ,'m' ,'n' ,'o' ,'p' ,'q' ,'r' ,'s' ,'t' ,'u' ,'v' ,'w' ,'x' ,'y' ,'z' }, 1550 { 0 ,0 ,0 ,0 ,0 ,0 ,'A' ,'B' ,'C' ,'D' ,'E' ,'F' ,'G' ,'H' ,'I' ,'J' , 1551 'K' ,'L' ,'M' ,'N' ,'O' ,'P' ,'Q' ,'R' ,'S' ,'T' ,'U' ,'V' ,'W' ,'X' ,'Y' ,'Z' }, 1552 { 0 ,0 ,0 ,0 ,0 ,0 ,' ' ,'\n','0' ,'1' ,'2' ,'3' ,'4' ,'5' ,'6' ,'7', 1553 '8' ,'9' ,'.' ,',' ,'!' ,'?' ,'_' ,'#' ,'\'','"' ,'/' ,'\\','-' ,':' ,'(' ,')' } 1554 }; 1555 int zscii_ptr; 1556 int zscii_mode; 1557 int zscii_tmp; 1558 char zscii_buf[]; 1559 1560 public void run() { 1561 LOG("start"); 1562 int i; 1563 1564 //for(i = 0; i < (32*3); i++) LOG("'"+ZSTRING(U16(abbr_ptr + i * 2) * 2)+"'"); 1565 1566 //for(i = 1; i < 251; i++) dumpobj(i); 1567 try { 1568 execute(new ZFrame(U16(6), 0, 0)); 1569 System.err.println("*** DONE ***"); 1570 } catch (Throwable t) { 1571 t.printStackTrace(); 1572 DIE("jvm oops"); 1573 } 1574 } 1575 1576 void restart() { 1577 System.arraycopy(backup, 0, mem, 0, static_ptr); 1578 syncstate(); 1579 frame = new ZFrame(U16(6), 0, 0); 1580 freelist = null; 1581 } 1582 1583 void SW(byte[] data, int addr, int val) { 1584 data[addr] = (byte) ((val >> 24) & 0xff); 1585 data[addr + 1] = (byte) ((val >> 16) & 0xff); 1586 data[addr + 2] = (byte) ((val >> 8) & 0xff); 1587 data[addr + 3] = (byte) (val & 0xff); 1588 } 1589 1590 int LW(byte[] mem, int addr) { 1591 return ((mem[addr] & 0xff) << 24) | 1592 ((mem[addr + 1] & 0xff) << 16) | 1593 ((mem[addr + 2] & 0xff) << 8) | 1594 (mem[addr + 3] & 0xff); 1595 } 1596 1597 byte[] save() { 1598 int size, p, i, c; 1599 ZFrame f; 1600 byte[] state; 1601 1602 /* dynram, stack, stacksize, fcount */ 1603 size = static_ptr + frame.sp * 4 + 8; 1604 1605 for(c = 0, f = frame; f != null; f = f.prev) { 1606 size += 16 + 4 * (f.locals.length - 1); 1607 /* pc, sp, bp, llen, local (x llen) */ 1608 c ++; 1609 } 1610 1611 state = new byte[size + 64]; 1612 System.arraycopy(mem, 0, state, 64, static_ptr); 1613 p = static_ptr + 64; 1614 1615 /* save stack */ 1616 SW(state, p, frame.sp); 1617 p += 4; 1618 for(i = 0; i < frame.sp; i++) { 1619 SW(state, p, stack[i]); 1620 p += 4; 1621 } 1622 1623 1624 /* save framelist */ 1625 SW(state, p, c); 1626 p += 4; 1627 for(f = frame ; f != null; f = f.prev) { 1628 SW(state, p, f.pc); 1629 SW(state, p + 4, f.sp); 1630 SW(state, p + 8, f.bp); 1631 SW(state, p + 12, f.locals.length - 1); 1632 p += 16; 1633 for(i = 0; i < f.locals.length - 1; i++){ 1634 SW(state, p, f.locals[1 + i]); 1635 p += 4; 1636 } 1637 } 1638 1639 return state; 1640 } 1641 1642 boolean restore(byte[] state) { 1643 int p = static_ptr + 64; 1644 int c, i, j; 1645 ZFrame f = null; 1646 1647 if((state == null) || (state.length < static_ptr)) return false; 1648 1649 /* sanity check that this is the same gamefile */ 1650 for(i = 0; i < 64; i++){ 1651 if(state[i+64] != mem[i]) return false; 1652 } 1653 /* restore dynamic ram */ 1654 System.arraycopy(state, 64, mem, 0, static_ptr); 1655 1656 /* restore stack */ 1657 c = LW(state, p); 1658 p += 4; 1659 for(i = 0; i < c; i++) { 1660 stack[i] = LW(state, p); 1661 p += 4; 1662 } 1663 1664 /* restore framelist */ 1665 c = LW(state, p); 1666 p += 4; 1667 frame = null; 1668 for(i = 0; i < c; i++) { 1669 int pc = LW(state, p + 0); 1670 int sp = LW(state, p + 4); 1671 int bp = LW(state, p + 8); 1672 int len = LW(state, p + 12); 1673 p += 16; 1674 if(f == null) { 1675 f = new ZFrame(pc, sp, len); 1676 frame = f; 1677 } else { 1678 ZFrame x = new ZFrame(pc, sp, len); 1679 f.prev = x; 1680 f = x; 1681 } 1682 f.bp = bp; 1683 for(j = 0; j < len; j++){ 1684 f.locals[j + 1] = LW(state, p); 1685 p += 4; 1686 } 1687 } 1688 1689 syncstate(); 1690 return true; 1691 } 1692 1693 int special_count; 1694 int special_chars[]; 1695 1696 public ZMachine(ZScreen screen, byte[] data) { 1697 int i; 1698 1699 LOG("init"); 1700 scr = screen; 1701 mem = data; 1702 stack = new int[4096]; 1703 zscii_buf = new char[4096]; 1704 int version = U8(0); 1705 1706 LOG("game version " + version); 1707 switch(version){ 1708 case 1: 1709 case 2: 1710 case 3: 1711 PACK = 2; 1712 notV4 = true; 1713 notV5 = true; 1714 V3 = true; 1715 obj = new ZObject(); 1716 break; 1717 case 4: 1718 PACK = 4; 1719 V4 = true; 1720 notV5 = true; 1721 obj = new ZObjectWide(); 1722 break; 1723 case 5: 1724 PACK = 4; 1725 V5 = true; 1726 obj = new ZObjectWide(); 1727 break; 1728 default: 1729 throw new RuntimeException("unsupported version"); 1730 } 1731 LOG("high memory @ 0x" + HEX(U16(4))); 1732 LOG("entry pc @ 0x" + HEX(U16(6))); // XXX V6 1733 dict_ptr = U16(8); 1734 // load seps 1735 special_count = U8(dict_ptr) + 1; 1736 special_chars = new int[special_count]; 1737 special_chars[0] = ' '; 1738 for(i = 1; i < special_count; i++){ 1739 special_chars[i] = U8(dict_ptr + i); 1740 } 1741 1742 dict_ptr += U8(dict_ptr) + 1; 1743 dent_len = U8(dict_ptr++); 1744 dent_count = U16(dict_ptr); 1745 dict_ptr += 2; 1746 LOG("dictionary @ 0x" + HEX(U16(8)) + ": " + dent_count + " of " + dent_len); 1747 1748 LOG("object table @ 0x" + HEX(U16(10))); 1749 LOG("global vars @ 0x" + HEX(U16(12))); 1750 global_ptr = U16(12) - 32; 1751 LOG("static memory @ 0x" + HEX(U16(14))); 1752 static_ptr = U16(14); 1753 LOG("abbrev table @ 0x" + HEX(U16(24))); 1754 abbr_ptr = U16(24); 1755 default_prop_ptr = U16(10); 1756 /* object table is after default props and biased for 1-based access */ 1757 object_ptr = default_prop_ptr + obj.PropMax * 2 - obj.Size; 1758 /* bias default props for 1-based access */ 1759 default_prop_ptr -= 2; 1760 1761 backup = new byte[static_ptr]; 1762 System.arraycopy(mem, 0, backup, 0, static_ptr); 1763 1764 syncstate(); 1765 } 1766 1767 /* let the interpreter know about our configuration */ 1768 void syncstate() { 1769 /* screen size in lines by chars */ 1770 mem[0x20] = (byte) scr.GetHeight(); 1771 mem[0x21] = (byte) scr.GetWidth(); 1772 /* screen size in 'units' w by h */ 1773 mem[0x22] = (byte) scr.GetWidth(); 1774 mem[0x24] = (byte) scr.GetHeight(); 1775 /* font size in 'units' w by h */ 1776 mem[0x26] = 1; 1777 mem[0x27] = 1; 1778 1779 scr.EraseWindow(-1); 1780 1781 if(V3){ 1782 scr.SplitWindow(1); 1783 } 1784 } 1785 1786 void Println(String s) { 1787 char data[] = new char[s.length()]; 1788 s.getChars(0, s.length(), data, 0); 1789 scr.Print(data,data.length); 1790 scr.PrintChar('\n'); 1791 System.err.println(s); 1792 } 1793 1794 void Print(char data[], int len) { 1795 if(str_display) scr.Print(data,len); 1796 } 1797 1798 void PrintChar(int ch) { 1799 if(str_display) scr.PrintChar(ch); 1800 } 1801 1802 void PrintNumber(int n) { 1803 if(str_display) scr.PrintNumber(n); 1804 } 1805 1806 boolean str_display = true; 1807 1808 void SetLog(PrintStream p) {} 1809 1810 ZObject obj; 1811 1812 int static_ptr; 1813 int global_ptr; /* start of globals - 16 */ 1814 int object_ptr; /* start of object table */ 1815 int default_prop_ptr; 1816 int dict_ptr; 1817 int abbr_ptr; 1818 1819 int dent_len; 1820 int dent_count; 1821 1822 int stack[]; 1823 ZFrame frame, freelist; 1824 1825 byte[] mem; 1826 byte[] backup; 1827 ZScreen scr; 1828 1829 int PACK; 1830 1831 boolean V3, V4, V5, notV4, notV5; 1832 1833 static final boolean TRACING = false; 1834 static final boolean LOGGING = false; 1835 1836 /* reuse zframes instead of allocating new ones, if we can */ 1837 static final boolean CONSERVE_MEMORY = true; 1838 }