New trap handling mechanism to support multiple trap causes in a single cycle
This commit is contained in:
@@ -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()
|
||||
|
@@ -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),
|
||||
|
7
src/main/kotlin/com/thequux/mcpdp/core/aborts.kt
Normal file
7
src/main/kotlin/com/thequux/mcpdp/core/aborts.kt
Normal 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()
|
Reference in New Issue
Block a user