示例#1
0
    def test_poke(self):
        """
        Tests address range and data for poke
        """
        e = ElfCPU()
        e.load_string('0,1,2,3,4,5,6,7,8,9')

        # TypeError
        with self.assertRaises(TypeError):
            # noinspection PyTypeChecker
            e.poke('x', 2)
        # Above memory range
        with self.assertRaises(ValueError):
            e.poke(2**65, 2)
        # Below memory range
        with self.assertRaises(ValueError):
            e.poke(-1, 2)

        # Value
        with self.assertRaises(ValueError):
            e.poke(0, 2**64 + 1)

        self.assertEqual(e.poke(0, 99), 99)
        self.assertEqual(e.poke(9, 88), 88)
        self.assertEqual(e.peek(0), 99)
        self.assertEqual(e.peek(9), 88)
示例#2
0
 def test_invalid_instr(self):
     """
     Tests for invalid op code
     """
     e = ElfCPU()
     e.load_string('123456789')
     with self.assertRaises(InvalidInstructionError):
         e.execute()
示例#3
0
 def test_load_string_types(self):
     """
     Checks for TypeError
     """
     e = ElfCPU()
     with self.assertRaises(TypeError):
         # noinspection PyTypeChecker
         e.load_string(0)
     e.load_string('1,2,3,4')
示例#4
0
 def test_gpf(self):
     """
     Tests for a general protection fault by allowing the program counter to
     go past the end of the memory.
     """
     e = ElfCPU()
     # Jump to 2**20, the last memory address
     e.load_string('1106,0,1048576')
     with self.assertRaises(ProtectionFaultError):
         e.execute()
示例#5
0
 def test_halt(self):
     """
     Tests for the halt op code
     """
     e = ElfCPU()
     e.load_string('1,0,0,0,99')
     e.step()
     self.assertFalse(e.is_halted)
     e.step()
     self.assertTrue(e.is_halted)
示例#6
0
    def test_op_jmp_true(self):
        """
        Tests jump if true op code
        """

        e = ElfCPU()
        """
        Tests address 8 (which is 1) if it is non-zero.  Since this is
        true, it jumps to the value of address 9 (which is 7).  This
        terminates the program.
        """
        e.load_string('5,8,9,1101,1,1,8,99,1,7')
        e.execute()
        self.assertEqual(e.peek(8), 1)
        """
        Tests immediate value 8 if it is non-zero.  Since it is
        true, jump to immediate address 7 which terminates.
        """
        e.load_string('1105,8,7,1101,1,1,8,99,1,7')
        e.execute()
        self.assertEqual(e.peek(8), 1)
        """
        Tests address 8 (which is 0) if it is non-zero.  Since this is
        false it does not jump and instead adds 1+1 to address 8.
        """
        e.load_string('5,8,9999,11101,1,1,8,99,0,7')
        e.execute()
        self.assertEqual(e.peek(8), 2)
        """
        Tests immediate value 0 if it is non-zero.  Since it is
        false it does not jump and instead adds 1+1 to address 8. 
        """
        e.load_string('1105,0,9999,11101,1,1,8,99,0,7')
        e.execute()
        self.assertEqual(e.peek(8), 2)
示例#7
0
    def test_reset(self):
        """
        Tests for CPU reset
        """
        e = ElfCPU()
        e.load_string('1,0,0,0,99')
        e.execute()
        e.reset()

        # Halted gets cleared
        self.assertFalse(e.is_halted)
        # Program counter goes to 0
        self.assertEqual(e.pc, 0)
        # Memory gets wiped so address 1 becomes invalid
        with self.assertRaises(ValueError):
            e.peek(1)
示例#8
0
    def test_peek(self):
        """
        Tests address range for peek
        """
        e = ElfCPU()
        e.load_string('0,1,2,3,4,5,6,7,8,9')

        # TypeError
        with self.assertRaises(TypeError):
            # noinspection PyTypeChecker
            e.peek('x')
        # Above memory range
        with self.assertRaises(ValueError):
            e.peek(2**65)
        # Below memory range
        with self.assertRaises(ValueError):
            e.peek(-1)

        self.assertEqual(e.peek(0), 0)
        self.assertEqual(e.peek(9), 9)
示例#9
0
    def test_op_relative_base(self):
        """
        Tests the relative base mode op code
        """
        e = ElfCPU()

        # Position
        e.load_string('9,5,204,1,99,6,7,777')
        e.interrupts = True
        # Step over relative mode op
        e.step()
        with self.assertRaises(OutputInterrupt):
            e.execute()
        self.assertEqual(e.output_buffer, 777)

        # Immediate
        e.reset()
        e.load_string('109,5,204,1,99,444,777')
        e.interrupts = True
        # Step over relative mode op
        e.step()
        with self.assertRaises(OutputInterrupt):
            e.execute()
        self.assertEqual(e.output_buffer, 777)

        # Relative
        e.reset()
        e.load_string('209,9,209,6,204,-2,99,5,333,4,6')
        e.interrupts = True
        e.debug = True
        with self.assertRaises(OutputInterrupt):
            e.execute()
        self.assertEqual(e.output_buffer, 333)
示例#10
0
    def test_op_add(self):
        """
        Tests ADD op code
        [dst]:=[a]+[b]
        """
        e = ElfCPU()
        # Invalid address 123456789 for a
        e.load_string('1,123456789,0,0')
        with self.assertRaises(ProtectionFaultError):
            e.step()

        # Invalid address 123456789 for b
        e.load_string('1,0,123456789,0')
        with self.assertRaises(ProtectionFaultError):
            e.step()

        # Invalid address 123456789 for dst
        e.load_string('1,0,0,123456789')
        with self.assertRaises(ProtectionFaultError):
            e.step()

        # 1 + 1 = 2 @ address 0
        e.load_string('1,0,0,0,99')
        e.step()
        self.assertEqual(e.peek(0), 2)

        # 2**64 + 1 = 1 @ address 0 (overflow and wrap)
        #e.load_string('1,5,6,0,99,'+str(2**64)+',1')
        #e.step()
        #self.assertEqual(e.peek(0), 1)

        # [dst]:=a+[b]
        e.load_string('101,44,5,6,99,2,6')
        e.execute()
        self.assertEqual(e.peek(6), 46)

        # [dst]:=[a]+b
        e.load_string('1001,5,50,6,99,2,6')
        e.execute()
        self.assertEqual(e.peek(6), 52)

        # [dst]:=a+b
        e.load_string('1101,5,5,6,99,2,6')
        e.execute()
        self.assertEqual(e.peek(6), 10)

        # [dst]:=r[a]+b
        e.load_string('109,10,1201,0,5,7,99,7,3,3,5')
        e.execute()
        self.assertEqual(e.peek(7), 10)

        # [dst]:=a+r[b]
        e.load_string('109,10,2101,20,0,7,99,7,3,3,10')
        e.execute()
        self.assertEqual(e.peek(7), 30)

        # [dst]:=a+r[b]
        e.load_string('109,10,2101,20,0,7,99,7,3,3,10')
        e.execute()
        self.assertEqual(e.peek(7), 30)

        # r[dst]:=a+b
        e.load_string('109,10,21101,16,16,0,99,7,3,3,7')
        e.execute()
        self.assertEqual(e.peek(10), 32)
示例#11
0
    def test_op_eq(self):
        """
        Tests equals op code
        """

        e = ElfCPU()
        """
        Tests if value of address 5 (10) is equal to value
        of address 6 (10).  Since this is true, write 1 to
        address 10.
        """
        e.load_string('8,8,9,10,99,10,10,-1,10,10,7')
        e.execute()
        self.assertEqual(e.peek(10), 1)
        """
        Tests if value of address 5 (10) is equal to value
        of address 6 (0).  Since this is false, write 0 to
        address 10.
        """
        e.load_string('8,8,9,10,99,10,0,-1,5,6,7')
        e.execute()
        self.assertEqual(e.peek(10), 0)
        """
        Tests if immediate value 10 is equal to immediate value
        10.  Since this is true, write 1 to address 7.
        """
        e.load_string('1108,10,10,7,99,2,3,-1')
        e.execute()
        self.assertEqual(e.peek(7), 1)
        """
        Tests if immediate value of 0 is equal to immediate value
        10.  Since this is false, write 0 to address 7.
        """
        e.load_string('1108,0,10,7,99,2,3,-1')
        e.execute()
        self.assertEqual(e.peek(7), 0)
        """
            if r[a] = r[b]
                r[dst]:=1
            else
                r[dst]:=0
        """
        e.load_string('109,10,22208,0,1,2,99,222,222,222,555,555,1')
        e.execute()
        self.assertEqual(e.peek(12), 1)
        """
            if r[a] < r[b]
                r[dst]:=1
            else
                r[dst]:=0
        """
        e.load_string('109,10,22208,0,1,2,99,222,222,222,-500,100,1')
        e.execute()
        self.assertEqual(e.peek(12), 0)
示例#12
0
 def test_op_cmp_lessthan(self):
     """
     Tests compare less than op code
     """
     e = ElfCPU()
     """
     Tests if value of address 8 (5) is less than value of
     address 9 (10).  Since this is true write 1 to address 10.
     """
     e.load_string('7,8,9,10,99,5,10,-1,5,10,7')
     e.execute()
     self.assertEqual(e.peek(10), 1)
     """
     Tests if value of address 5 (10) is less than value of
     address 6 (5).  Since this is false write 0 to address 10.
     """
     e.load_string('7,8,9,10,99,5,10,-1,10,5,7')
     e.execute()
     self.assertEqual(e.peek(10), 0)
     """
     Tests if immediate value of 5 is less than immediate value of
     10.  Since this is true write 1 to address 7.
     """
     e.load_string('1107,5,10,7,99,0,0,-1')
     e.execute()
     self.assertEqual(e.peek(7), 1)
     """
     Tests if immediate value of 10 is less than immediate value of
     5.  Since this is false write 0 to address 7.
     """
     e.load_string('11107,10,5,7,99,0,0,-1')
     e.execute()
     self.assertEqual(e.peek(7), 0)
     """
         if r[a] < r[b]
             r[dst]:=1
         else
             r[dst]:=0
     """
     e.load_string('109,10,22207,0,1,2,99,222,222,222,100,50,1')
     e.execute()
     self.assertEqual(e.peek(12), 0)
     """
         if r[a] < r[b]
             r[dst]:=1
         else
             r[dst]:=0
     """
     e.load_string('109,10,22207,0,1,2,99,222,222,222,50,100,1')
     e.execute()
     self.assertEqual(e.peek(12), 1)
示例#13
0
    def test_op_output(self):
        """
        Tests output op code

        Use io.StringIO() to capture the output
        """
        e = ElfCPU()

        # Interrupts off
        e.load_string('4,5,104,66,99,55,5')
        e.interrupts = False

        result = None
        with patch('sys.stdout', new=io.StringIO()) as output:
            e.execute()
            result = output.getvalue()
        result = result.splitlines()

        # First is a reference to memory address 5
        self.assertEqual(result[0].strip(), '55')
        # Second is an immediate value
        self.assertEqual(result[1].strip(), '66')

        # Interrupts on

        e.load_string('4,5,104,66,99,55,5')
        e.interrupts = True

        with self.assertRaises(OutputInterrupt):
            e.execute()
        self.assertEqual(e.output_buffer, 55)
        # Don't clear buffer
        with self.assertRaises(OutputOverflow):
            e.execute()

        # Restart test
        e.reset()
        e.load_string('4,5,104,66,99,55,5')
        e.interrupts = True

        with self.assertRaises(OutputInterrupt):
            e.execute()
        self.assertEqual(e.output_buffer, 55)

        # Clear buffer
        del e.output_buffer

        with self.assertRaises(OutputInterrupt):
            e.execute()
        self.assertEqual(e.output_buffer, 66)

        ###############################################
        # Interrupts on RELATIVE MODE
        # Restart test
        e.reset()
        e.load_string('109,5,204,1,99,6,1234')
        e.interrupts = True

        with self.assertRaises(OutputInterrupt):
            e.execute()
        self.assertEqual(e.output_buffer, 1234)
示例#14
0
    def test_op_input(self):
        """
        Tests input op code

        Use unittest.mock.patch to fake the input value
        """
        e = ElfCPU()

        # Interrupts off
        e.load_string('103,3,99,-1')
        e.interrupts = False
        with patch('builtins.input', return_value='1234'):
            e.execute()
            self.assertEqual(e.peek(3), 1234)

        # Interrupts on IMMEDIATE MODE
        e.load_string('103,5,103,5,99,-1')
        e.interrupts = True
        with self.assertRaises(InputInterrupt):
            e.step()

        # Should be back at pc = 0
        self.assertEqual(e.pc, 0)

        # Load input
        e.input_buffer = 567

        # Loading again overflows
        with self.assertRaises(InputOverflow):
            e.input_buffer = 123

        # Execute the input instruction
        e.step()
        self.assertEqual(e.peek(5), 567)

        # Exec next input instruction
        with self.assertRaises(InputInterrupt):
            e.step()

        e.input_buffer = 987

        # Execute until end
        e.execute()
        self.assertEqual(e.peek(5), 987)

        ######################################################
        # Interrupts on RELATIVE MODE
        e.load_string('109,10,203,0,203,1,203,-1,99,102,100,101')
        e.interrupts = True

        # step past the relative base op code
        e.step()

        with self.assertRaises(InputInterrupt):
            e.step()

        # Should be back at pc = 2 (after relative base op code)
        self.assertEqual(e.pc, 2)

        # Load input
        e.input_buffer = 567

        # Loading again overflows
        with self.assertRaises(InputOverflow):
            e.input_buffer = 123

        # Execute the input instruction
        e.step()
        self.assertEqual(e.peek(10), 567)

        # Exec next input instruction
        with self.assertRaises(InputInterrupt):
            e.step()

        e.input_buffer = 987

        # Step to execute this input
        e.step()
        self.assertEqual(e.peek(11), 987)

        # Exec next input instruction
        with self.assertRaises(InputInterrupt):
            e.step()

        e.input_buffer = 456

        # Execute until end
        e.execute()
        self.assertEqual(e.peek(9), 456)

        ######################################################
        # Interrupts on POSITIONAL MODE
        e.load_string('3,7,3,8,3,9,99,1,3,5')
        e.interrupts = True

        with self.assertRaises(InputInterrupt):
            e.step()

        # Should be back at pc = 0
        self.assertEqual(e.pc, 0)

        # Load input
        e.input_buffer = 345

        # Loading again overflows
        with self.assertRaises(InputOverflow):
            e.input_buffer = 123

        # Execute the input instruction
        e.step()
        self.assertEqual(e.peek(7), 345)

        # Exec next input instruction
        with self.assertRaises(InputInterrupt):
            e.step()

        e.input_buffer = 765

        # Step to execute this input
        e.step()
        self.assertEqual(e.peek(8), 765)

        # Exec next input instruction
        with self.assertRaises(InputInterrupt):
            e.step()

        e.input_buffer = 555

        # Execute until end
        e.execute()
        self.assertEqual(e.peek(9), 555)
示例#15
0
    def test_op_mul(self):
        """
        Tests MUL op code
        [dst]:=[a]*[b]
        """
        e = ElfCPU()
        # Invalid address 123456789 for a
        e.load_string('2,123456789,0,0')
        with self.assertRaises(ProtectionFaultError):
            e.step()

        # Invalid address 123456789 for b
        e.load_string('2,0,123456789,0')
        with self.assertRaises(ProtectionFaultError):
            e.step()

        # Invalid address 123456789 for dst
        e.load_string('2,0,0,123456789')
        with self.assertRaises(ProtectionFaultError):
            e.step()

        # [dst]:=[a]*[b]
        e.load_string('2,0,0,0,99')
        e.step()
        self.assertEqual(e.peek(0), 4)

        # [dst]:=a*[b]
        e.load_string('102,44,5,6,99,2,6')
        e.execute()
        self.assertEqual(e.peek(6), 88)

        # [dst]:=[a]*b
        e.load_string('1002,5,50,6,99,2,6')
        e.execute()
        self.assertEqual(e.peek(6), 100)

        # [dst]:=a*b
        e.load_string('1102,5,5,6,99,2,6')
        e.execute()
        self.assertEqual(e.peek(6), 25)

        # [dst]:=r[a]*b
        e.load_string('109,10,1202,0,4,7,99,7,3,3,4')
        e.execute()
        self.assertEqual(e.peek(7), 16)

        # [dst]:=a*r[b]
        e.load_string('109,10,2102,7,0,7,99,7,3,3,2')
        e.execute()
        self.assertEqual(e.peek(7), 14)

        # [dst]:=r[a]*r[b]
        e.load_string('109,10,2202,0,1,7,99,7,3,3,2,6')
        e.execute()
        self.assertEqual(e.peek(7), 12)

        # dst:=a*b
        e.load_string('11102,6,6,0,99')
        e.execute()
        self.assertEqual(e.peek(0), 36)

        # r[dst]:=a*b
        e.load_string('109,7,21102,8,3,0,99,1')
        e.execute()
        self.assertEqual(e.peek(7), 24)
示例#16
0
class HullRobot(object):
    def __init__(self, debug: bool = False):
        # The brain
        self._cpu = ElfCPU()

        # Current position x,y
        self._pos_x = 0
        self._pos_y = 0
        self._dir = Direction.UP

        # State
        self._state = State.PAINTING

        # Debug?
        if not isinstance(debug, bool):
            raise TypeError(
                f"debug must be of type bool, got {debug.__class__}")
        self._debug = debug

        if self._debug:
            print("HullRobot powered on")

    def run(self) -> None:
        """
        Tells robot to begin
        """
        if self._debug:
            print("HullRobot run requested")
        while not self._cpu.is_halted:
            try:
                if self._debug:
                    print("HullRobot CPU executing")
                self._cpu.execute()
            except InputInterrupt:
                if self._debug:
                    print("HullRobot input interrupt")
                self._cpu.input_buffer = self._camera().value
            except OutputInterrupt:
                if self._debug:
                    print("HullRobot output interrupt")
                if self._state == State.PAINTING:
                    # Paint the hull
                    self._paint(Paint(self._cpu.output_buffer))
                    self._state = State.MOVING
                elif self._state == State.MOVING:
                    # Move
                    self._move(Move(self._cpu.output_buffer))
                    self._state = State.PAINTING
                # Clear the output buffer
                del self._cpu.output_buffer

    def load_program(self, code: str) -> None:
        """
        Loads a new program
        """
        if not isinstance(code, str):
            raise TypeError(f"code must be of type str, got {code.__class__}")

        if self._debug:
            print("HullRobot loaded program code")

        self._cpu.load_string(code)
        self._cpu.interrupts = True

    def _move(self, dest: Move) -> None:
        """
        Moves the robot left or right
        """
        if self._dir == Direction.UP:
            if dest == Move.LEFT:
                self._pos_x -= 1
                self._dir = Direction.LEFT
            elif dest == Move.RIGHT:
                self._pos_x += 1
                self._dir = Direction.RIGHT
        elif self._dir == Direction.DOWN:
            if dest == Move.LEFT:
                self._pos_x += 1
                self._dir = Direction.RIGHT
            elif dest == Move.RIGHT:
                self._pos_x -= 1
                self._dir = Direction.LEFT
        elif self._dir == Direction.LEFT:
            if dest == Move.LEFT:
                self._pos_y -= 1
                self._dir = Direction.DOWN
            elif dest == Move.RIGHT:
                self._pos_y += 1
                self._dir = Direction.UP
        elif self._dir == Direction.RIGHT:
            if dest == Move.LEFT:
                self._pos_y += 1
                self._dir = Direction.UP
            elif dest == Move.RIGHT:
                self._pos_y -= 1
                self._dir = Direction.DOWN

        if self._debug:
            print(
                f"HullRobot moved {'left' if dest == Move.LEFT else 'right'} to ({self._pos_x}, {self._pos_y})"
            )

    def _camera(self) -> Paint:
        """
        Access the robot camera to determine if over black or white panel by
        looking up the hull map.  Anything not explicitly painted by the robot
        is black.
        """
        cam = hull_map.get((self._pos_x, self._pos_y), Paint.BLACK)
        if self._debug:
            print(f"HullRobot camera at ({self._pos_x}, {self._pos_y}) sees "
                  f"{'black' if cam == Paint.BLACK else 'white'}")
        return cam

    def _paint(self, color: Paint) -> None:
        """
        Robot has painted its current location
        """
        try:
            x = hull_map[(self._pos_x, self._pos_y)]
        except KeyError:
            x = None
        if self._debug:
            print(
                f"HullRobot {'painting' if x is None else 're-painting'} "
                f"{'black' if color == Paint.BLACK else 'white'} at ({self._pos_x}, {self._pos_y})"
            )
        hull_map[(self._pos_x, self._pos_y)] = color