Esempio n. 1
0
    def _write_values_to_memory(
            self,
            vm: Chip8VirtualMachine,
            max_register: int,
            write_dest: int = 0xA00,
            value_start: int = 128
    ) -> None:
        """

        Helper function that executes a bulk register save operation.

        Will write value_start +0, ... value_start + max_register to
        the first max_register registers.

        Then runs FX55, with max_register as X.

        :param vm: vm object to write to
        :param max_register: the highest register that will be saved
        :param value_start: what the start of the written values will be
        :return:
        """

        # set up the registers with unique values that can be checked
        # for correct write order in memory.
        for index in range(0, max_register + 1):
            vm.v_registers[index] = value_start + index

        # set up where the registers will be stored to
        vm.i_register = write_dest

        load_and_execute_instruction(
            vm, 0xF055, x=max_register
        )
Esempio n. 2
0
def test_00e0_calls_vram_clear_screen():
    """00E0 causes VM to call screen clear on video ram"""
    vm = VM()
    vm.video_ram = Mock(VideoRam)

    load_and_execute_instruction(vm, 0x00E0)
    assert vm.video_ram.clear_screen.called_once()
Esempio n. 3
0
    def _load_from_memory(
            self,
            vm: Chip8VirtualMachine,
            max_register: int,
            read_src: int = 0xA00,
            value_start: int = 128
    ) -> None:
        """

        Helper function that executes a bulk register save operation.

        Will write value_start +0, ... value_start + max_register to
        the first max_register registers.

        Then runs FX55, with max_register as X.

        :param vm: vm object to write to
        :param max_register: the highest register that will be saved
        :param value_start: what the start of the written values will be
        :return:
        """

        # set up the memory with values to read into registers
        for register_index in range(0, max_register + 1):
            vm.memory[read_src + register_index] = 128 + register_index

        # set up where the registers will be stored to
        vm.i_register = read_src

        load_and_execute_instruction(
            vm, 0xF065, x=max_register
        )
Esempio n. 4
0
    def test_8xy0_same_value_for_x_and_y_leaves_reg_unchanged(self, reg_index):
        """8xy0 does not alter the value when x & y are the same"""
        vm = VM()

        vm.v_registers[reg_index] = 5
        load_and_execute_instruction(vm, 0x8000, x=reg_index, y=reg_index)

        assert vm.v_registers[reg_index] == 5
Esempio n. 5
0
    def test_data_past_ram_end_raises_indexerror(self, ram_size, location):
        vm = VM(memory_size=ram_size)

        # 1 after end of RAM
        data_len = 1 + ram_size - location

        with pytest.raises(IndexError):
            vm.load_to_memory(b"a" * data_len, location)
Esempio n. 6
0
    def test_6xy0_skips_next_if_vx_not_equal_vy(self, x, y):
        vm = VM()
        vm.v_registers[x] = 0
        vm.v_registers[y] = 1

        load_and_execute_instruction(vm, 0x9000, x=x, y=y)

        assert vm.program_counter ==\
               DEFAULT_EXECUTION_START + (2 * INSTRUCTION_LENGTH)
Esempio n. 7
0
    def test_8xy0_leaves_original_alone(self, x, y):
        """8xy0 leaves VY alone"""

        vm = VM()
        vm.v_registers[x] = 2
        vm.v_registers[y] = 3

        load_and_execute_instruction(vm, 0x8000, x=x, y=y)

        assert other_registers_untouched(vm, {x, y})
Esempio n. 8
0
    def test_8xy6_sets_vf_to_least_significant_digit_of_vy(self, x, y, a, b):
        """8xy6 sets VF to least significant"""
        vm = VM()

        vm.v_registers[x] = a
        vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x8006, x=x, y=y)

        assert vm.v_registers[0xF] == b & 1
Esempio n. 9
0
    def test_8xye_sets_vf_to_most_significant_digit_of_vy(self, x, y, a, b):
        """8xyE sets VF to most significant digit of VY"""
        vm = VM()

        vm.v_registers[x] = a
        vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x800E, x=x, y=y)

        assert vm.v_registers[0xF] == bool(b & 0b10000000)
Esempio n. 10
0
    def test_8xy2_leaves_other_registers_alone(self, x, y):
        """8xy2 leaves registers other than VX and VY alone"""

        vm = VM()
        vm.v_registers[x] = 0b10101010
        vm.v_registers[y] = 0b01010101

        load_and_execute_instruction(vm, 0x8002, x=x, y=y)

        assert other_registers_untouched(vm, (x, y))
Esempio n. 11
0
 def test_4xkk_does_not_skip_next_instruction_if_vx_eq_kk(
         self, vx: int, vx_and_kk_value: int, exec_start: int):
     vm = VM(execution_start=exec_start)
     vm.v_registers[vx] = vx_and_kk_value
     load_and_execute_instruction(vm,
                                  0x4000,
                                  load_point=exec_start,
                                  x=vx,
                                  kk=vx_and_kk_value)
     assert vm.program_counter == exec_start + INSTRUCTION_LENGTH
Esempio n. 12
0
    def test_9xy0_doesnt_skip_next_if_vx_eq_vy(self, x, y, equal_val):

        vm = VM()
        vm.v_registers[x] = 7
        vm.v_registers[y] = 7

        load_and_execute_instruction(vm, 0x9000, x=x, y=y)

        assert vm.program_counter ==\
               DEFAULT_EXECUTION_START + INSTRUCTION_LENGTH
Esempio n. 13
0
def test_00ee_decrements_stack_size(call_location):
    """Return instruction decrements stack size"""
    vm = VM()
    vm.stack_call(call_location)

    load_and_execute_instruction(
        vm,
        0x00EE,  # return instruction
        load_point=call_location)
    assert vm.stack_size == 0
Esempio n. 14
0
def test_00ee_returns_to_last_location_plus_two(call_location):
    """Return instruction returns to last location on the stack"""
    vm = VM()
    vm.stack_call(call_location)

    load_and_execute_instruction(
        vm,
        0x00EE,  # return instruction
        load_point=call_location)
    assert vm.program_counter == DEFAULT_EXECUTION_START + 2
Esempio n. 15
0
    def test_8xye_sets_vx_to_vy_shifted_left_one(self, x, y, a, b):
        """8xyE sets VX = VY >> 1"""

        vm = VM()

        vm.v_registers[x] = a
        vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x800E, x=x, y=y)

        assert vm.v_registers[x] == 0xFF & (b << 1)
Esempio n. 16
0
    def test_8xy6_sets_vx_to_vy_shifted_right_one(self, x, y, a, b):
        """8xy6 sets VX = VY >> 1"""

        vm = VM()

        vm.v_registers[x] = a
        vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x8006, x=x, y=y)

        assert vm.v_registers[x] == b >> 1
Esempio n. 17
0
    def test_8xy5_sets_vf_to_not_borrow(self, x, y, a, b):
        """8xy5 sets VF to 1 if VX >= VY, otherwise 0"""
        vm = VM()

        vm.v_registers[x] = a
        if x != y:
            vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x8005, x=x, y=y)

        assert vm.v_registers[0xF] == int(a >= b)
Esempio n. 18
0
    def test_8xy4_sets_vf_to_carry_bit(self, x, y, a, b):
        """8xy4 sets VF to 1 if VX + VY > 255, otherwise 0"""
        vm = VM()

        vm.v_registers[x] = a
        if x != y:
            vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x8004, x=x, y=y)

        assert vm.v_registers[0xF] == int(a + b > 255)
Esempio n. 19
0
 def test_4xkk_skips_next_instruction_if_vx_neq_kk(self, vx: int, vx_value,
                                                   kk_value, exec_start):
     vm = VM(execution_start=exec_start)
     vm.v_registers[vx] = vx_value
     load_and_execute_instruction(
         vm,
         0x4000,
         load_point=exec_start,
         x=vx_value,
         kk=kk_value,
     )
     assert vm.program_counter == exec_start + (2 * INSTRUCTION_LENGTH)
Esempio n. 20
0
    def test_8xy0_sets_vx_to_vy(self, x, y):
        """8xy0 sets VX = VX OR VY"""

        vm = VM()
        original_x_value = 2
        default_y_value = 3

        vm.v_registers[x] = original_x_value
        vm.v_registers[y] = default_y_value

        load_and_execute_instruction(vm, 0x8000, x=x, y=y)

        assert vm.v_registers[x] == default_y_value
Esempio n. 21
0
    def test_8xye_leaves_other_registers_alone_unless_theyre_vf(self, x, y):
        """8xy6 leaves registers other than VX alone except for VF"""

        vm = VM()

        vm.v_registers[x] = 2
        vm.v_registers[y] = 1

        load_and_execute_instruction(vm, 0x800E, x=x, y=y)

        # make sure we don't check VF since it should always be set
        touched = {x, 0xF}

        assert other_registers_untouched(vm, touched)
Esempio n. 22
0
    def test_8xy2_sets_vx_to_and_of_vx_and_vy(self, x, y):
        """8xy2 sets VX to VX AND VY"""

        vm = VM()

        left_half_filled = 0b11110000

        vm.v_registers[x] = 0xFF
        vm.v_registers[y] = left_half_filled

        # set vx = vx AND 0x11110000. If vx is already set to 0x11110000,
        # the value will still be 0x11110000 after the instruction runs.
        load_and_execute_instruction(vm, 0x8002, x=x, y=y)

        assert vm.v_registers[x] == left_half_filled
Esempio n. 23
0
def test_bnnn_jumps_to_address_plus_offset(memory_location, v0):
    """Bnnn sets program counter to nnn + v0"""
    vm = VM()

    assert vm.program_counter == DEFAULT_EXECUTION_START
    assert vm.stack_size == 0

    vm.v_registers[0] = v0
    load_and_execute_instruction(
        vm,
        0xB000,
        nnn=memory_location,
    )
    assert vm.program_counter == memory_location + v0
    assert vm.stack_size == 0
Esempio n. 24
0
    def test_8xy3_sets_vx_to_xor_of_vx_and_vy(self, x, y):
        """8xy3 sets VX to VX XOR VY"""

        vm = VM()

        vm.v_registers[x] = 0b10101111
        vm.v_registers[y] = 0b01011111

        load_and_execute_instruction(vm, 0x8003, x=x, y=y)

        if x != y:
            assert vm.v_registers[x] == 0b11110000

        else:  # any value xor itself yields zero
            assert vm.v_registers[x] == 0
Esempio n. 25
0
 def setup_vm(self, x: int, key: int) -> VM:
     """
     Helper function to set up the VM and template instructions
     """
     vm = VM()
     load_multiple(
         vm,
         (0xE09E, {
             'x': x
         }),
         # set the I register. This command can't touch that I, and
         # I is set to zero on VM initialization. Therefore, it is
         # safe to use I as a test condition for ex9e.
         0xA0FF)
     vm.v_registers[x] = key
     return vm
Esempio n. 26
0
    def test_8xy4_sets_vx_to_sum_of_vx_and_vy(self, x, y, a, b):
        """8xy4 sets VX to VX + VY, modulo 256"""

        vm = VM()

        vm.v_registers[x] = a

        if x != y:
            vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x8004, x=x, y=y)

        if x != y:
            assert vm.v_registers[x] == (a + b) % 256
        else:
            assert vm.v_registers[x] == (a * 2) % 256
Esempio n. 27
0
    def test_8xy1_sets_vx_to_logical_or_of_vx_and_vy(self, x, y):
        """8xy1 sets VX = VX OR VY"""

        vm = VM()
        original_x_value = 0b10101010
        default_y_value = 0b01010101

        vm.v_registers[x] = original_x_value
        if y != x:
            vm.v_registers[y] = default_y_value

        load_and_execute_instruction(vm, 0x8001, x=x, y=y)

        if y != x:
            assert vm.v_registers[x] == original_x_value | default_y_value
        else:
            assert vm.v_registers[x] == original_x_value
Esempio n. 28
0
    def test_8xy7_sets_vx_to_vy_minus_vx(self, x, y, a, b):
        """8xy7 sets VX = VY - VX, clamped to 0 minimum"""

        vm = VM()

        vm.v_registers[x] = a

        if x != y:
            vm.v_registers[y] = b

        load_and_execute_instruction(vm, 0x8007, x=x, y=y)

        if x != y:
            assert vm.v_registers[x] == max(b - a, 0)

        else:  # any value minus itself yields zero
            assert vm.v_registers[x] == 0
Esempio n. 29
0
    def test_fx55_writes_registers_to_ram(self, max_register):
        """Fx55 writes register contents to RAM correctly"""
        vm = Chip8VirtualMachine()

        self._write_values_to_memory(
            vm, max_register, 0xA00, 128
        )

        for index in range(0, max_register+1):
            assert vm.memory[0xA00 + index] == 128 + index
Esempio n. 30
0
    def test_fx65_loads_ram_to_registers(self, max_register):
        """Fx65 reads ram contents to registers correctly"""
        vm = Chip8VirtualMachine()

        self._load_from_memory(
            vm, max_register, 0xA00, 128
        )

        for register_index in range(0, max_register+1):
            assert vm.v_registers[register_index] == 128 + register_index