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.runState = CPU.RunState.RUNNING
|
||||||
cpu.pc = 0x80u
|
cpu.pc = 0x80u
|
||||||
val start = System.nanoTime()
|
val start = System.nanoTime()
|
||||||
// val ninsn = cpu.run(600000000)
|
val ninsn = cpu.run(600000000)
|
||||||
var ninsn = cpu.run(30000)
|
// var ninsn = cpu.run(30000)
|
||||||
// cpu.tracer = tracer
|
// cpu.tracer = tracer
|
||||||
// ninsn += cpu.run(100)
|
// ninsn += cpu.run(100)
|
||||||
val end = System.nanoTime()
|
val end = System.nanoTime()
|
||||||
|
@@ -9,12 +9,44 @@ import org.slf4j.Logger
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.lang.StringBuilder
|
import java.lang.StringBuilder
|
||||||
|
|
||||||
/// The main CPU
|
private enum class TrapReason(val vector: UShort, val load_mmr2: Boolean, val clear: Int) {
|
||||||
///
|
/** Floating point error */
|
||||||
/// By default, the memory map is arranged as follows:
|
FPE(0xA4u, true, 0),
|
||||||
/// Fxxx: Channel I/O
|
/** Power fail */
|
||||||
/// Exxx: ROM
|
PWR(0x14u, true, 0),
|
||||||
/// xxxx: rest
|
/** 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)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
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_DST_BIT: Int = 24
|
||||||
val PADDR_ARG_BIT: Int = 25
|
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()
|
val debugMode = System.getProperty("jdp11.itrace").toBoolean()
|
||||||
|
|
||||||
private val insnTable: Array<CPU.(Int) -> Unit> = Array(256) {{throw InvalidOpcodeException()}}
|
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 {
|
init {
|
||||||
insnTable[0x00] = { opcode -> when (opcode) {
|
insnTable[0x00] = { opcode -> when (opcode) {
|
||||||
0x0000 -> {
|
0x0000 -> {
|
||||||
if (cur_mode == 0)
|
if (cur_mode == 0) {
|
||||||
runState = RunState.HALTED
|
runState = RunState.HALTED
|
||||||
|
} else {
|
||||||
|
cpu_err = cpu_err or CPU_ERR_ILL_HALT
|
||||||
|
setTrap(TrapReason.PRV)
|
||||||
|
}
|
||||||
} // HALT
|
} // HALT
|
||||||
0x0001 -> runState = RunState.WAIT_FOR_INTERRUPT // WAIT
|
0x0001 -> runState = RunState.WAIT_FOR_INTERRUPT // WAIT
|
||||||
0x0002 -> { // RTI
|
0x0002 -> { // RTI
|
||||||
pc = stack_pop()
|
pc = stack_pop()
|
||||||
psw = stack_pop() // TODO: check privilege on mode; check psw[11]
|
psw = stack_pop() // TODO: check privilege on mode; check psw[11]
|
||||||
} // RTI
|
} // RTI
|
||||||
0x0003 -> trap(0x0Cu) // BPT
|
0x0003 -> setTrap(TrapReason.BPT) // BPT
|
||||||
0x0004 -> trap(0x10u) // IOT
|
0x0004 -> setTrap(TrapReason.IOT) // IOT
|
||||||
0x0005 -> {
|
0x0005 -> {
|
||||||
stack_limit = 0u
|
stack_limit = 0u
|
||||||
pirq = 0u
|
pirq = 0u
|
||||||
@@ -57,7 +101,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
// jmp
|
// jmp
|
||||||
val dst = opc_dst(opcode)
|
val dst = opc_dst(opcode)
|
||||||
if (is_paddr_reg(dst)) {
|
if (is_paddr_reg(dst)) {
|
||||||
trap(4u); // Illegal instruction
|
throw InvalidOpcodeException() // ABORT Illegal instruction
|
||||||
}
|
}
|
||||||
pc = dst.toUShort()
|
pc = dst.toUShort()
|
||||||
} // JMP
|
} // JMP
|
||||||
@@ -100,7 +144,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
val r = opcode shr 6 and 0x7
|
val r = opcode shr 6 and 0x7
|
||||||
val dst = opc_dst(opcode)
|
val dst = opc_dst(opcode)
|
||||||
if (is_paddr_reg(dst)) {
|
if (is_paddr_reg(dst)) {
|
||||||
trap(4u) // illegal opcode
|
abort(4u) // ABORT illegal opcode
|
||||||
}
|
}
|
||||||
stack_push(registers[r])
|
stack_push(registers[r])
|
||||||
registers[r] = pc
|
registers[r] = pc
|
||||||
@@ -230,7 +274,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
when (opcode shr 6 and 3) {
|
when (opcode shr 6 and 3) {
|
||||||
0 -> { // MARK
|
0 -> { // MARK
|
||||||
val count = opcode and 0x3F
|
val count = opcode and 0x3F
|
||||||
sp = check_sp((pc + (2 * count).toUInt()).toUShort())
|
sp = checkSP((pc + (2 * count).toUInt()).toUShort())
|
||||||
pc = registers[5]
|
pc = registers[5]
|
||||||
registers[5] = stack_pop()
|
registers[5] = stack_pop()
|
||||||
} // MARK
|
} // MARK
|
||||||
@@ -477,8 +521,8 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
insnTable[0x85] = { br_rel(V, it) } // BVS
|
insnTable[0x85] = { br_rel(V, it) } // BVS
|
||||||
insnTable[0x86] = { br_rel(!C, it) } // BCC/BHIS
|
insnTable[0x86] = { br_rel(!C, it) } // BCC/BHIS
|
||||||
insnTable[0x87] = { br_rel(C, it) } // BCS/BLO
|
insnTable[0x87] = { br_rel(C, it) } // BCS/BLO
|
||||||
insnTable[0x88] = { trap(0x18u) } // EMT
|
insnTable[0x88] = { setTrap(TrapReason.EMT) } // EMT
|
||||||
insnTable[0x89] = { trap(0x1Cu) } // TRAP
|
insnTable[0x89] = { setTrap(TrapReason.TRP) } // TRAP
|
||||||
insnTable[0x8A] = { opcode ->
|
insnTable[0x8A] = { opcode ->
|
||||||
when (opcode shr 6 and 3) {
|
when (opcode shr 6 and 3) {
|
||||||
0 -> { // CLRB
|
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 general_registers = Array<UShortArray>(2) { UShortArray(6) }
|
||||||
private val shadow_r6 = UShortArray(4) //
|
private val shadow_r6 = UShortArray(4) //
|
||||||
private var stack_limit: UShort = 0u
|
private var stack_limit: UShort = 0u
|
||||||
|
private var trapReq: Int = 0
|
||||||
val core = PagingUnit(mbus)
|
val core = PagingUnit(mbus)
|
||||||
|
|
||||||
var runState: RunState = RunState.HALTED
|
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() {
|
fun step_int() {
|
||||||
try {
|
try {
|
||||||
tracer.noteBegin(this)
|
tracer.noteBegin(this)
|
||||||
@@ -967,14 +1014,27 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun step() {
|
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)) {
|
for (i in 7 downTo (psw_priority + 1)) {
|
||||||
if (pirq bit 8 + i) {
|
if (pirq bit 8 + i) {
|
||||||
logger.debug("PIRQ{} trap to 0xA0", i)
|
logger.debug("PIRQ{} trap to 0xA0", i)
|
||||||
trap(0xA0u)
|
callVector(0xA0u)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
val source = mbus.unibus.checkInterrupt(i)
|
val source = mbus.unibus.checkInterrupt(i)
|
||||||
@@ -983,7 +1043,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
runState = RunState.RUNNING
|
runState = RunState.RUNNING
|
||||||
val vector = source.vector
|
val vector = source.vector
|
||||||
logger.debug("Unibus interrupt at pri {} to {}", i, vector)
|
logger.debug("Unibus interrupt at pri {} to {}", i, vector)
|
||||||
trap(vector)
|
callVector(vector)
|
||||||
source.handled()
|
source.handled()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -992,26 +1052,49 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
val pirqLvl = pirq.toInt() shr 1 and 7;
|
val pirqLvl = pirq.toInt() shr 1 and 7;
|
||||||
if (pirqLvl > psw_priority) {
|
if (pirqLvl > psw_priority) {
|
||||||
logger.debug("PIRQ{} trap to 0xA0", pirqLvl)
|
logger.debug("PIRQ{} trap to 0xA0", pirqLvl)
|
||||||
trap(0xA0u)
|
callVector(0xA0u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (runState == RunState.WAIT_FOR_INTERRUPT) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// System.err.println("Executing insn at ${pc.toString(8)}")
|
|
||||||
|
// Proceed to handling instruction
|
||||||
|
try {
|
||||||
step_int()
|
step_int()
|
||||||
// TODO: handle T bit
|
|
||||||
} catch (error: MemoryError) {
|
} catch (error: MemoryError) {
|
||||||
// TODO: fill in memory manager fields
|
// TODO: fill in memory manager fields
|
||||||
cpu_err = when (error) {
|
when (error) {
|
||||||
is OddAddressError -> cpu_err or 0x40u
|
is OddAddressError -> {
|
||||||
is NonExistentMemoryError -> cpu_err or 0x20u
|
cpu_err = cpu_err or CPU_ERR_ODD_ADDR
|
||||||
is BusTimeoutError -> cpu_err or 0x10u
|
setTrap(TrapReason.ODD)
|
||||||
}
|
}
|
||||||
logger.warn("Threw error: ${error}")
|
is NonExistentMemoryError -> {
|
||||||
trap(0x04u)
|
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")
|
||||||
} catch (_: InvalidOpcodeException) {
|
} 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
|
return ninsn
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trap(vector: UShort) {
|
fun callVector(vector: UShort) {
|
||||||
// logger.info("Trap to {}", vector.toString(8))
|
|
||||||
val old_psw = psw
|
val old_psw = psw
|
||||||
// update PSW first so that this gets pushed to the
|
// update PSW first so that this gets pushed to the
|
||||||
psw = core.getw((vector + 2u).toUShort())
|
psw = core.getw((vector + 2u).toUShort())
|
||||||
@@ -1043,6 +1125,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
|||||||
pc = core.getw(vector)
|
pc = core.getw(vector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setTrap(reason: TrapReason) {
|
||||||
|
trapReq = trapReq or reason.mask
|
||||||
|
}
|
||||||
|
|
||||||
private inner class Registers: PAddressSpace {
|
private inner class Registers: PAddressSpace {
|
||||||
override fun getw(addr: UInt): UShort = when (addr) {
|
override fun getw(addr: UInt): UShort = when (addr) {
|
||||||
0x3FF78u -> 0x70u // Console switch/display
|
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 (cur_mode != 0) return addr
|
||||||
if (sp < stack_limit + 224u) {
|
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
|
// we can always assume that this is running in kernel mode, as no stack check occurs in exec or user mode
|
||||||
sp = 4u
|
sp = 4u
|
||||||
|
cpu_err = cpu_err or CPU_ERR_STK_RED
|
||||||
throw Trap(0x04)
|
throw Trap(0x04)
|
||||||
// stack limit red
|
// stack limit red
|
||||||
} else if (sp < stack_limit + 256u) {
|
} else if (sp < stack_limit + 256u) {
|
||||||
// stack limit yellow
|
// stack limit yellow
|
||||||
|
cpu_err = cpu_err or CPU_ERR_STK_YLW
|
||||||
|
|
||||||
// setTRAP(TRAP_YELLOW)
|
// setTRAP(TRAP_YELLOW)
|
||||||
// setCPUERR(CPUE_YELLOW)
|
|
||||||
}
|
}
|
||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun abort(vector: UShort) {
|
||||||
|
throw CpuAbort(vector)
|
||||||
|
}
|
||||||
|
|
||||||
enum class RunState(val handleInterrupt: Boolean) {
|
enum class RunState(val handleInterrupt: Boolean) {
|
||||||
RUNNING(handleInterrupt = true),
|
RUNNING(handleInterrupt = true),
|
||||||
HALTED(handleInterrupt = false),
|
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