From 1b3a3646d6f1d1e99a28b5004038742319a99227 Mon Sep 17 00:00:00 2001 From: Jared Miller Date: Tue, 10 Feb 2026 15:42:15 -0500 Subject: [PATCH] Pre-consume store byte in op_aread and op_read_char before blocking reads Without this, MUD-level saves during read_line/read_char capture PC pointing at the store byte, which gets misinterpreted as an opcode on restore. --- src/mudlib/embedded_if_session.py | 4 ++-- src/mudlib/zmachine/zcpu.py | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/mudlib/embedded_if_session.py b/src/mudlib/embedded_if_session.py index 130c462..c5404d3 100644 --- a/src/mudlib/embedded_if_session.py +++ b/src/mudlib/embedded_if_session.py @@ -52,8 +52,8 @@ class EmbeddedIFSession: branch data (V3) or store byte (V5+). Process them so execution resumes at the next instruction. - MUD-level _do_save during sread/aread: PC points past the read - instruction. No post-processing needed (phantom output suppressed - in start()). + instruction (store byte pre-consumed in op_aread/op_read_char). + No post-processing needed (phantom output suppressed in start()). """ if not self.save_path.exists(): return False diff --git a/src/mudlib/zmachine/zcpu.py b/src/mudlib/zmachine/zcpu.py index b83ed53..0f76d69 100644 --- a/src/mudlib/zmachine/zcpu.py +++ b/src/mudlib/zmachine/zcpu.py @@ -808,7 +808,13 @@ class ZCpu: text_buffer_addr = args[0] parse_buffer_addr = args[1] if len(args) > 1 else 0 - # Read input from keyboard + # Consume store byte BEFORE blocking in read_line(). This ensures + # the PC is past the entire instruction when MUD-level saves capture + # state during read_line(). Without this, saves point PC at the store + # byte, which gets misinterpreted as an opcode on restore. + store_addr = self._opdecoder.get_store_address() + + # Read input from keyboard (blocks until player types something) text = self._ui.keyboard_input.read_line() text = text.lower().strip("\n\r") @@ -840,7 +846,7 @@ class ZCpu: offset = pos + word_len # Store terminating character (13 = newline) - self._write_result(13) + self._write_result(13, store_addr=store_addr) def op_print_char(self, char): """Output the given ZSCII character.""" @@ -968,8 +974,11 @@ class ZCpu: if time != 0 or input_routine != 0: raise ZCpuNotImplemented + # Consume store byte BEFORE blocking in read_char() — same reason + # as op_aread: PC must be past the full instruction for MUD saves. + store_addr = self._opdecoder.get_store_address() char = self._ui.keyboard_input.read_char() - self._write_result(char) + self._write_result(char, store_addr=store_addr) def op_scan_table(self, x, table, length, *args): """Search a table for a value, branch if found, store address (V4+).