Port 4 medium opcodes to hybrid z-machine interpreter
Implements op_print_addr, op_print_num, op_ret, and op_show_status following TDD approach with tests first. Each opcode now properly decodes/prints text, handles signed numbers, returns from routines, or acts as a no-op as appropriate.
This commit is contained in:
parent
1b9d84f41a
commit
c76ee337d3
2 changed files with 51 additions and 10 deletions
|
|
@ -405,8 +405,9 @@ class ZCpu:
|
|||
self._write_result(val, store_addr=variable)
|
||||
|
||||
def op_print_addr(self, string_byte_address):
|
||||
"""TODO: Write docstring here."""
|
||||
raise ZCpuNotImplemented
|
||||
"""Print the z-encoded string at the given byte address."""
|
||||
text = self._string.get(string_byte_address)
|
||||
self._ui.screen.write(text)
|
||||
|
||||
def op_call_1s(self, routine_address):
|
||||
"""Call the given routine and store the return value."""
|
||||
|
|
@ -421,9 +422,10 @@ class ZCpu:
|
|||
shortname = self._objects.get_shortname(obj)
|
||||
self._ui.screen.write(shortname)
|
||||
|
||||
def op_ret(self, *args):
|
||||
"""TODO: Write docstring here."""
|
||||
raise ZCpuNotImplemented
|
||||
def op_ret(self, value):
|
||||
"""Return from the current routine with the given value."""
|
||||
pc = self._stackmanager.finish_routine(value)
|
||||
self._opdecoder.program_counter = pc
|
||||
|
||||
def op_jump(self, offset):
|
||||
"""Jump unconditionally to the given branch offset. This
|
||||
|
|
@ -535,8 +537,8 @@ class ZCpu:
|
|||
self._ui.screen.write("\n")
|
||||
|
||||
def op_show_status(self, *args):
|
||||
"""TODO: Write docstring here."""
|
||||
raise ZCpuNotImplemented
|
||||
"""Update status line (V3 only). No-op in this implementation."""
|
||||
pass
|
||||
|
||||
def op_verify(self, *args):
|
||||
"""TODO: Write docstring here."""
|
||||
|
|
@ -592,9 +594,10 @@ class ZCpu:
|
|||
"""Output the given ZSCII character."""
|
||||
self._ui.screen.write(self._string.zscii.get([char]))
|
||||
|
||||
def op_print_num(self, *args):
|
||||
"""TODO: Write docstring here."""
|
||||
raise ZCpuNotImplemented
|
||||
def op_print_num(self, value):
|
||||
"""Print a signed 16-bit number as text."""
|
||||
signed_value = self._make_signed(value)
|
||||
self._ui.screen.write(str(signed_value))
|
||||
|
||||
def op_random(self, n):
|
||||
"""Generate a random number, or seed the PRNG.
|
||||
|
|
|
|||
|
|
@ -250,6 +250,44 @@ class ZMachineOpcodeTests(TestCase):
|
|||
# Stack should be empty
|
||||
self.assertEqual(len(self.stack.stack), 0)
|
||||
|
||||
def test_op_print_addr(self):
|
||||
"""Test print_addr decodes and prints text at byte address."""
|
||||
# Configure mock string decoder to return a known string
|
||||
self.cpu._string.get = Mock(return_value="Hello, world!")
|
||||
# Print text at address 0x5000
|
||||
self.cpu.op_print_addr(0x5000)
|
||||
# Should have called string decoder with the address
|
||||
self.cpu._string.get.assert_called_once_with(0x5000)
|
||||
# Should have written the decoded text
|
||||
self.ui.screen.write.assert_called_once_with("Hello, world!")
|
||||
|
||||
def test_op_print_num_positive(self):
|
||||
"""Test print_num prints positive number."""
|
||||
self.cpu.op_print_num(42)
|
||||
self.ui.screen.write.assert_called_once_with("42")
|
||||
|
||||
def test_op_print_num_negative(self):
|
||||
"""Test print_num prints negative number."""
|
||||
# -1 as unsigned 16-bit is 65535
|
||||
self.cpu.op_print_num(65535)
|
||||
self.ui.screen.write.assert_called_once_with("-1")
|
||||
|
||||
def test_op_print_num_zero(self):
|
||||
"""Test print_num prints zero."""
|
||||
self.cpu.op_print_num(0)
|
||||
self.ui.screen.write.assert_called_once_with("0")
|
||||
|
||||
def test_op_ret(self):
|
||||
"""Test ret returns from routine with value."""
|
||||
self.cpu.op_ret(42)
|
||||
# Should have set PC to caller's address (0x1000 from mock)
|
||||
self.assertEqual(self.cpu._opdecoder.program_counter, 0x1000)
|
||||
|
||||
def test_op_show_status(self):
|
||||
"""Test show_status is a no-op (V3 only, not needed in MUD)."""
|
||||
# Should just not raise an exception
|
||||
self.cpu.op_show_status()
|
||||
|
||||
|
||||
class MockObjectParser:
|
||||
"""Mock object parser for testing CPU opcodes."""
|
||||
|
|
|
|||
Loading…
Reference in a new issue