hzmachine

Hiptop Z Machine
git clone http://frotz.net/git/hzmachine.git
Log | Files | Refs | LICENSE

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 }