}/* 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 ZInsertObj() { 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.StreamMssgOn(); Text.PrintString("@move_obj "); Text.PrintObject(obj1); Text.PrintString(" "); Text.PrintObject(obj2); Stream.StreamMssgOff(); } if (obj1 == 0) { Err.RuntimeError(ErrorCodes.ERR_MOVE_OBJECT_0); return; } if (obj2 == 0) { Err.RuntimeError(ErrorCodes.ERR_MOVE_OBJECT_TO_0); return; } /* Get addresses of both objects */ obj1_addr = ObjectAddress(obj1); obj2_addr = ObjectAddress(obj2); /* Remove object 1 from current parent */ UnlinkObject(obj1); /* Make object 1 first child of object 2 */ if (Main.h_version <= ZMachine.V3) { obj1_addr += O1_PARENT; FastMem.SetByte(obj1_addr, (zbyte)obj2); obj2_addr += O1_CHILD; FastMem.LowByte(obj2_addr, out byte child); FastMem.SetByte(obj2_addr, (zbyte)obj1); obj1_addr += (zword)(O1_SIBLING - O1_PARENT); FastMem.SetByte(obj1_addr, child); } else { obj1_addr += O4_PARENT; FastMem.SetWord(obj1_addr, obj2); obj2_addr += O4_CHILD; FastMem.LowWord(obj2_addr, out ushort child); FastMem.SetWord(obj2_addr, obj1); obj1_addr += (zword)(O4_SIBLING - O4_PARENT); FastMem.SetWord(obj1_addr, child); } }/* z_insert_obj */
} /* z_loadb */ /* * z_loadw, store a value from a table of words. * * zargs[0] = address of table * zargs[1] = index of table entry to store * */ internal static void ZLoadW() { zword addr = (zword)(Process.zargs[0] + 2 * Process.zargs[1]); FastMem.LowWord(addr, out ushort value); Process.Store(value); } /* z_loadw */
}/* 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 ZGetProp() { zword prop_addr; zword wprop_val; zbyte value; zbyte mask; if (Process.zargs[0] == 0) { Err.RuntimeError(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 = FirstProperty(Process.zargs[0]); /* Scan down the property list */ for (; ;) { FastMem.LowByte(prop_addr, out value); if ((value & mask) <= Process.zargs[1]) { break; } prop_addr = NextProperty(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.LowByte(prop_addr, out zbyte bprop_val); wprop_val = bprop_val; } else { FastMem.LowWord(prop_addr, out wprop_val); } } else { /* property not found */ /* Load default value */ prop_addr = (zword)(Main.h_objects + 2 * (Process.zargs[1] - 1)); FastMem.LowWord(prop_addr, out wprop_val); } /* Store the property value */ Process.Store(wprop_val); }/* z_get_prop */
/* * translate_from_zscii * * Map a ZSCII character into Unicode. * */ internal static zword TranslateFromZscii(zbyte c) { if (c == 0xfc) { return(CharCodes.ZC_MENU_CLICK); } if (c == 0xfd) { return(CharCodes.ZC_DOUBLE_CLICK); } if (c == 0xfe) { return(CharCodes.ZC_SINGLE_CLICK); } if (c >= 0x9b && Main.StoryId != Story.BEYOND_ZORK) { if (Main.hx_unicode_table != 0) { /* game has its own Unicode table */ FastMem.LowByte(Main.hx_unicode_table, out zbyte N); if (c - 0x9b < N) { zword addr = (zword)(Main.hx_unicode_table + 1 + 2 * (c - 0x9b)); FastMem.LowWord(addr, out zword unicode); if (unicode < 0x20) { return('?'); } return(unicode); } else { return('?'); } } else if (c <= 0xdf) /* game uses standard set */ { return(zscii_to_latin1[c - 0x9b]); } else { return('?'); } } return(c); }/* translate_from_zscii */
internal static void ZMakeMenu() { /* This opcode was only used for the Macintosh version of Journey. * It controls menus with numbers greater than 2 (menus 0, 1 and 2 * are system menus). */ if (Process.zargs[0] < 3) { Process.Branch(false); return; } if (Process.zargs[1] != 0) { FastMem.LowWord(Process.zargs[1], out zword items); for (int i = 0; i < items; i++) { FastMem.LowWord(Process.zargs[1] + 2 + (2 * i), out zword item); FastMem.LowByte(item, out zbyte length); if (length > 31) { length = 31; } menu[length] = 0; for (int j = 0; j < length; j++) { FastMem.LowByte(item + j + 1, out zbyte c); menu[j] = Text.TranslateFromZscii(c); } if (i == 0) { OS.Menu(ZMachine.MENU_NEW, Process.zargs[0], menu); } else { OS.Menu(ZMachine.MENU_ADD, Process.zargs[0], menu); } } } else { OS.Menu(ZMachine.MENU_REMOVE, Process.zargs[0], Array.Empty <zword>()); } Process.Branch(true); }/* z_make_menu */
}/* z_push */ /* * z_push_stack, push a value onto a user stack then branch if successful. * * zargs[0] = value to push onto the stack * zargs[1] = address of user stack * */ internal static void ZPushStack() { zword addr = Process.zargs[1]; FastMem.LowWord(addr, out ushort size); if (size != 0) { FastMem.StoreW((zword)(addr + 2 * size), Process.zargs[0]); size--; FastMem.StoreW(addr, size); } Process.Branch(size > 0); // TODO I think that's what's expected here }/* z_push_stack */
internal static void ZPop() => Main.sp++; /* z_pop */ /* * z_pop_stack, pop n values off the game or user stack and discard them. * * zargs[0] = number of values to discard * zargs[1] = address of user stack (optional) * */ internal static void ZPopStack() { if (Process.zargc == 2) { /* it's a user stack */ zword addr = Process.zargs[1]; FastMem.LowWord(addr, out ushort size); size += Process.zargs[0]; FastMem.StoreW(addr, size); } else { Main.sp += Process.zargs[0]; /* it's the game stack */ } }/* z_pop_stack */
}/* 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 ZJin() { zword obj_addr; /* If we are monitoring object locating display a short note */ if (Main.option_object_locating) { Stream.StreamMssgOn(); Text.PrintString("@jin "); Text.PrintObject(Process.zargs[0]); Text.PrintString(" "); Text.PrintObject(Process.zargs[1]); Stream.StreamMssgOff(); } if (Process.zargs[0] == 0) { Err.RuntimeError(ErrorCodes.ERR_JIN_0); Process.Branch(0 == Process.zargs[1]); return; } obj_addr = ObjectAddress(Process.zargs[0]); if (Main.h_version <= ZMachine.V3) { /* Get parent id from object */ obj_addr += O1_PARENT; FastMem.LowByte(obj_addr, out zbyte parent); /* Branch if the parent is obj2 */ Process.Branch(parent == Process.zargs[1]); } else { /* Get parent id from object */ obj_addr += O4_PARENT; FastMem.LowWord(obj_addr, out zword parent); /* Branch if the parent is obj2 */ Process.Branch(parent == Process.zargs[1]); } }/* z_jin */
}/* z_get_next_prop */ /* * z_get_parent, store the parent of an object. * * Process.zargs[0] = object * */ internal static void ZGetParent() { zword obj_addr; /* If we are monitoring object locating display a short note */ if (Main.option_object_locating) { Stream.StreamMssgOn(); Text.PrintString("@get_parent "); Text.PrintObject(Process.zargs[0]); Stream.StreamMssgOff(); } if (Process.zargs[0] == 0) { Err.RuntimeError(ErrorCodes.ERR_GET_PARENT_0); Process.Store(0); return; } obj_addr = ObjectAddress(Process.zargs[0]); if (Main.h_version <= ZMachine.V3) { /* Get parent id from object */ obj_addr += O1_PARENT; FastMem.LowByte(obj_addr, out zbyte parent); /* Store parent */ Process.Store(parent); } else { /* Get parent id from object */ obj_addr += O4_PARENT; FastMem.LowWord(obj_addr, out zword parent); /* Store parent */ Process.Store(parent); } }/* z_get_parent */
}/* z_jin */ /* * z_get_child, store the child of an object. * * Process.zargs[0] = object * */ internal static void ZGetChild() { zword obj_addr; /* If we are monitoring object locating display a short note */ if (Main.option_object_locating) { Stream.StreamMssgOn(); Text.PrintString("@get_child "); Text.PrintObject(Process.zargs[0]); Stream.StreamMssgOff(); } if (Process.zargs[0] == 0) { Err.RuntimeError(ErrorCodes.ERR_GET_CHILD_0); Process.Store(0); Process.Branch(false); return; } obj_addr = ObjectAddress(Process.zargs[0]); if (Main.h_version <= ZMachine.V3) { /* Get child id from object */ obj_addr += O1_CHILD; FastMem.LowByte(obj_addr, out zbyte child); /* Store child id and branch */ Process.Store(child); Process.Branch(child > 0); } else { /* Get child id from object */ obj_addr += O4_CHILD; FastMem.LowWord(obj_addr, out zword child); /* Store child id and branch */ Process.Store(child); Process.Branch(child > 0); } }/* z_get_child */
}/* z_pop_stack */ /* * z_pull, pop a value off... * * a) ...the game or a user stack and store it (V6) * * zargs[0] = address of user stack (optional) * * b) ...the game stack and write it to a variable (other than V6) * * zargs[0] = variable to write value to * */ internal static void ZPull() { zword value; if (Main.h_version != ZMachine.V6) { /* not a V6 game, pop stack and write */ value = Main.Stack[Main.sp++]; if (Process.zargs[0] == 0) { Main.Stack[Main.sp] = value; } else if (Process.zargs[0] < 16) { // *(fp - Process.zargs[0]) = value; Main.Stack[Main.fp - Process.zargs[0]] = value; } else { zword addr = (zword)(Main.h_globals + 2 * (Process.zargs[0] - 16)); FastMem.SetWord(addr, value); } } else { /* it's V6, but is there a user stack? */ if (Process.zargc == 1) { /* it's a user stack */ zword addr = Process.zargs[0]; FastMem.LowWord(addr, out ushort size); size++; FastMem.StoreW(addr, size); addr += (zword)(2 * size); FastMem.LowWord(addr, out value); } else { value = Main.Stack[Main.sp++];// value = *sp++; /* it's the game stack */ } Process.Store(value); } }/* z_pull */
}/* z_dec_chk */ /* * z_inc, increment a variable. * * zargs[0] = variable to increment * */ internal static void ZInc() { if (Process.zargs[0] == 0) { Main.Stack[Main.sp]++; // (*sp)++; } else if (Process.zargs[0] < 16) { (Main.Stack[Main.fp - Process.zargs[0]])++; } else { zword addr = (zword)(Main.h_globals + 2 * (Process.zargs[0] - 16)); FastMem.LowWord(addr, out ushort value); value++; FastMem.SetWord(addr, value); } }/* z_inc */
/* * z_dec, decrement a variable. * * zargs[0] = variable to decrement * */ internal static void ZDec() { if (Process.zargs[0] == 0) { Main.Stack[Main.sp]--; } else if (Process.zargs[0] < 16) { Main.Stack[Main.fp - Process.zargs[0]]--; } else { zword addr = (zword)(Main.h_globals + 2 * (Process.zargs[0] - 16)); FastMem.LowWord(addr, out ushort value); value--; FastMem.SetWord(addr, value); } }/* z_dec */
} /* z_loadw */ /* * z_scan_table, find and store the address of a target within a table. * * zargs[0] = target value to be searched for * zargs[1] = address of table * zargs[2] = number of table entries to check value against * zargs[3] = type of table (optional, defaults to 0x82) * * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise * it's a byte array. The lower bits hold the address step. * */ internal static void ZScanTable() { zword addr = Process.zargs[1]; int i; /* Supply default arguments */ if (Process.zargc < 4) { Process.zargs[3] = 0x82; } /* Scan byte or word array */ for (i = 0; i < Process.zargs[2]; i++) { if ((Process.zargs[3] & 0x80) > 0) { /* scan word array */ FastMem.LowWord(addr, out ushort wvalue); if (wvalue == Process.zargs[0]) { goto finished; } } else { /* scan byte array */ FastMem.LowByte(addr, out byte bvalue); if (bvalue == Process.zargs[0]) { goto finished; } } addr += (zword)(Process.zargs[3] & 0x7f); } addr = 0; finished: Process.Store(addr); Process.Branch(addr > 0); }/* z_scan_table */
}/* object_address */ /* * object_name * * Return the address of the given object's name. * */ internal static zword ObjectName(zword object_var) { zword obj_addr = ObjectAddress(object_var); /* The object name address is found at the start of the properties */ if (Main.h_version <= ZMachine.V3) { obj_addr += O1_PROPERTY_OFFSET; } else { obj_addr += O4_PROPERTY_OFFSET; } FastMem.LowWord(obj_addr, out zword name_addr); return(name_addr); }/* object_name */
}/* z_inc_chk */ /* * z_load, store the value of a variable. * * zargs[0] = variable to store * */ internal static void ZLoad() { zword value; if (Process.zargs[0] == 0) { value = Main.Stack[Main.sp]; } else if (Process.zargs[0] < 16) { value = Main.Stack[Main.fp - Process.zargs[0]]; } else { zword addr = (zword)(Main.h_globals + 2 * (Process.zargs[0] - 16)); FastMem.LowWord(addr, out value); } Process.Store(value); }/* z_load */
}/* translate_from_zscii */ /* * unicode_to_zscii * * Convert a Unicode character to ZSCII, returning 0 on failure. * */ internal static zbyte UnicodeToZscii(zword c) { int i; if (c >= CharCodes.ZC_LATIN1_MIN) { if (Main.hx_unicode_table != 0) { /* game has its own Unicode table */ FastMem.LowByte(Main.hx_unicode_table, out zbyte N); for (i = 0x9b; i < 0x9b + N; i++) { zword addr = (zword)(Main.hx_unicode_table + 1 + 2 * (i - 0x9b)); FastMem.LowWord(addr, out zword unicode); if (c == unicode) { return((zbyte)i); } } return(0); } else { /* game uses standard set */ for (i = 0x9b; i <= 0xdf; i++) { if (c == zscii_to_latin1[i - 0x9b]) { return((zbyte)i); } } return(0); } } return((zbyte)c); }/* unicode_to_zscii */
}/* z_dec */ /* * z_dec_chk, decrement a variable and branch if now less than value. * * zargs[0] = variable to decrement * zargs[1] = value to check variable against * */ internal static void ZDecChk() { zword value; if (Process.zargs[0] == 0) { value = --Main.Stack[Main.sp]; } else if (Process.zargs[0] < 16) { value = --Main.Stack[Main.fp - Process.zargs[0]]; } else { zword addr = (zword)(Main.h_globals + 2 * (Process.zargs[0] - 16)); FastMem.LowWord(addr, out value); value--; FastMem.SetWord(addr, value); } Process.Branch((short)value < (short)Process.zargs[1]); }/* z_dec_chk */
}/* memory_new_line */ /* * memory_word * * Redirect a string of characters to the memory of the Z-machine. * */ internal static void MemoryWord(ReadOnlySpan <zword> s) { zword addr; zword c; int pos = 0; if (Main.h_version == ZMachine.V6) { int width = OS.StringWidth(s); if (redirect[depth].XSize != 0xffff) { if (redirect[depth].Width + width > redirect[depth].XSize) { if (s[pos] is ' ' or CharCodes.ZC_INDENT or CharCodes.ZC_GAP) { width = OS.StringWidth(s.Slice(++pos)); } MemoryNewline(); } } redirect[depth].Width += (zword)width; } addr = redirect[depth].Table; FastMem.LowWord(addr, out zword size); addr += 2; while ((c = s[pos++]) != 0) { FastMem.StoreB((zword)(addr + (size++)), Text.TranslateToZscii(c)); } FastMem.StoreW(redirect[depth].Table, size); }/* memory_word */
}/* z_get_prop_len */ /* * z_get_sibling, store the sibling of an object. * * Process.zargs[0] = object * */ internal static void ZGetSibling() { zword obj_addr; if (Process.zargs[0] == 0) { Err.RuntimeError(ErrorCodes.ERR_GET_SIBLING_0); Process.Store(0); Process.Branch(false); return; } obj_addr = ObjectAddress(Process.zargs[0]); if (Main.h_version <= ZMachine.V3) { /* Get sibling id from object */ obj_addr += O1_SIBLING; FastMem.LowByte(obj_addr, out byte 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 { /* Get sibling id from object */ obj_addr += O4_SIBLING; FastMem.LowWord(obj_addr, out ushort sibling); /* Store sibling and branch */ Process.Store(sibling); Process.Branch(sibling > 0); } }/* z_get_sibling */
}/* memory_open */ /* * memory_new_line * * Redirect a newline to the memory of the Z-machine. * */ internal static void MemoryNewline() { zword addr; redirect[depth].Total += redirect[depth].Width; redirect[depth].Width = 0; addr = redirect[depth].Table; FastMem.LowWord(addr, out zword size); addr += 2; if (redirect[depth].XSize != 0xffff) { redirect[depth].Table = (zword)(addr + size); size = 0; } else { FastMem.StoreB((zword)(addr + (size++)), 13); } FastMem.StoreW(redirect[depth].Table, size); }/* memory_new_line */
}/* next_property */ /* * unlink_object * * Unlink an object from its parent and siblings. * */ private static void UnlinkObject(zword object_var) { zword parent_addr; zword sibling_addr; if (object_var == 0) { Err.RuntimeError(ErrorCodes.ERR_REMOVE_OBJECT_0); return; } zword obj_addr = ObjectAddress(object_var); if (Main.h_version <= ZMachine.V3) { zbyte zero = 0; /* Get parent of object, and return if no parent */ obj_addr += O1_PARENT; FastMem.LowByte(obj_addr, out byte parent); if (parent == 0) { return; } /* Get (older) sibling of object and set both parent and sibling * pointers to 0 */ FastMem.SetByte(obj_addr, zero); obj_addr += (zword)(O1_SIBLING - O1_PARENT); FastMem.LowByte(obj_addr, out byte older_sibling); FastMem.SetByte(obj_addr, zero); /* Get first child of parent (the youngest sibling of the object) */ parent_addr = (zword)(ObjectAddress(parent) + O1_CHILD); FastMem.LowByte(parent_addr, out byte younger_sibling); /* Remove object from the list of siblings */ if (younger_sibling == object_var) { FastMem.SetByte(parent_addr, older_sibling); } else { do { sibling_addr = (zword)(ObjectAddress(younger_sibling) + O1_SIBLING); FastMem.LowByte(sibling_addr, out younger_sibling); } while (younger_sibling != object_var); FastMem.SetByte(sibling_addr, older_sibling); } } else { zword zero = 0; /* Get parent of object, and return if no parent */ obj_addr += O4_PARENT; FastMem.LowWord(obj_addr, out zword parent); if (parent == 0) { return; } /* Get (older) sibling of object and set both parent and sibling * pointers to 0 */ FastMem.SetWord(obj_addr, zero); obj_addr += (zword)(O4_SIBLING - O4_PARENT); FastMem.LowWord(obj_addr, out ushort older_sibling); FastMem.SetWord(obj_addr, zero); /* Get first child of parent (the youngest sibling of the object) */ parent_addr = (zword)(ObjectAddress(parent) + O4_CHILD); FastMem.LowWord(parent_addr, out ushort younger_sibling); /* Remove object from the list of siblings */ if (younger_sibling == object_var) { FastMem.SetWord(parent_addr, older_sibling); } else { do { sibling_addr = (zword)(ObjectAddress(younger_sibling) + O4_SIBLING); FastMem.LowWord(sibling_addr, out younger_sibling); } while (younger_sibling != object_var); FastMem.SetWord(sibling_addr, older_sibling); } } }/* unlink_object */