diff --git a/src/main/kotlin/razorvine/examplemachine/GUI.kt b/src/main/kotlin/razorvine/examplemachine/GUI.kt index a7a983d..da62d41 100644 --- a/src/main/kotlin/razorvine/examplemachine/GUI.kt +++ b/src/main/kotlin/razorvine/examplemachine/GUI.kt @@ -204,8 +204,10 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener { val resetBt = JButton("Reset").also { it.actionCommand = "reset" } val cycleBt = JButton("Step").also { it.actionCommand = "step" } + val irqBt = JButton("IRQ").also { it.actionCommand = "irq" } + val nmiBt = JButton("NMI").also { it.actionCommand = "nmi" } val quitBt = JButton("Quit").also { it.actionCommand = "quit" } - listOf(resetBt, cycleBt, pauseBt, quitBt).forEach { + listOf(resetBt, cycleBt, irqBt, nmiBt, pauseBt, quitBt).forEach { it.addActionListener(this) buttonPanel.add(it) } @@ -235,6 +237,8 @@ class DebugWindow(val vm: VirtualMachine) : JFrame("debugger"), ActionListener { pauseBt.actionCommand = "pause" pauseBt.text = "Pause" } + "irq" -> vm.cpu.irq() + "nmi" -> vm.cpu.nmi() "quit" -> { dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING)) } diff --git a/src/main/kotlin/razorvine/ksim65/Cpu6502.kt b/src/main/kotlin/razorvine/ksim65/Cpu6502.kt index da7bb52..643f807 100644 --- a/src/main/kotlin/razorvine/ksim65/Cpu6502.kt +++ b/src/main/kotlin/razorvine/ksim65/Cpu6502.kt @@ -107,7 +107,11 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() { get() = currentInstruction.mnemonic // has an interrupt been requested? - protected var pendingInterrupt: Pair? = null + protected enum class Interrupt { + IRQ, + NMI + } + protected var pendingInterrupt: Interrupt? = null // data byte from the instruction (only set when addr.mode is Accumulator, Immediate or Implied) protected var fetchedData: Int = 0 @@ -334,13 +338,13 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() { while (instrCycles > 0) clock() } - fun nmi(source: BusComponent) { - pendingInterrupt = Pair(true, source) + fun nmi() { + pendingInterrupt = Interrupt.NMI } - fun irq(source: BusComponent) { + fun irq() { if (!regP.I) - pendingInterrupt = Pair(false, source) + pendingInterrupt = Interrupt.IRQ } fun logState(): String = @@ -1092,19 +1096,17 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() { protected open fun iBrk() { // handle BRK ('software interrupt') or a real hardware IRQ - val interrupt = pendingInterrupt - val nmi = interrupt?.first == true - if (interrupt != null) { + if (pendingInterrupt != null) { pushStackAddr(regPC - 1) } else { regPC++ pushStackAddr(regPC) } - regP.B = interrupt == null + regP.B = pendingInterrupt == null pushStack(regP) regP.I = true // interrupts are now disabled // NMOS 6502 doesn't clear the D flag (CMOS 65C02 version does...) - regPC = readWord(if (nmi) NMI_vector else IRQ_vector) + regPC = readWord(if (pendingInterrupt==Interrupt.NMI) NMI_vector else IRQ_vector) pendingInterrupt = null } diff --git a/src/main/kotlin/razorvine/ksim65/Cpu65C02.kt b/src/main/kotlin/razorvine/ksim65/Cpu65C02.kt index a6a18bb..69312dd 100644 --- a/src/main/kotlin/razorvine/ksim65/Cpu65C02.kt +++ b/src/main/kotlin/razorvine/ksim65/Cpu65C02.kt @@ -637,15 +637,14 @@ class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) { override fun iBrk() { // handle BRK ('software interrupt') or a real hardware IRQ - val interrupt = pendingInterrupt - val nmi = interrupt?.first == true - if (interrupt != null) { + val nmi = pendingInterrupt == Interrupt.NMI + if (pendingInterrupt != null) { pushStackAddr(regPC - 1) } else { regPC++ pushStackAddr(regPC) } - regP.B = interrupt == null + regP.B = pendingInterrupt == null pushStack(regP) regP.I = true // interrupts are now disabled regP.D = false // this is different from NMOS 6502 diff --git a/src/main/kotlin/razorvine/ksim65/components/Timer.kt b/src/main/kotlin/razorvine/ksim65/components/Timer.kt index 1ac329c..b5a0906 100644 --- a/src/main/kotlin/razorvine/ksim65/components/Timer.kt +++ b/src/main/kotlin/razorvine/ksim65/components/Timer.kt @@ -34,9 +34,9 @@ class Timer(startAddress: Address, endAddress: Address, val cpu: Cpu6502) : MemM counter++ if (counter == interval) { if (nmi) - cpu.nmi(this) + cpu.nmi() else - cpu.irq(this) + cpu.irq() counter = 0 } } diff --git a/src/main/resources/vmdemo.asm b/src/main/resources/vmdemo.asm index 85f8ebd..dd5fbbe 100644 --- a/src/main/resources/vmdemo.asm +++ b/src/main/resources/vmdemo.asm @@ -6,6 +6,7 @@ TIMER = $d200 MOUSE = $d300 KEYBOARD = $d400 + IRQVEC = $fffe SCREEN_WIDTH=640 * = $1000 @@ -13,8 +14,21 @@ start sei ldx #$ff - txs - cli + txs ; clear the stack + ; setup timer irq + lda #irq + sta IRQVEC+1 + lda #<1000 + sta TIMER+1 ; every 1000 clock cycles an irq + lda #>1000 + sta TIMER+2 + lda #0 + sta TIMER+3 + lda #1 + sta TIMER+0 + cli ; enable irqs ; ------- print stuff lda #10 @@ -127,3 +141,50 @@ done jmp done character .byte 0 + +; ---------- irq routine +; this one simply read the RTC and prints it on the bottom of the screen. +irq + pha + txa + pha + tya + pha + ; we don't check for BRK flag because we're lazy + lda DISPLAY+0 + pha + lda DISPLAY+1 + pha + ldy #29 + sty DISPLAY+1 + ldy #0 + sty DISPLAY+0 +- lda _time_msg,y + beq + + sta DISPLAY+2 + inc DISPLAY+0 + iny + bne - ++ ; read the clock now + ldx #0 +- lda RTC,x + clc + adc #32 + sta DISPLAY+2 + inc DISPLAY+0 + inx + cpx #9 + bne - + + pla + sta DISPLAY+1 + pla + sta DISPLAY+0 + pla + tay + pla + tax + pla + rti + +_time_msg .text "The time is: ",0 diff --git a/src/main/resources/vmdemo.prg b/src/main/resources/vmdemo.prg index 01ebd5a..70eb437 100644 Binary files a/src/main/resources/vmdemo.prg and b/src/main/resources/vmdemo.prg differ