New trap handling mechanism to support multiple trap causes in a single cycle

This commit is contained in:
2023-09-28 17:27:40 +02:00
parent 311fab9801
commit c5683a8383
3 changed files with 138 additions and 40 deletions

View File

@@ -37,8 +37,8 @@ fun main(args: Array<String>) {
cpu.runState = CPU.RunState.RUNNING
cpu.pc = 0x80u
val start = System.nanoTime()
// val ninsn = cpu.run(600000000)
var ninsn = cpu.run(30000)
val ninsn = cpu.run(600000000)
// var ninsn = cpu.run(30000)
// cpu.tracer = tracer
// ninsn += cpu.run(100)
val end = System.nanoTime()

View File

@@ -9,12 +9,44 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.lang.StringBuilder
/// The main CPU
///
/// By default, the memory map is arranged as follows:
/// Fxxx: Channel I/O
/// Exxx: ROM
/// xxxx: rest
private enum class TrapReason(val vector: UShort, val load_mmr2: Boolean, val clear: Int) {
/** Floating point error */
FPE(0xA4u, true, 0),
/** Power fail */
PWR(0x14u, true, 0),
/** Stack limit yellow */
YEL(0x04u, true, 0),
/** T bit set */
TRC(0x0Cu, true, 0),
/** TRAP insn */
TRP(0x1Cu, false, TRC.mask),
/** EMT insn */
EMT(0x18u, false, TRC.mask),
/** IOT insn */
IOT(0x10u, false, TRC.mask),
/** Breakpoint */
BPT(0x0Cu, false, TRC.mask),
/** Illegal instruction */
ILL(0x08u, false, TRC.mask),
/** Privileged insn */
PRV(0x04u, false, TRC.mask),
/** Parity error */
PAR(0x4Cu, false, TRC.mask or YEL.mask),
/** Memory management exception */
MME(0xA8u, true, TRC.mask or YEL.mask or PAR.mask),
/** Non-existent memory (or bus timeout) */
NXM(0x04u, true, TRC.mask or YEL.mask or PAR.mask or MME.mask),
/** Odd address error */
ODD(0x04u, true, TRC.mask or YEL.mask or PAR.mask or MME.mask or NXM.mask),
/** Stack limit red */
RED(0x04u, true, TRC.mask or YEL.mask or PAR.mask or MME.mask or NXM.mask or ODD.mask);
val mask: Int = 1 shl ordinal
}
/** The main CPU
*/
@OptIn(ExperimentalUnsignedTypes::class)
class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
@@ -24,6 +56,14 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val PADDR_DST_BIT: Int = 24
val PADDR_ARG_BIT: Int = 25
// CPU errors
val CPU_ERR_STK_RED: UShort = 0x04u
val CPU_ERR_STK_YLW: UShort = 0x08u
val CPU_ERR_UNIBUS_TIMEOUT: UShort = 0x10u
val CPU_ERR_NXM: UShort = 0x20u
val CPU_ERR_ODD_ADDR: UShort = 0x40u
val CPU_ERR_ILL_HALT: UShort = 0x80u
val debugMode = System.getProperty("jdp11.itrace").toBoolean()
private val insnTable: Array<CPU.(Int) -> Unit> = Array(256) {{throw InvalidOpcodeException()}}
@@ -32,16 +72,20 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
init {
insnTable[0x00] = { opcode -> when (opcode) {
0x0000 -> {
if (cur_mode == 0)
if (cur_mode == 0) {
runState = RunState.HALTED
} else {
cpu_err = cpu_err or CPU_ERR_ILL_HALT
setTrap(TrapReason.PRV)
}
} // HALT
0x0001 -> runState = RunState.WAIT_FOR_INTERRUPT // WAIT
0x0002 -> { // RTI
pc = stack_pop()
psw = stack_pop() // TODO: check privilege on mode; check psw[11]
} // RTI
0x0003 -> trap(0x0Cu) // BPT
0x0004 -> trap(0x10u) // IOT
0x0003 -> setTrap(TrapReason.BPT) // BPT
0x0004 -> setTrap(TrapReason.IOT) // IOT
0x0005 -> {
stack_limit = 0u
pirq = 0u
@@ -57,7 +101,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// jmp
val dst = opc_dst(opcode)
if (is_paddr_reg(dst)) {
trap(4u); // Illegal instruction
throw InvalidOpcodeException() // ABORT Illegal instruction
}
pc = dst.toUShort()
} // JMP
@@ -100,7 +144,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val r = opcode shr 6 and 0x7
val dst = opc_dst(opcode)
if (is_paddr_reg(dst)) {
trap(4u) // illegal opcode
abort(4u) // ABORT illegal opcode
}
stack_push(registers[r])
registers[r] = pc
@@ -230,7 +274,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
when (opcode shr 6 and 3) {
0 -> { // MARK
val count = opcode and 0x3F
sp = check_sp((pc + (2 * count).toUInt()).toUShort())
sp = checkSP((pc + (2 * count).toUInt()).toUShort())
pc = registers[5]
registers[5] = stack_pop()
} // MARK
@@ -477,8 +521,8 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
insnTable[0x85] = { br_rel(V, it) } // BVS
insnTable[0x86] = { br_rel(!C, it) } // BCC/BHIS
insnTable[0x87] = { br_rel(C, it) } // BCS/BLO
insnTable[0x88] = { trap(0x18u) } // EMT
insnTable[0x89] = { trap(0x1Cu) } // TRAP
insnTable[0x88] = { setTrap(TrapReason.EMT) } // EMT
insnTable[0x89] = { setTrap(TrapReason.TRP) } // TRAP
insnTable[0x8A] = { opcode ->
when (opcode shr 6 and 3) {
0 -> { // CLRB
@@ -718,6 +762,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
private val general_registers = Array<UShortArray>(2) { UShortArray(6) }
private val shadow_r6 = UShortArray(4) //
private var stack_limit: UShort = 0u
private var trapReq: Int = 0
val core = PagingUnit(mbus)
var runState: RunState = RunState.HALTED
@@ -942,7 +987,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
}
}
/// Internal step; evaluates one opcode, but does not handle errors, interrupts, or updating system registers
/**
* Internal step; evaluates one opcode, but does not handle errors, interrupts, or updating system registers
*/
fun step_int() {
try {
tracer.noteBegin(this)
@@ -967,14 +1014,27 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
}
fun step() {
try {
if (runState == RunState.HALTED) return
if (runState == RunState.HALTED) return
if (mbus.unibus.interruptPending) {
try {
// Check early traps
if (trapReq != 0) {
for (cause in TrapReason.entries.reversed()) {
if (trapReq and cause.mask != 0) {
trapReq = trapReq and cause.clear.inv() and cause.mask.inv()
// TODO: load MMR2 if necessary
callVector(cause.vector)
break
}
}
}
// Check unibus/PIRQ
else if (mbus.unibus.interruptPending) {
for (i in 7 downTo (psw_priority + 1)) {
if (pirq bit 8 + i) {
logger.debug("PIRQ{} trap to 0xA0", i)
trap(0xA0u)
callVector(0xA0u)
break
}
val source = mbus.unibus.checkInterrupt(i)
@@ -983,7 +1043,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
runState = RunState.RUNNING
val vector = source.vector
logger.debug("Unibus interrupt at pri {} to {}", i, vector)
trap(vector)
callVector(vector)
source.handled()
break
}
@@ -992,26 +1052,49 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val pirqLvl = pirq.toInt() shr 1 and 7;
if (pirqLvl > psw_priority) {
logger.debug("PIRQ{} trap to 0xA0", pirqLvl)
trap(0xA0u)
callVector(0xA0u)
}
}
if (runState == RunState.WAIT_FOR_INTERRUPT) {
return
}
// System.err.println("Executing insn at ${pc.toString(8)}")
step_int()
// TODO: handle T bit
} catch (_: EndCycle) {
return
} catch (_: MemoryError) {
// Failed to transfer to vector, treat as STK_RED
cur_mode = 0
sp = 4u
cpu_err = cpu_err or CPU_ERR_STK_RED
setTrap(TrapReason.RED)
}
if (runState == RunState.WAIT_FOR_INTERRUPT) {
return
}
// Proceed to handling instruction
try {
step_int()
} catch (error: MemoryError) {
// TODO: fill in memory manager fields
cpu_err = when (error) {
is OddAddressError -> cpu_err or 0x40u
is NonExistentMemoryError -> cpu_err or 0x20u
is BusTimeoutError -> cpu_err or 0x10u
when (error) {
is OddAddressError -> {
cpu_err = cpu_err or CPU_ERR_ODD_ADDR
setTrap(TrapReason.ODD)
}
is NonExistentMemoryError -> {
cpu_err = cpu_err or CPU_ERR_NXM
setTrap(TrapReason.NXM)
}
is BusTimeoutError -> {
cpu_err = cpu_err or CPU_ERR_UNIBUS_TIMEOUT
setTrap(TrapReason.NXM)
}
}
logger.warn("Threw error: ${error}")
trap(0x04u)
logger.warn("Threw error: $error")
} catch (_: InvalidOpcodeException) {
trap(0x10u)
setTrap(TrapReason.ILL)
} catch (_: EndCycle) {
// all end-of-cycle handling already performed
}
}
@@ -1033,8 +1116,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
return ninsn
}
fun trap(vector: UShort) {
// logger.info("Trap to {}", vector.toString(8))
fun callVector(vector: UShort) {
val old_psw = psw
// update PSW first so that this gets pushed to the
psw = core.getw((vector + 2u).toUShort())
@@ -1043,6 +1125,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
pc = core.getw(vector)
}
private fun setTrap(reason: TrapReason) {
trapReq = trapReq or reason.mask
}
private inner class Registers: PAddressSpace {
override fun getw(addr: UInt): UShort = when (addr) {
0x3FF78u -> 0x70u // Console switch/display
@@ -1074,22 +1160,27 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
}
}
fun check_sp(addr: UShort): UShort {
fun checkSP(addr: UShort): UShort {
if (cur_mode != 0) return addr
if (sp < stack_limit + 224u) {
// setCPUERR(CPUE_RED)
// we can always assume that this is running in kernel mode, as no stack check occurs in exec or user mode
sp = 4u
cpu_err = cpu_err or CPU_ERR_STK_RED
throw Trap(0x04)
// stack limit red
} else if (sp < stack_limit + 256u) {
// stack limit yellow
cpu_err = cpu_err or CPU_ERR_STK_YLW
// setTRAP(TRAP_YELLOW)
// setCPUERR(CPUE_YELLOW)
}
return addr
}
fun abort(vector: UShort) {
throw CpuAbort(vector)
}
enum class RunState(val handleInterrupt: Boolean) {
RUNNING(handleInterrupt = true),
HALTED(handleInterrupt = false),

View File

@@ -0,0 +1,7 @@
package com.thequux.mcpdp.core
open class CpuAbort(val vector: UShort): Exception()
class CpuError(val errcode: UShort): CpuAbort(0x04u)
// Immediate end of insn cycle
class EndCycle: Exception()