def test_zword_bitwise_operations(): zw = ZWord.from_unsigned_int(0b1010_1010_1010_1010) << 1 assert zw.unsigned_int == 0b0101_0101_0101_0100 zw = ZWord.from_unsigned_int(0b0000_1111_0000_1111) << 4 assert zw.unsigned_int == 0b1111_0000_1111_0000 zw = ZWord.from_unsigned_int(0b0000_1111_1111_0000) >> 4 assert zw.unsigned_int == 0b0000_0000_1111_1111 zw = ZWord.from_unsigned_int(0b0001_0000) >> 1 assert zw.unsigned_int == 0b0000_1000 zw = ZWord.from_unsigned_int(0b1111_1111) & 0b0101_0101 assert zw.unsigned_int == 0b0101_0101 zw = ZWord.from_unsigned_int(0b1111_0000) | 0b0000_1111 assert zw.bytes == b'\x00\xff' zw = ~ZWord.from_unsigned_int(0b0101_0101) assert zw.unsigned_int == 0b1111_1111_1010_1010 zw = ~ZWord.from_unsigned_int(0b0000_1111) assert zw.unsigned_int == 0b1111_1111_1111_0000 zw = ZWord.from_unsigned_int(0b1111_0000_1111_0000) assert zw.is_bit_set(15) is True assert zw.is_bit_set(0) is False assert zw.is_bit_set(4) is True assert zw.is_bit_clear(15) is False assert zw.is_bit_clear(0) is True assert zw.is_bit_clear(4) is False assert ZWord.from_int(-1) == ZWord(b'\xff\xff') assert ZWord.from_int(-1) != ZWord(b'\x00\x00')
def test_set_own_property_v5(zork_v5_obj_table: ZMachineObjectTable): # Property #46 is a two-byte property zorkmid = zork_v5_obj_table.object(122) zorkmid.properties.set(46, ZWord(b'\x12\x34')) assert ZWord(b'\x12\x34') == zorkmid.properties.value(46) you = zork_v5_obj_table.object(21) with pytest.raises(ZMachineIllegalOperation, match='size 8 with a value of size '): you.properties.set(46, ZWord(b'\x12\x34'))
def test_stack_quetzal_chunk_with_routine_calls(): new_stack = ZMachineStack() new_stack.push_routine_call(PC(0x1234), 2, ZByte.from_int(0), *[ZWord.from_int(1), ZWord.from_int(2)]) new_stack.push(ZWord.from_int(1955)) new_stack.push(ZWord.from_int(1985)) quetzal_stack = StacksQuetzalChunk.create(new_stack) restored_stack_chunk, next_chunk = StacksQuetzalChunk.read( quetzal_stack.bytes()) assert next_chunk == len(quetzal_stack.bytes()) assert restored_stack_chunk.saved_stack( ).frames == new_stack.frames, 'initial stack and saved stack are not the same'
def test_short_form_opcodes(opcodes_v3: ZMachineOpcodeParserV3): # One operand of type LARGE_CONSTANT jump, next_pc = opcodes_v3.parse(0x85ab) assert jump.name == 'jump' assert len(jump.operand_types) == 1 assert len(jump.operands) == 1 assert jump.operand_types[0] == ZMachineOperandTypes.LARGE_CONSTANT assert jump.operands[0] == ZWord(bytes([0x00, 0x0b])) assert next_pc == 0x85ab + 3 # One operand of type VARIABLE load, next_pc = opcodes_v3.parse(0x7d58) assert load.name == 'load' assert len(load.operand_types) == 1 assert len(load.operands) == 1 assert load.operand_types[0] == ZMachineOperandTypes.VARIABLE assert load.operands[0] == ZByte(b'\x00') assert next_pc == 0x7d58 + 3 # Zero operands print_ret, next_pc = opcodes_v3.parse(0x4fb1) assert print_ret.name == 'print_ret' assert print_ret.operand_types is None assert print_ret.operands is None assert next_pc == 0x4fc4
def test_own_properties_v5(zork_v5_obj_table: ZMachineObjectTable): door = zork_v5_obj_table.object(127) # Get own properties own_properties = door.properties.all() assert 46 in own_properties assert own_properties[46] == ZWord(b'\x4d\xff') assert 45 in own_properties assert own_properties[45] == ZWord(b'\x3e\x24') assert 44 in own_properties assert own_properties[44].hex() == '50364a60' assert own_properties[46] == door.properties.value(46) assert own_properties[45] == door.properties.value(45) assert own_properties[44] == door.properties.value(44)
def test_push_peek_and_pop(stack: ZMachineStack): a = ZWord.from_int(42) b = ZWord(bytes([0, 69])) c = ZByte.from_int(-3) stack.push(a) stack.push(b) stack.push(c) assert stack.peek() == c.pad() assert stack.pop() == c.pad() assert stack.peek() == b assert stack.pop() == b assert stack.peek() == a assert stack.pop() == a
def test_set_own_property_v3(zork_v3_obj_table: ZMachineObjectTable): # Property #16 is a single-byte property pair_of_hands = zork_v3_obj_table.object(1) # Set with a single byte pair_of_hands.properties.set(16, ZByte(b'\x42')) assert ZByte(b'\x42') == pair_of_hands.properties.value(16), \ 'a single byte property is set with a single byte value' # Set with two bytes pair_of_hands.properties.set(16, ZWord.from_int(-6)) assert ZByte.from_int(-6) == pair_of_hands.properties.value(16), \ 'a single byte property is set with a two byte value' # Property #18 is a two-byte property zorkmid = zork_v3_obj_table.object(2) zorkmid.properties.set(18, ZWord(b'\x12\x34')) assert ZWord(b'\x12\x34') == zorkmid.properties.value(18)
def test_own_properties_v3(zork_v3_obj_table: ZMachineObjectTable): door = zork_v3_obj_table.object(181) # Get own properties own_properties = door.properties.all() assert 18 in own_properties assert own_properties[18] == ZWord(b'\x3f\x9d') assert 17 in own_properties assert own_properties[17] == ZWord(b'\x6d\x57') assert 16 in own_properties assert own_properties[16] == ZWord(b'\xc9\xca') assert own_properties[18] == door.properties.get(18).value assert own_properties[17] == door.properties.get(17).value assert own_properties[16] == door.properties.get(16).value assert own_properties[18] == door.properties.value(18) assert own_properties[17] == door.properties.value(17) assert own_properties[16] == door.properties.value(16)
def test_variable_form_opcodes(opcodes_v3: ZMachineOpcodeParserV3): # Test variable form of 2OP opcode instructions call, next_pc = opcodes_v3.parse(0x5157) assert call.name == 'je' assert len(call.operand_types) == 3 assert call.operand_types == (ZMachineOperandTypes.VARIABLE, ZMachineOperandTypes.SMALL_CONSTANT, ZMachineOperandTypes.SMALL_CONSTANT) assert call.operands == (ZByte(b'\x88'), ZByte(b'\x32'), ZByte(b'\x12')) assert next_pc == 0x5157 + 6 # Test unique variable opcode instructions call, next_pc = opcodes_v3.parse(0x4f05) assert call.name == 'call' assert len(call.operand_types) == 3 assert call.operand_types == (ZMachineOperandTypes.LARGE_CONSTANT, ZMachineOperandTypes.LARGE_CONSTANT, ZMachineOperandTypes.LARGE_CONSTANT) assert len(call.operands) == 3 assert call.operands == (ZWord(bytes([0x2a, 0x39])), ZWord(bytes([0x80, 0x10])), ZWord(bytes([0xff, 0xff]))) assert next_pc == 0x4f05 + 9
def test_zword_to_zbyte_math(): zw = ZWord.from_int(129) + ZByte.from_int(127) assert zw.bytes == b'\x01\x00', 'ZWord + ZByte' zw = ZWord.from_int(0) - ZByte.from_int(127) assert zw.int == -127, 'ZWord - ZByte' zw = ZWord.from_int(-4) * ZByte.from_int(64) assert zw.int == -256, 'ZWord * ZByte' zw = ZWord.from_int(126) // ZByte.from_int(2) assert zw.int == 63, 'ZWord/ZByte integer division' with pytest.raises(ZMachineIllegalOperation, match='Divide by zero'): ZWord.from_int(45) // ZByte.from_int(0) with pytest.raises(ZMachineIllegalOperation): ZWord.from_int(4) / ZByte.from_int(8)
def test_umem_quetzal_chunk(zork_v3_obj_table: ZMachineObjectTable): original_memory = bytes(zork_v3_obj_table._memory) cretin = zork_v3_obj_table.object(4) cretin.properties.set(18, ZWord(bytes.fromhex('ffff'))) prop_address = cretin.properties.get(18).value_address cmem_chunk = UMemQuetzalChunk(zork_v3_obj_table._memory) restored_chunk, offset = UMemQuetzalChunk.read(cmem_chunk.bytes()) assert offset == len(cmem_chunk.bytes()) restored_memory = restored_chunk.saved_memory() for i in range(len(restored_memory)): if i not in [prop_address, prop_address + 1]: assert restored_memory[i] == original_memory[ i], f'memory different at {hex(i)} should be different at {hex(prop_address)}' else: assert restored_memory[i] != original_memory[ i], f'memory should be different at {hex(i)} for prop at address {hex(prop_address)}'
def test_zbyte_to_zword_math(): zw = ZByte.from_int(0) + ZWord.from_int(256) assert zw.bytes == b'\x01\x00', 'ZByte + ZWord' zw = ZByte.from_int(0) - ZWord.from_int(129) assert zw.int == -129, 'ZByte - ZWord' zw = ZByte.from_int(-2) * ZWord.from_int(128) assert zw.int == -256, 'ZByte * ZWord' zw = ZByte.from_int(126) // ZWord.from_int(2) assert zw.int == 63, 'ZByte/ZWord integer division' with pytest.raises(ZMachineIllegalOperation, match='Divide by zero'): ZByte.from_int(45) // ZWord.from_int(0) with pytest.raises(ZMachineIllegalOperation): ZByte.from_int(4) / ZWord.from_int(8) zw = ZByte.from_int(127) % ZWord.from_int(2) assert zw.int == 1 zw = ZByte.from_int(-128) % ZWord.from_int(2) assert zw.int == 0
def test_zword_write_memory(zork1_v3_data: memoryview): ZWord.from_unsigned_int(0x4269).write(zork1_v3_data, 6) assert zork1_v3_data[6:8] == b'\x42\x69'
def test_zword_zword_math(): zw = ZWord.from_int(0) + ZWord.from_int(512) assert zw.bytes == b'\x02\x00', 'ZWord + ZWord' zw = ZWord.from_int(0) - ZWord.from_int(1) assert zw.bytes == b'\xff\xff', 'ZWord - ZWord' zw = ZWord.from_int(-32768) - ZWord.from_int(1) assert zw.bytes == b'\x7f\xff', 'ZWord arithmetic truncation' zw = ZWord.from_int(-2) * ZWord.from_int(8) assert zw.int == -16, 'ZWord * ZWord' zw = ZWord.from_int(32767) * ZWord.from_int(32767) assert zw.bytes == b'\x00\x01', 'ZWord multiplication truncation' zw = ZWord.from_int(3) // ZWord.from_int(2) assert zw.bytes == b'\x00\x01', 'ZWord integer division' with pytest.raises(ZMachineIllegalOperation, match='Divide by zero'): ZWord.from_int(45) // ZWord.from_int(0) with pytest.raises(ZMachineIllegalOperation): ZWord.from_int(4) / ZWord.from_int(8) zw = ZWord(b'\x00\x01').inc() assert zw.bytes == b'\x00\x02' zw = ZWord(b'\xff\xfe').inc() assert zw.bytes == b'\xff\xff' zw = ZWord(b'\xff\xff').inc() assert zw.bytes == b'\x00\x00', 'Rollover on increment' zw = ZWord.from_int(500).dec() assert zw.int == 499 zw = ZWord(b'\xff\xff').dec() assert zw.bytes == b'\xff\xfe' zw = ZWord(b'\x00\x00').dec() assert zw.bytes == b'\xff\xff', 'Rollover on decrement' zw = ZWord.from_int(32767) % ZWord.from_int(2) assert zw.int == 1 zw = ZWord.from_int(-32768) % ZWord.from_int(2) assert zw.int == 0
def test_instantiate_zword(): zw = ZWord(bytes.fromhex('1234')) assert zw.bytes == b'\x12\x34', 'Instantiate ZWord from bytes' zw = ZWord(memoryview(bytes.fromhex('4242'))) assert zw.bytes == b'\x42\x42', 'Instantiate ZWord from memoryview' zw = ZWord.from_int(-1) assert zw.bytes == b'\xff\xff', 'Instantiate ZWord from int' zw = ZWord.from_unsigned_int(0xf0f0) assert zw.bytes == b'\xf0\xf0', 'Instantiate ZWord from unsigned int' with pytest.raises(TypeError): ZWord(22) with pytest.raises(ValueError): ZWord(b'\x12') with pytest.raises(ValueError): ZWord(b'') with pytest.raises(ValueError): ZWord.from_int(-32769) with pytest.raises(ValueError): ZWord.from_int(32768) with pytest.raises(ValueError): ZWord.from_unsigned_int(-1) with pytest.raises(ValueError): ZWord.from_unsigned_int(65536)
def read_token_block(memory: memoryview, parse_buf: int, token_num: int): block_offset = parse_buf + 2 + (token_num * 4) return TokenBlock( ZWord(memory, block_offset).unsigned_int, memory[block_offset + 2], memory[block_offset + 3])
def test_routine_calls(stack: ZMachineStack): ret_a = PC(1234) ret_var_a = ZByte.from_int(2) a_locals = [ ZWord.from_int(1), ZWord.from_int(2), ZWord.from_int(3), ZByte.from_int(4) ] ret_b = PC(5678) ret_var_b = ZByte.from_unsigned_int(45) b_locals = [ZWord.from_int(42), ZWord.from_int(56)] stack.push_routine_call(ret_a, len(a_locals), ret_var_a, *a_locals) assert stack.local_var(0) == a_locals[0] assert stack.local_var(1) == a_locals[1] assert stack.local_var(2) == a_locals[2] stack.set_local_var(3, ZWord.from_int(34)) assert stack.local_var(3) == ZWord.from_int(34) stack.push(ZWord.from_int(69)) stack.push_routine_call(ret_b, len(b_locals), ret_var_b, *b_locals) assert stack.local_var(0) == b_locals[0] assert stack.local_var(1) == b_locals[1] stack.push(ZWord.from_int(342)) ret_pc, ret_var = stack.pop_routine_call() assert ret_pc == ret_b assert ret_var == ret_var # Now ensure the previous call's variables are still correct assert stack.local_var(0) == a_locals[0] assert stack.local_var(1) == a_locals[1] assert stack.local_var(2) == a_locals[2] assert stack.local_var(3) == ZWord.from_int(34) assert stack.pop() == ZWord.from_int(69) ret_pc, ret_var = stack.pop_routine_call() assert ret_pc == ret_a assert ret_var == ret_var_a