From 5cea8025cebea28285c28566f7f4fa5cdcc5c131 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 21 Sep 2019 23:16:55 +0200 Subject: [PATCH] Added irq() and nmi() to the MPU --- CHANGES.txt | 4 ++++ py65/devices/mpu6502.py | 22 ++++++++++++++++++++ py65/tests/devices/test_mpu6502.py | 32 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index b430d10..701bea8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,10 @@ Py65 now requires Python 3.4 or later. On Python 2, Py65 now requires Python 2.7. + - added ``irq()`` and ``nmi()`` methods to the ``MPU`` class, so that + interrupts can be simulated. Patch by Irmen de Jong. + + 1.1.0 (2018-07-01) ------------------ diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index 4166d70..b211570 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -74,6 +74,28 @@ class MPU: self.p = self.BREAK | self.UNUSED self.processorCycles = 0 + def irq(self): + # triggers a normal IRQ + # this is very similar to the BRK instruction + if self.p & self.INTERRUPT: + return + self.stPushWord(self.pc) + self.p &= ~self.BREAK + self.stPush(self.p | self.UNUSED) + self.p |= self.INTERRUPT + self.pc = self.WordAt(self.IRQ) + self.processorCycles += 7 + + def nmi(self): + # triggers a NMI IRQ in the processor + # this is very similar to the BRK instruction + self.stPushWord(self.pc) + self.p &= ~self.BREAK + self.stPush(self.p | self.UNUSED) + self.p |= self.INTERRUPT + self.pc = self.WordAt(self.NMI) + self.processorCycles += 7 + # Helpers for addressing modes def ByteAt(self, addr): diff --git a/py65/tests/devices/test_mpu6502.py b/py65/tests/devices/test_mpu6502.py index 8cfd6a3..fc03a24 100644 --- a/py65/tests/devices/test_mpu6502.py +++ b/py65/tests/devices/test_mpu6502.py @@ -1905,6 +1905,38 @@ class Common6502Tests: self.assertEqual(mpu.BREAK | mpu.UNUSED | mpu.INTERRUPT, mpu.p) + # IRQ and NMI handling (very similar to BRK) + + def test_irq_pushes_pc_and_correct_status_then_sets_pc_to_irq_vector(self): + mpu = self._make_mpu() + mpu.p = mpu.UNUSED + self._write(mpu.memory, 0xFFFA, (0x88, 0x77)) + self._write(mpu.memory, 0xFFFE, (0xCD, 0xAB)) + mpu.pc = 0xC123 + mpu.irq() + self.assertEqual(0xABCD, mpu.pc) + self.assertEqual(0xC1, mpu.memory[0x1FF]) # PCH + self.assertEqual(0x23, mpu.memory[0x1FE]) # PCL + self.assertEqual(mpu.UNUSED, mpu.memory[0x1FD]) # Status + self.assertEqual(0xFC, mpu.sp) + self.assertEqual(mpu.UNUSED | mpu.INTERRUPT, mpu.p) + self.assertEqual(7, mpu.processorCycles) + + def test_nmi_pushes_pc_and_correct_status_then_sets_pc_to_nmi_vector(self): + mpu = self._make_mpu() + mpu.p = mpu.UNUSED + self._write(mpu.memory, 0xFFFA, (0x88, 0x77)) + self._write(mpu.memory, 0xFFFE, (0xCD, 0xAB)) + mpu.pc = 0xC123 + mpu.nmi() + self.assertEqual(0x7788, mpu.pc) + self.assertEqual(0xC1, mpu.memory[0x1FF]) # PCH + self.assertEqual(0x23, mpu.memory[0x1FE]) # PCL + self.assertEqual(mpu.UNUSED, mpu.memory[0x1FD]) # Status + self.assertEqual(0xFC, mpu.sp) + self.assertEqual(mpu.UNUSED | mpu.INTERRUPT, mpu.p) + self.assertEqual(7, mpu.processorCycles) + # BVC def test_bvc_overflow_clear_branches_relative_forward(self):