}/* reset_memory */ /* * storeb * * Write a byte value to the dynamic Z-machine memory. * */ internal static void storeb(zword addr, zbyte value) { if (addr >= main.h_dynamic_size) { Err.runtime_error(ErrorCodes.ERR_STORE_RANGE); } if (addr == ZMachine.H_FLAGS + 1) { /* flags register is modified */ main.h_flags &= (zword)(~(ZMachine.SCRIPTING_FLAG | ZMachine.FIXED_FONT_FLAG)); main.h_flags |= (zword)(value & (ZMachine.SCRIPTING_FLAG | ZMachine.FIXED_FONT_FLAG)); if ((value & ZMachine.SCRIPTING_FLAG) > 0) { if (!main.ostream_script) { Files.script_open(); } } else { if (main.ostream_script) { Files.script_close(); } } Screen.refresh_text_style(); } SET_BYTE(addr, value); DebugState.Output("storeb: {0} -> {1}", addr, value); }/* storeb */
}/* z_catch */ /* * z_throw, go back to the given stack frame and return the given value. * * zargs[0] = value to return * zargs[1] = stack frame * */ internal static void z_throw() { // TODO This has never been tested if (main.option_save_quetzal == true) { if (zargs[1] > main.frame_count) { Err.runtime_error(ErrorCodes.ERR_BAD_FRAME); } /* Unwind the stack a frame at a time. */ for (; main.frame_count > zargs[1]; --main.frame_count) { //fp = stack + 1 + fp[1]; main.fp = 1 + main.stack[main.fp + 1]; // TODO I think this is correct } } else { if (zargs[1] > General.STACK_SIZE) { Err.runtime_error(ErrorCodes.ERR_BAD_FRAME); } main.fp = zargs[1]; // fp = stack + zargs[1]; } ret(zargs[0]); }/* z_throw */
/* * memory_open * * Begin output redirection to the memory of the Z-machine. * */ internal static void memory_open(zword table, zword xsize, bool buffering) { if (++depth < MAX_NESTING) { if (!buffering) { xsize = 0xffff; } if (buffering && (short)xsize <= 0) { xsize = Screen.get_max_width((zword)(-(short)xsize)); } FastMem.storew(table, 0); redirect[depth].table = table; redirect[depth].width = 0; redirect[depth].total = 0; redirect[depth].xsize = xsize; main.ostream_memory = true; } else { Err.runtime_error(ErrorCodes.ERR_STR3_NESTING); } }/* memory_open */
}/* z_art_shift */ /* * z_div, signed 16bit division. * * zargs[0] = first value * zargs[1] = second value * */ internal static void z_div() { if (Process.zargs[1] == 0) { Err.runtime_error(ErrorCodes.ERR_DIV_ZERO); } Process.store((zword)((short)Process.zargs[0] / (short)Process.zargs[1])); }/* z_div */
}/* z_get_prop */ /* * z_get_prop_addr, store the address of an object property. * * Process.zargs[0] = object * Process.zargs[1] = number of property to be examined * */ internal static void z_get_prop_addr() { zword prop_addr; zbyte value; zbyte mask; if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_GET_PROP_ADDR_0); Process.store(0); return; } if (main.story_id == Story.BEYOND_ZORK) { if (Process.zargs[0] > MAX_OBJECT) { Process.store(0); return; } } /* Property id is in bottom five (six) bits */ mask = (zbyte)((main.h_version <= ZMachine.V3) ? 0x1f : 0x3f); /* Load address of first property */ prop_addr = first_property(Process.zargs[0]); /* Scan down the property list */ for (; ;) { FastMem.LOW_BYTE(prop_addr, out value); if ((value & mask) <= Process.zargs[1]) { break; } prop_addr = next_property(prop_addr); } /* Calculate the property address or return zero */ if ((value & mask) == Process.zargs[1]) { if (main.h_version >= ZMachine.V4 && (value & 0x80) > 0) { prop_addr++; } Process.store((zword)(prop_addr + 1)); } else { Process.store(0); } }/* z_get_prop_addr */
}/* z_insert_obj */ /* * z_put_prop, set the value of an object property. * * Process.zargs[0] = object * Process.zargs[1] = number of property to set * Process.zargs[2] = value to set property to * */ internal static void z_put_prop() { zword prop_addr; zbyte value; zbyte mask; if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_PUT_PROP_0); return; } /* Property id is in bottom five or six bits */ mask = (zbyte)((main.h_version <= ZMachine.V3) ? 0x1f : 0x3f); /* Load address of first property */ prop_addr = first_property(Process.zargs[0]); /* Scan down the property list */ for (; ;) { FastMem.LOW_BYTE(prop_addr, out value); if ((value & mask) <= Process.zargs[1]) { break; } prop_addr = next_property(prop_addr); } /* Exit if the property does not exist */ if ((value & mask) != Process.zargs[1]) { Err.runtime_error(ErrorCodes.ERR_NO_PROP); } /* Store the new property value (byte or word sized) */ prop_addr++; if ((main.h_version <= ZMachine.V3 && !((value & 0xe0) > 0)) || (main.h_version >= ZMachine.V4 && !((value & 0xc0) > 0))) { zbyte v = (zbyte)Process.zargs[2]; FastMem.SET_BYTE(prop_addr, v); } else { zword v = Process.zargs[2]; FastMem.SET_WORD(prop_addr, v); } }/* z_put_prop */
internal static void print_char(zword c) { if (main.message || main.ostream_memory || main.enable_buffering) { if (!print_char_flag) { /* Characters 0 and ZC_RETURN are special cases */ if (c == CharCodes.ZC_RETURN) { new_line(); return; } if (c == 0) { return; } /* Flush the buffer before a whitespace or after a hyphen */ if (c == ' ' || c == CharCodes.ZC_INDENT || c == CharCodes.ZC_GAP || (prev_c == '-' && c != '-')) { flush_buffer(); } /* Set the flag if this is part one of a style or font change */ if (c == CharCodes.ZC_NEW_FONT || c == CharCodes.ZC_NEW_STYLE) { print_char_flag = true; } /* Remember the current character code */ prev_c = c; } else { print_char_flag = false; } /* Insert the character into the buffer */ buffer_var[bufpos++] = (char)c; if (bufpos == General.TEXT_BUFFER_SIZE) { Err.runtime_error(ErrorCodes.ERR_TEXT_BUF_OVF); } } else { Stream.stream_char(c); } }/* print_char */
}/* z_jin */ /* * z_get_child, store the child of an object. * * Process.zargs[0] = object * */ internal static void z_get_child() { zword obj_addr; /* If we are monitoring object locating display a short note */ if (main.option_object_locating == true) { Stream.stream_mssg_on(); Text.print_string("@get_child "); Text.print_object(Process.zargs[0]); Stream.stream_mssg_off(); } if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_GET_CHILD_0); Process.store(0); Process.branch(false); return; } obj_addr = object_address(Process.zargs[0]); if (main.h_version <= ZMachine.V3) { zbyte child; /* Get child id from object */ obj_addr += O1_CHILD; FastMem.LOW_BYTE(obj_addr, out child); /* Store child id and branch */ Process.store(child); Process.branch(child > 0); } else { zword child; /* Get child id from object */ obj_addr += O4_CHILD; FastMem.LOW_WORD(obj_addr, out child); /* Store child id and branch */ Process.store(child); Process.branch(child > 0); } }/* z_get_child */
}/* z_clear_attr */ /* * z_jin, branch if the first object is inside the second. * * Process.zargs[0] = first object * Process.zargs[1] = second object * */ internal static void z_jin() { zword obj_addr; /* If we are monitoring object locating display a short note */ if (main.option_object_locating == true) { Stream.stream_mssg_on(); Text.print_string("@jin "); Text.print_object(Process.zargs[0]); Text.print_string(" "); Text.print_object(Process.zargs[1]); Stream.stream_mssg_off(); } if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_JIN_0); Process.branch(0 == Process.zargs[1]); return; } obj_addr = object_address(Process.zargs[0]); if (main.h_version <= ZMachine.V3) { zbyte parent; /* Get parent id from object */ obj_addr += O1_PARENT; FastMem.LOW_BYTE(obj_addr, out parent); /* Branch if the parent is obj2 */ Process.branch(parent == Process.zargs[1]); } else { zword parent; /* Get parent id from object */ obj_addr += O4_PARENT; FastMem.LOW_WORD(obj_addr, out parent); /* Branch if the parent is obj2 */ Process.branch(parent == Process.zargs[1]); } }/* z_jin */
}/* z_remove_obj */ /* * z_set_attr, set an object attribute. * * Process.zargs[0] = object * Process.zargs[1] = number of attribute to set * */ internal static void z_set_attr() { zword obj_addr; zbyte value; if (main.story_id == Story.SHERLOCK) { if (Process.zargs[1] == 48) { return; } } if (Process.zargs[1] > ((main.h_version <= ZMachine.V3) ? 31 : 47)) { Err.runtime_error(ErrorCodes.ERR_ILL_ATTR); } /* If we are monitoring attribute assignment display a short note */ if (main.option_attribute_assignment == true) { Stream.stream_mssg_on(); Text.print_string("@set_attr "); Text.print_object(Process.zargs[0]); Text.print_string(" "); Text.print_num(Process.zargs[1]); Stream.stream_mssg_off(); } if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_SET_ATTR_0); return; } /* Get attribute address */ obj_addr = (zword)(object_address(Process.zargs[0]) + Process.zargs[1] / 8); /* Load attribute byte */ FastMem.LOW_BYTE(obj_addr, out value); /* Set attribute bit */ value |= (zbyte)(0x80 >> (Process.zargs[1] & 7)); /* Store attribute byte */ FastMem.SET_BYTE(obj_addr, value); }/* z_set_attr */
}/* z_check_arg_count */ /* * z_jump, jump unconditionally to the given address. * * zargs[0] = PC relative address * */ internal static void z_jump() { long pc; FastMem.GET_PC(out pc); pc += (short)zargs[0] - 2; // TODO This actually counts on an overflow to work if (pc >= main.story_size) { Err.runtime_error(ErrorCodes.ERR_ILL_JUMP_ADDR); } FastMem.SET_PC(pc); }/* z_jump */
}/* z_get_child */ /* * z_get_next_prop, store the number of the first or next property. * * Process.zargs[0] = object * Process.zargs[1] = address of current property (0 gets the first property) * */ internal static void z_get_next_prop() { zword prop_addr; zbyte value; zbyte mask; if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_GET_NEXT_PROP_0); Process.store(0); return; } /* Property id is in bottom five (six) bits */ mask = (zbyte)((main.h_version <= ZMachine.V3) ? 0x1f : 0x3f); /* Load address of first property */ prop_addr = first_property(Process.zargs[0]); if (Process.zargs[1] != 0) { /* Scan down the property list */ do { FastMem.LOW_BYTE(prop_addr, out value); prop_addr = next_property(prop_addr); } while ((value & mask) > Process.zargs[1]); /* Exit if the property does not exist */ if ((value & mask) != Process.zargs[1]) { Err.runtime_error(ErrorCodes.ERR_NO_PROP); } } /* Return the property id */ FastMem.LOW_BYTE(prop_addr, out value); Process.store((zword)(value & mask)); }/* z_get_next_prop */
}/* z_get_prop_len */ /* * z_get_sibling, store the sibling of an object. * * Process.zargs[0] = object * */ internal static void z_get_sibling() { zword obj_addr; if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_GET_SIBLING_0); Process.store(0); Process.branch(false); return; } obj_addr = object_address(Process.zargs[0]); if (main.h_version <= ZMachine.V3) { zbyte sibling; /* Get sibling id from object */ obj_addr += O1_SIBLING; FastMem.LOW_BYTE(obj_addr, out sibling); /* Store sibling and branch */ Process.store(sibling); Process.branch(sibling > 0); // TODO I'm not sure about this logic Process.branch (sibling); // I think it means if the sibling isn't zero, jump.. } else { zword sibling; /* Get sibling id from object */ obj_addr += O4_SIBLING; FastMem.LOW_WORD(obj_addr, out sibling); /* Store sibling and branch */ Process.store(sibling); Process.branch(sibling > 0); } }/* z_get_sibling */
}/* alphabet */ /* * find_resolution * * Find the number of bytes used for dictionary resolution. * */ internal static void find_resolution() { zword dct = main.h_dictionary; zbyte sep_count; zbyte entry_len; FastMem.LOW_BYTE(dct, out sep_count); dct += (zword)(1 + sep_count); /* skip word separators */ FastMem.LOW_BYTE(dct, out entry_len); resolution = (main.h_version <= ZMachine.V3) ? 2 : 3; if (2 * resolution > entry_len) { Err.runtime_error(ErrorCodes.ERR_DICT_LEN); } decoded = new zword[3 * resolution + 1]; encoded = new zword[resolution]; }/* find_resolution */
}/* z_set_attr */ /* * z_test_attr, branch if an object attribute is set. * * Process.zargs[0] = object * Process.zargs[1] = number of attribute to test * */ internal static void z_test_attr() { zword obj_addr; zbyte value; if (Process.zargs[1] > ((main.h_version <= ZMachine.V3) ? 31 : 47)) { Err.runtime_error(ErrorCodes.ERR_ILL_ATTR); } /* If we are monitoring attribute testing display a short note */ if (main.option_attribute_testing == true) { Stream.stream_mssg_on(); Text.print_string("@test_attr "); Text.print_object(Process.zargs[0]); Text.print_string(" "); Text.print_num(Process.zargs[1]); Stream.stream_mssg_off(); } if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_TEST_ATTR_0); Process.branch(false); return; } /* Get attribute address */ obj_addr = (zword)(object_address(Process.zargs[0]) + Process.zargs[1] / 8); /* Load attribute byte */ FastMem.LOW_BYTE(obj_addr, out value); /* Test attribute */ Process.branch((value & (0x80 >> (Process.zargs[1] & 7))) > 0); }/* z_test_attr */
}/* call */ /* * ret * * Return from the current subroutine and restore the previous stack * frame. The result may be stored (0), thrown away (1) or pushed on * the stack (2). In the latter case a direct call has been finished * and we must exit the interpreter loop. * */ internal static void ret(zword value) { long pc; int ct; if (main.sp > main.fp) { Err.runtime_error(ErrorCodes.ERR_STK_UNDF); } main.sp = main.fp; DebugState.Output("Removing Frame: {0}", main.frame_count); ct = main.stack[main.sp++] >> (main.option_save_quetzal == true ? 12 : 8); main.frame_count--; main.fp = 1 + main.stack[main.sp++]; // fp = stack + 1 + *sp++; pc = main.stack[main.sp++]; pc = (main.stack[main.sp++] << 9) | (int)pc; // TODO Really don't trust casting PC to int FastMem.SET_PC(pc); /* Handle resulting value */ if (ct == 0) { store(value); } if (ct == 2) { main.stack[--main.sp] = value; } /* Stop main loop for direct calls */ if (ct == 2) { finished++; } }/* ret */
/* * object_address * * Calculate the address of an object. * */ internal static zword object_address(zword obj) { /* Check object number */ if (obj > ((main.h_version <= ZMachine.V3) ? 255 : MAX_OBJECT)) { Text.print_string("@Attempt to address illegal object "); Text.print_num(obj); Text.print_string(". This is normally fatal."); Buffer.new_line(); Err.runtime_error(ErrorCodes.ERR_ILL_OBJ); } /* Return object address */ if (main.h_version <= ZMachine.V3) { return((zword)(main.h_objects + ((obj - 1) * O1_SIZE + 62))); } else { return((zword)(main.h_objects + ((obj - 1) * O4_SIZE + 126))); } }/* object_address */
/* * Save a game using Quetzal format. Return 1 if OK, 0 if failed. */ internal static zword save_quetzal(FileStream svf, MemoryStream stf) { zlong ifzslen = 0, cmemlen = 0, stkslen = 0; long pc; zword i, j, n; int nvars, nargs, nstk, p; zbyte var; long cmempos, stkspos; int c; /* Write `IFZS' header. */ if (!write_chnk(svf, ID_FORM, 0)) { return(0); } if (!write_long(svf, ID_IFZS)) { return(0); } /* Write `IFhd' chunk. */ FastMem.GET_PC(out pc); if (!write_chnk(svf, ID_IFhd, 13)) { return(0); } if (!write_word(svf, main.h_release)) { return(0); } for (i = ZMachine.H_SERIAL; i < ZMachine.H_SERIAL + 6; ++i) { if (!write_byte(svf, FastMem.ZMData[FastMem.zmp + i])) { return(0); } } if (!write_word(svf, main.h_checksum)) { return(0); } if (!write_long(svf, pc << 8)) /* Includes pad. */ return { (0); } /* Write `CMem' chunk. */ if ((cmempos = svf.Position) < 0) { return(0); } if (!write_chnk(svf, ID_CMem, 0)) { return(0); } // (void) fseek (stf, 0, SEEK_SET); stf.Position = 0; /* j holds current run length. */ for (i = 0, j = 0, cmemlen = 0; i < main.h_dynamic_size; ++i) { if ((c = stf.ReadByte()) == -1) { return(0); } c ^= (int)FastMem.ZMData[i]; if (c == 0) { ++j; /* It's a run of equal bytes. */ } else { /* Write out any run there may be. */ if (j > 0) { for (; j > 0x100; j -= 0x100) { if (!write_run(svf, 0xFF)) { return(0); } cmemlen += 2; } if (!write_run(svf, (byte)(j - 1))) { return(0); } cmemlen += 2; j = 0; } /* Any runs are now written. Write this (nonzero) byte. */ if (!write_byte(svf, (zbyte)c)) { return(0); } ++cmemlen; } } /* * Reached end of dynamic memory. We ignore any unwritten run there may be * at this point. */ if ((cmemlen & 1) > 0) /* Chunk length must be even. */ { if (!write_byte(svf, 0)) { return(0); } } /* Write `Stks' chunk. You are not expected to understand this. ;) */ if ((stkspos = svf.Position) < 0) { return(0); } if (!write_chnk(svf, ID_Stks, 0)) { return(0); } /* * We construct a list of frame indices, most recent first, in `frames'. * These indices are the offsets into the `stack' array of the word before * the first word pushed in each frame. */ frames[0] = (zword)main.sp; /* The frame we'd get by doing a call now. */ for (i = (zword)(main.fp + 4), n = 0; i < General.STACK_SIZE + 4; i = (zword)(main.stack[i - 3] + 5)) { frames[++n] = i; } /* * All versions other than V6 can use evaluation stack outside a function * context. We write a faked stack frame (most fields zero) to cater for * this. */ if (main.h_version != ZMachine.V6) { for (i = 0; i < 6; ++i) { if (!write_byte(svf, 0)) { return(0); } } nstk = General.STACK_SIZE - frames[n]; if (!write_word(svf, nstk)) { return(0); } for (j = (zword)(General.STACK_SIZE - 1); j >= frames[n]; --j) { if (!write_word(svf, main.stack[j])) { return(0); } } stkslen = (zword)(8 + 2 * nstk); } /* Write out the rest of the stack frames. */ for (i = n; i > 0; --i) { p = frames[i] - 4; // p = stack + frames[i] - 4; /* Points to call frame. */ nvars = (main.stack[p] & 0x0F00) >> 8; nargs = main.stack[p] & 0x00FF; nstk = (zword)(frames[i] - frames[i - 1] - nvars - 4); pc = ((zlong)main.stack[p + 3] << 9) | main.stack[p + 2]; switch (main.stack[p] & 0xF000) /* Check type of call. */ { case 0x0000: /* Function. */ var = FastMem.ZMData[FastMem.zmp + pc]; pc = ((pc + 1) << 8) | (zlong)nvars; break; case 0x1000: /* Procedure. */ var = 0; pc = (pc << 8) | 0x10 | (zlong)nvars; /* Set procedure flag. */ break; /* case 0x2000: */ default: Err.runtime_error(ErrorCodes.ERR_SAVE_IN_INTER); return(0); } if (nargs != 0) { nargs = (zword)((1 << nargs) - 1); /* Make args into bitmap. */ } /* Write the main part of the frame... */ if (!write_long(svf, pc) || !write_byte(svf, var) || !write_byte(svf, nargs) || !write_word(svf, nstk)) { return(0); } /* Write the variables and eval stack. */ for (j = 0, --p; j < nvars + nstk; ++j, --p) { if (!write_word(svf, main.stack[p])) { return(0); } } /* Calculate length written thus far. */ stkslen += (zword)(8 + 2 * (nvars + nstk)); } /* Fill in variable chunk lengths. */ ifzslen = 3 * 8 + 4 + 14 + cmemlen + stkslen; if ((cmemlen & 1) > 0) { ++ifzslen; } svf.Position = 4; if (!write_long(svf, ifzslen)) { return(0); } svf.Position = cmempos + 4; if (!write_long(svf, cmemlen)) { return(0); } svf.Position = stkspos + 4; if (!write_long(svf, stkslen)) { return(0); } /* After all that, still nothing went wrong! */ return(1); }
}/* __extended__ */ /* * __illegal__ * * Exit game because an unknown opcode has been hit. * */ static void __illegal__() { Err.runtime_error(ErrorCodes.ERR_ILL_OPCODE); }/* __illegal__ */
}/* interpret */ /* * call * * Call a subroutine. Save PC and FP then load new PC and initialise * new stack frame. Note that the caller may legally provide less or * more arguments than the function actually has. The call type "ct" * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call). * */ internal static void call(zword routine, int argc, int args_offset, int ct) { long pc; zword value; zbyte count; int i; if (main.sp < 4)//if (sp - stack < 4) { Err.runtime_error(ErrorCodes.ERR_STK_OVF); } FastMem.GET_PC(out pc); main.stack[--main.sp] = (zword)(pc >> 9); main.stack[--main.sp] = (zword)(pc & 0x1ff); main.stack[--main.sp] = (zword)(main.fp - 1); // *--sp = (zword) (fp - stack - 1); main.stack[--main.sp] = (zword)(argc | (ct << (main.option_save_quetzal == true ? 12 : 8))); main.fp = main.sp; main.frame_count++; DebugState.Output("Added Frame: {0} -> {1}:{2}:{3}:{4}", main.frame_count, main.stack[main.sp + 0], main.stack[main.sp + 1], main.stack[main.sp + 2], main.stack[main.sp + 3]); /* Calculate byte address of routine */ if (main.h_version <= ZMachine.V3) { pc = (long)routine << 1; } else if (main.h_version <= ZMachine.V5) { pc = (long)routine << 2; } else if (main.h_version <= ZMachine.V7) { pc = ((long)routine << 2) + ((long)main.h_functions_offset << 3); } else /* (h_version <= V8) */ { pc = (long)routine << 3; } if (pc >= main.story_size) { Err.runtime_error(ErrorCodes.ERR_ILL_CALL_ADDR); } FastMem.SET_PC(pc); /* Initialise local variables */ FastMem.CODE_BYTE(out count); if (count > 15) { Err.runtime_error(ErrorCodes.ERR_CALL_NON_RTN); } if (main.sp < count) { Err.runtime_error(ErrorCodes.ERR_STK_OVF); } if (main.option_save_quetzal == true) { main.stack[main.fp] |= (zword)(count << 8); /* Save local var count for Quetzal. */ } value = 0; for (i = 0; i < count; i++) { if (main.h_version <= ZMachine.V4) /* V1 to V4 games provide default */ { FastMem.CODE_WORD(out value); /* values for all local variables */ } main.stack[--main.sp] = (zword)((argc-- > 0) ? zargs[args_offset + i] : value); //*--sp = (zword) ((argc-- > 0) ? args[i] : value); } /* Start main loop for direct calls */ if (ct == 2) { interpret(); } }/* call */
static void decode_text(string_type st, zword addr) { // zword* ptr; long byte_addr; zword c2; zword code; zbyte c, prev_c = 0; int shift_state = 0; int shift_lock = 0; int status = 0; // ptr = NULL; /* makes compilers shut up */ byte_addr = 0; if (resolution == 0) { find_resolution(); } /* Calculate the byte address if necessary */ if (st == string_type.ABBREVIATION) { byte_addr = (long)addr << 1; } else if (st == string_type.HIGH_STRING) { if (main.h_version <= ZMachine.V3) { byte_addr = (long)addr << 1; } else if (main.h_version <= ZMachine.V5) { byte_addr = (long)addr << 2; } else if (main.h_version <= ZMachine.V7) { byte_addr = ((long)addr << 2) + ((long)main.h_strings_offset << 3); } else /* (h_version <= V8) */ { byte_addr = (long)addr << 3; } if (byte_addr >= main.story_size) { Err.runtime_error(ErrorCodes.ERR_ILL_PRINT_ADDR); } } /* Loop until a 16bit word has the highest bit set */ if (st == string_type.VOCABULARY) { ptrDt = 0; } do { int i; /* Fetch the next 16bit word */ if (st == string_type.LOW_STRING || st == string_type.VOCABULARY) { FastMem.LOW_WORD(addr, out code); addr += 2; } else if (st == string_type.HIGH_STRING || st == string_type.ABBREVIATION) { FastMem.HIGH_WORD(byte_addr, out code); byte_addr += 2; } else { FastMem.CODE_WORD(out code); } /* Read its three Z-characters */ for (i = 10; i >= 0; i -= 5) { zword abbr_addr; zword ptr_addr; zword zc; c = (zbyte)((code >> i) & 0x1f); switch (status) { case 0: /* normal operation */ if (shift_state == 2 && c == 6) { status = 2; } else if (main.h_version == ZMachine.V1 && c == 1) { Buffer.new_line(); } else if (main.h_version >= ZMachine.V2 && shift_state == 2 && c == 7) { Buffer.new_line(); } else if (c >= 6) { outchar(st, alphabet(shift_state, c - 6)); } else if (c == 0) { outchar(st, ' '); } else if (main.h_version >= ZMachine.V2 && c == 1) { status = 1; } else if (main.h_version >= ZMachine.V3 && c <= 3) { status = 1; } else { shift_state = (shift_lock + (c & 1) + 1) % 3; if (main.h_version <= ZMachine.V2 && c >= 4) { shift_lock = shift_state; } break; } shift_state = shift_lock; break; case 1: /* abbreviation */ ptr_addr = (zword)(main.h_abbreviations + 64 * (prev_c - 1) + 2 * c); FastMem.LOW_WORD(ptr_addr, out abbr_addr); decode_text(string_type.ABBREVIATION, abbr_addr); status = 0; break; case 2: /* ZSCII character - first part */ status = 3; break; case 3: /* ZSCII character - second part */ zc = (zword)((prev_c << 5) | c); c2 = translate_from_zscii((zbyte)zc); // TODO This doesn't seem right outchar(st, c2); status = 0; break; } prev_c = c; } } while (!((code & 0x8000) > 0)); if (st == string_type.VOCABULARY) { ptrDt = 0; } }/* decode_text */
}/* next_property */ /* * unlink_object * * Unlink an object from its parent and siblings. * */ static void unlink_object(zword object_var) { zword obj_addr; zword parent_addr; zword sibling_addr; if (object_var == 0) { Err.runtime_error(ErrorCodes.ERR_REMOVE_OBJECT_0); return; } obj_addr = object_address(object_var); if (main.h_version <= ZMachine.V3) { zbyte parent; zbyte younger_sibling; zbyte older_sibling; zbyte zero = 0; /* Get parent of object, and return if no parent */ obj_addr += O1_PARENT; FastMem.LOW_BYTE(obj_addr, out parent); if (parent == 0) { return; } /* Get (older) sibling of object and set both parent and sibling * pointers to 0 */ FastMem.SET_BYTE(obj_addr, zero); obj_addr += (zword)(O1_SIBLING - O1_PARENT); FastMem.LOW_BYTE(obj_addr, out older_sibling); FastMem.SET_BYTE(obj_addr, zero); /* Get first child of parent (the youngest sibling of the object) */ parent_addr = (zword)(object_address(parent) + O1_CHILD); FastMem.LOW_BYTE(parent_addr, out younger_sibling); /* Remove object from the list of siblings */ if (younger_sibling == object_var) { FastMem.SET_BYTE(parent_addr, older_sibling); } else { do { sibling_addr = (zword)(object_address(younger_sibling) + O1_SIBLING); FastMem.LOW_BYTE(sibling_addr, out younger_sibling); } while (younger_sibling != object_var); FastMem.SET_BYTE(sibling_addr, older_sibling); } } else { zword parent; zword younger_sibling; zword older_sibling; zword zero = 0; /* Get parent of object, and return if no parent */ obj_addr += O4_PARENT; FastMem.LOW_WORD(obj_addr, out parent); if (parent == 0) { return; } /* Get (older) sibling of object and set both parent and sibling * pointers to 0 */ FastMem.SET_WORD(obj_addr, zero); obj_addr += (zword)(O4_SIBLING - O4_PARENT); FastMem.LOW_WORD(obj_addr, out older_sibling); FastMem.SET_WORD(obj_addr, zero); /* Get first child of parent (the youngest sibling of the object) */ parent_addr = (zword)(object_address(parent) + O4_CHILD); FastMem.LOW_WORD(parent_addr, out younger_sibling); /* Remove object from the list of siblings */ if (younger_sibling == object_var) { FastMem.SET_WORD(parent_addr, older_sibling); } else { do { sibling_addr = (zword)(object_address(younger_sibling) + O4_SIBLING); FastMem.LOW_WORD(sibling_addr, out younger_sibling); } while (younger_sibling != object_var); FastMem.SET_WORD(sibling_addr, older_sibling); } } }/* unlink_object */
}/* z_get_sibling */ /* * z_insert_obj, make an object the first child of another object. * * Process.zargs[0] = object to be moved * Process.zargs[1] = destination object * */ internal static void z_insert_obj() { zword obj1 = Process.zargs[0]; zword obj2 = Process.zargs[1]; zword obj1_addr; zword obj2_addr; /* If we are monitoring object movements display a short note */ if (main.option_object_movement == true) { Stream.stream_mssg_on(); Text.print_string("@move_obj "); Text.print_object(obj1); Text.print_string(" "); Text.print_object(obj2); Stream.stream_mssg_off(); } if (obj1 == 0) { Err.runtime_error(ErrorCodes.ERR_MOVE_OBJECT_0); return; } if (obj2 == 0) { Err.runtime_error(ErrorCodes.ERR_MOVE_OBJECT_TO_0); return; } /* Get addresses of both objects */ obj1_addr = object_address(obj1); obj2_addr = object_address(obj2); /* Remove object 1 from current parent */ unlink_object(obj1); /* Make object 1 first child of object 2 */ if (main.h_version <= ZMachine.V3) { zbyte child; obj1_addr += O1_PARENT; FastMem.SET_BYTE(obj1_addr, (zbyte)obj2); obj2_addr += O1_CHILD; FastMem.LOW_BYTE(obj2_addr, out child); FastMem.SET_BYTE(obj2_addr, (zbyte)obj1); obj1_addr += (zword)(O1_SIBLING - O1_PARENT); FastMem.SET_BYTE(obj1_addr, child); } else { zword child; obj1_addr += O4_PARENT; FastMem.SET_WORD(obj1_addr, obj2); obj2_addr += O4_CHILD; FastMem.LOW_WORD(obj2_addr, out child); FastMem.SET_WORD(obj2_addr, obj1); obj1_addr += (zword)(O4_SIBLING - O4_PARENT); FastMem.SET_WORD(obj1_addr, child); } }/* z_insert_obj */
}/* z_get_parent */ /* * z_get_prop, store the value of an object property. * * Process.zargs[0] = object * Process.zargs[1] = number of property to be examined * */ internal static void z_get_prop() { zword prop_addr; zword wprop_val; zbyte bprop_val; zbyte value; zbyte mask; if (Process.zargs[0] == 0) { Err.runtime_error(ErrorCodes.ERR_GET_PROP_0); Process.store(0); return; } /* Property id is in bottom five (six) bits */ mask = (zbyte)((main.h_version <= ZMachine.V3) ? 0x1f : 0x3f); /* Load address of first property */ prop_addr = first_property(Process.zargs[0]); /* Scan down the property list */ for (; ;) { FastMem.LOW_BYTE(prop_addr, out value); if ((value & mask) <= Process.zargs[1]) { break; } prop_addr = next_property(prop_addr); } if ((value & mask) == Process.zargs[1]) /* property found */ /* Load property (byte or word sized) */ { prop_addr++; if ((main.h_version <= ZMachine.V3 && !((value & 0xe0) > 0)) || (main.h_version >= ZMachine.V4 && !((value & 0xc0) > 0))) { FastMem.LOW_BYTE(prop_addr, out bprop_val); wprop_val = bprop_val; } else { FastMem.LOW_WORD(prop_addr, out wprop_val); } } else /* property not found */ /* Load default value */ { prop_addr = (zword)(main.h_objects + 2 * (Process.zargs[1] - 1)); FastMem.LOW_WORD(prop_addr, out wprop_val); } /* Store the property value */ Process.store(wprop_val); }/* z_get_prop */