Implement op_verify and wire ZLexer into sread for Zork 1

op_verify now performs actual checksum validation against the header
instead of raising NotImplemented. ZLexer is injected into ZCpu and
sread tokenizes input into the parse buffer per the V3 spec.
This commit is contained in:
Jared Miller 2026-02-09 20:57:15 -05:00
parent 205e2716dd
commit fb8cbf7219
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
3 changed files with 27 additions and 5 deletions

View file

@ -43,7 +43,7 @@ class ZCpuRestart(ZCpuError):
class ZCpu: class ZCpu:
def __init__( def __init__(
self, zmem, zopdecoder, zstack, zobjects, zstring, zstreammanager, zui self, zmem, zopdecoder, zstack, zobjects, zstring, zstreammanager, zui, zlexer
): ):
self._memory = zmem self._memory = zmem
self._opdecoder = zopdecoder self._opdecoder = zopdecoder
@ -52,6 +52,7 @@ class ZCpu:
self._string = zstring self._string = zstring
self._streammanager = zstreammanager self._streammanager = zstreammanager
self._ui = zui self._ui = zui
self._lexer = zlexer
def _get_handler(self, opcode_class, opcode_number): def _get_handler(self, opcode_class, opcode_number):
try: try:
@ -556,8 +557,10 @@ class ZCpu:
pass pass
def op_verify(self, *args): def op_verify(self, *args):
"""TODO: Write docstring here.""" """Verify story file checksum. Branch if checksum matches."""
raise ZCpuNotImplemented expected_checksum = self._memory.read_word(0x1C)
actual_checksum = self._memory.generate_checksum()
self._branch(expected_checksum == actual_checksum)
def op_piracy(self, *args): def op_piracy(self, *args):
"""TODO: Write docstring here.""" """TODO: Write docstring here."""
@ -619,9 +622,22 @@ class ZCpu:
self._memory[text_buffer_addr + 1 + len(text)] = 0 self._memory[text_buffer_addr + 1 + len(text)] = 0
# Tokenize if parse buffer provided # Tokenize if parse buffer provided
# Note: ZLexer not yet wired up - tokenization will be added when needed
if parse_buffer_addr != 0: if parse_buffer_addr != 0:
pass # TODO: add tokenization when ZLexer is integrated max_words = self._memory[parse_buffer_addr]
tokens = self._lexer.parse_input(text)
num_words = min(len(tokens), max_words)
self._memory[parse_buffer_addr + 1] = num_words
offset = 0
for i in range(num_words):
word_str, dict_addr = tokens[i]
# Find word position in text
pos = text.find(word_str, offset)
word_len = len(word_str)
base = parse_buffer_addr + 2 + (i * 4)
self._memory.write_word(base, dict_addr)
self._memory[base + 2] = word_len
self._memory[base + 3] = pos + 1 # 1-indexed from start of text buffer
offset = pos + word_len
def op_sread_v4(self, *args): def op_sread_v4(self, *args):
"""TODO: Write docstring here.""" """TODO: Write docstring here."""

View file

@ -7,6 +7,7 @@
from . import zlogging from . import zlogging
from .zcpu import ZCpu from .zcpu import ZCpu
from .zlexer import ZLexer
from .zmemory import ZMemory from .zmemory import ZMemory
from .zobjectparser import ZObjectParser from .zobjectparser import ZObjectParser
from .zopdecoder import ZOpDecoder from .zopdecoder import ZOpDecoder
@ -33,6 +34,7 @@ class ZMachine:
self._opdecoder.program_counter = self._mem.read_word(0x06) self._opdecoder.program_counter = self._mem.read_word(0x06)
self._ui = ui self._ui = ui
self._stream_manager = ZStreamManager(self._mem, self._ui) self._stream_manager = ZStreamManager(self._mem, self._ui)
self._lexer = ZLexer(self._mem)
self._cpu = ZCpu( self._cpu = ZCpu(
self._mem, self._mem,
self._opdecoder, self._opdecoder,
@ -41,6 +43,7 @@ class ZMachine:
self._stringfactory, self._stringfactory,
self._stream_manager, self._stream_manager,
self._ui, self._ui,
self._lexer,
) )
# --------- Public APIs ----------- # --------- Public APIs -----------

View file

@ -107,6 +107,7 @@ class ZMachineOpcodeTests(TestCase):
Mock(), # string Mock(), # string
Mock(), # stream manager Mock(), # stream manager
self.ui, self.ui,
Mock(), # lexer
) )
def test_op_nop(self): def test_op_nop(self):
@ -356,6 +357,7 @@ class ZMachineObjectOpcodeTests(TestCase):
self.string, self.string,
Mock(), # stream manager Mock(), # stream manager
self.ui, self.ui,
Mock(), # lexer
) )
def test_op_get_sibling_with_sibling(self): def test_op_get_sibling_with_sibling(self):
@ -536,6 +538,7 @@ class ZMachineComplexOpcodeTests(TestCase):
Mock(), # string Mock(), # string
Mock(), # stream manager Mock(), # stream manager
self.ui, self.ui,
Mock(), # lexer
) )
def test_op_sread_v3_basic_input(self): def test_op_sread_v3_basic_input(self):