Ejemplo n.º 1
0
class Chip8Widget(QWidget):

    def __init__(self, machine: Machine, sound: bool=False, instruction_per_second: int = 500):
        super().__init__()
        self._machine = machine
        self.init_ui()
        self._sound = QSound('beep.wav')
        self._sound_support = sound

        self._machine_update_timer = Timer(interval=1.0 / instruction_per_second)
        self._machine_update_timer.add_handler(self._execute_instruction)
        self._machine_update_timer.start()

        self._machine_sound_delay_timer = Timer(interval=1.0 / 60)  # 60 Hz
        self._machine_sound_delay_timer.add_handler(self._update_sound_delay)
        self._machine_sound_delay_timer.start()

        self._key_dict = {
            Qt.Key_1: 1, Qt.Key_2: 2, Qt.Key_3: 3, Qt.Key_4: 0xC,
            Qt.Key_Q: 4, Qt.Key_W: 5, Qt.Key_E: 6, Qt.Key_R: 0xD,
            Qt.Key_A: 7, Qt.Key_S: 8, Qt.Key_D: 9, Qt.Key_F: 0xE,
            Qt.Key_Z: 0xA, Qt.Key_X: 0x0, Qt.Key_C: 0xB, Qt.Key_V: 0xF
        }

    def init_ui(self):
        screen = Chip8ScreenWidget(self._machine.Screen, self)
        self.setWindowTitle('Chip 8')
        self.show()

    def keyPressEvent(self, event: QtGui.QKeyEvent):
        if event.key() in self._key_dict:
            self._machine.Keyboard.key_down(self._key_dict[event.key()])

    def keyReleaseEvent(self, event: QtGui.QKeyEvent):
        if event.key() in self._key_dict:
            self._machine.Keyboard.key_up(self._key_dict[event.key()])

    def _execute_instruction(self):
        self._machine.execute_next_instruction()

    def _update_sound_delay(self):
        if self._machine.SoundTimer.get_count() != 0 and self._sound_support:
            self._sound.play()
        self._machine.DelayTimer.decrease()
        self._machine.SoundTimer.decrease()
Ejemplo n.º 2
0
class Chip8ScreenWidget(QWidget):
    def __init__(self, screen: Screen, parent: QWidget = None):
        super().__init__(parent)
        self._screen = screen
        screen_size = QDesktopWidget().screenGeometry(-1)
        self._pixel_width = int(screen_size.width() * 1 / 100)
        self._pixel_height = self._pixel_width

        self.init_ui()

        self._draw_timer = Timer(interval=1.0 / 30)
        self._draw_timer.add_handler(self._draw_screen_event)
        self._draw_timer.start()

    def init_ui(self):
        self.setFixedSize(self._screen.width() * self._pixel_width,
                          self._screen.height() * self._pixel_height)
        self.show()

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        self._draw_screen(qp)
        qp.end()

    def keyPressEvent(self, a0: QtGui.QKeyEvent):
        self.parent().keyPressEvent(a0)

    def _draw_screen_event(self):
        self.update()

    def _draw_screen(self, qp):
        qp.setPen(QColor(0, 0, 0))
        for y in range(self._screen.height()):
            for x in range(self._screen.width()):
                self._draw_pixel(y, x, qp)

    def _draw_pixel(self, y, x, qp):
        qp.fillRect(
            x * self._pixel_width, y * self._pixel_height, self._pixel_width,
            self._pixel_height,
            QColor(0, 0, 0) if self._screen.get_pixel(y, x) == 0 else QColor(
                255, 255, 255))
Ejemplo n.º 3
0
class Chip8MachineStateWidget(QWidget):
    def __init__(self, machine: Machine, parent: QWidget=None):
        super().__init__(parent)
        self._machine = machine

        self._init_ui()

        self._draw_timer = Timer(interval=1.0 / 30)
        self._draw_timer.add_handler(self._draw_state_event)
        self._draw_timer.start()

        self._instruction_factory = InstructionFactory()

    def _init_ui(self):
        screen_size = QDesktopWidget().screenGeometry(-1)
        self._pixel_width = int(screen_size.width() * 1 / 100)
        self._pixel_height = self._pixel_width

        self.setFixedHeight(self._pixel_height * 32)
        self.setFixedWidth(self._pixel_width * 35)

        self._text_size = int(self._pixel_height * 0.7)

        self.show()

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)

        self._draw_state(qp)

        qp.end()

    def _draw_state_event(self):
        self.update()

    def keyPressEvent(self, a0: QtGui.QKeyEvent):
        self.parent().keyPressEvent(a0)

    def _draw_state(self, qp):
        qp.setFont(QFont('Noto Sans', self._text_size))

        t = self._text_size // 2

        qp.drawText(0, self._text_size, 'V: {}'.format(list(map(to_hex, self._machine.VRegisters))))
        qp.drawText(0, 2 * self._text_size + t, 'PC: {}, I: {}, DT: {}, ST: {}'
                    .format(to_hex(self._machine.PC),
                            to_hex(self._machine.AddressRegister),
                            to_hex(self._machine.DelayTimer.get_count()),
                            to_hex(self._machine.SoundTimer.get_count())))
        qp.drawText(0, 3 * self._text_size + 2 * t, 'Stack: {}'.format(list(map(to_hex, self._machine.Stack.items()))))
        qp.drawText(0, 4 * self._text_size + 3 * t, 'Memory at 10-radius of PC:')

        y = 5 * self._text_size + 4 * t

        for i in range(-10, 11):
            index = self._machine.PC + i * 2

            if index < 0 or index > self._machine.MemorySize:
                qp.drawText(0, y, '')
                continue

            opcode = self._get_opcode_at(index)

            try:
                instruction = self._instruction_factory.from_opcode(opcode)

                qp.drawText(0, y, '{}{} : {}, {} {} {}'
                            .format('-> ' if i == 0 else '    ',
                                    to_hex(index),
                                    list(map(to_hex, opcode)),
                                    instruction.__class__.__name__,
                                    list((map(to_hex, instruction.arg_registers))),
                                    to_hex(instruction.arg_constant) if instruction.arg_constant is not None else '-'))
            except InstructionFactoryError:
                qp.drawText(0, y, '{}{} : {}, [unknown]'
                            .format('-> ' if i == 0 else '    ',
                                    to_hex(index),
                                    list(map(to_hex, opcode))))

            y += self._text_size + t

        qp.drawText(0, y, 'Machine blocked' if self._machine.Block else 'Machine not blocked')

    def _get_opcode_at(self, i):
        return bytearray([self._machine.Memory[i], self._machine.Memory[i + 1]])