Mostly through stack yellow testing

This commit is contained in:
2023-09-28 20:03:55 +02:00
parent c5683a8383
commit 473a12798e
3 changed files with 102 additions and 64 deletions

View File

@@ -38,9 +38,9 @@ fun main(args: Array<String>) {
cpu.pc = 0x80u
val start = System.nanoTime()
val ninsn = cpu.run(600000000)
// var ninsn = cpu.run(30000)
// var ninsn = cpu.run(17405)
// cpu.tracer = tracer
// ninsn += cpu.run(100)
// ninsn += cpu.run(30)
val end = System.nanoTime()
System.err.println("Halted at 0${cpu.pc.toString(8)}")

View File

@@ -762,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 handlingStackRed: Boolean = false
private var trapReq: Int = 0
val core = PagingUnit(mbus)
@@ -882,32 +883,40 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
private fun is_paddr_reg(addr: UInt): Boolean = addr bit PADDR_REG_BIT
private fun is_paddr_ispace(addr: UInt): Boolean = addr bit PADDR_ISPACE_BIT
private inline fun adjust_reg(reg: Int, amount: Int) {
val new_val = (registers[reg].toInt() + amount).toUShort()
registers[reg] = new_val
core.logIncrement(reg, amount)
}
private fun op_resolve(operand: Int, src: Boolean, byte_mode: Boolean = false): UInt {
val mode = operand shr 3
val mode = operand shr 3 and 0x7
val reg = operand and 0x7
val is_pc = reg == 7
val increment = if (byte_mode && reg < 6) 1U else 2U
val decoded = 0x80_FFFFu and when (mode and 0x7) {
val increment = if (byte_mode && reg < 6) 1 else 2
val decoded = 0x80_FFFFu and when (mode) {
0 -> reg.toUInt() bis PADDR_REG_BIT
1 -> registers[reg].toUInt()
2 -> {
val addr = registers[reg]
if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false))
registers[reg] = (addr + increment).toUShort()
adjust_reg(reg, increment)
addr.toUInt().bit(PADDR_ISPACE_BIT, is_pc)
}
3 -> {
val addr = registers[reg]
if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false))
registers[reg] = (addr + 2U).toUShort()
adjust_reg(reg, 2)
core.getw(addr, dspace = !is_pc).toUInt()
}
4 -> {
registers[reg] = (registers[reg] - increment).toUShort()
return registers[reg].toUInt().bit(PADDR_ISPACE_BIT, is_pc)
adjust_reg(reg, -increment)
// if (reg == 6) checkSP(sp)
registers[reg].toUInt().bit(PADDR_ISPACE_BIT, is_pc)
}
5 -> {
registers[reg] = (registers[reg] - 2U).toUShort()
adjust_reg(reg, -2)
// if (reg == 6) checkSP(sp)
core.getw(registers[reg], dspace = !is_pc).toUInt()
}
6 -> {
@@ -924,6 +933,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
}
else -> throw InvalidOpcodeException() // unreachable
}
if (mode >= 1 && reg == 6) {
checkSP(sp)
}
return decoded.bit(PADDR_DST_BIT, !src) bis PADDR_ARG_BIT
}
@@ -977,7 +989,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
private fun stack_pop(): UShort = core.getw(sp).also { sp = (sp + 2u).toUShort() }
private fun stack_push(value: UShort) {
sp = (sp-2u).toUShort()
sp = checkSP((sp-2u).toUShort())
core.setw(sp, value)
}
@@ -1013,6 +1025,18 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
}
}
fun trapRed() {
// This is handled separately because otherwise the stack push
// would itself trigger a red trap
logger.warn("Stack RED")
cpu_err = cpu_err or CPU_ERR_STK_RED
val old_psw = psw
psw = core.getw(6u)
core.setw(2u, psw)
core.setw(0u, pc)
pc = core.getw(4u)
}
fun step() {
if (runState == RunState.HALTED) return
@@ -1021,9 +1045,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
if (trapReq != 0) {
for (cause in TrapReason.entries.reversed()) {
if (trapReq and cause.mask != 0) {
// logger.warn("Trapping because $cause")
callVector(cause.vector)
trapReq = trapReq and cause.clear.inv() and cause.mask.inv()
// TODO: load MMR2 if necessary
callVector(cause.vector)
break
}
}
@@ -1033,7 +1058,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
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)
// logger.debug("PIRQ{} trap to 0xA0", i)
callVector(0xA0u)
break
}
@@ -1042,7 +1067,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// we might have been waiting for an interrupt
runState = RunState.RUNNING
val vector = source.vector
logger.debug("Unibus interrupt at pri {} to {}", i, vector)
// logger.debug("Unibus interrupt at pri {} to {}", i, vector)
callVector(vector)
source.handled()
break
@@ -1051,7 +1076,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
} else {
val pirqLvl = pirq.toInt() shr 1 and 7;
if (pirqLvl > psw_priority) {
logger.debug("PIRQ{} trap to 0xA0", pirqLvl)
// logger.debug("PIRQ{} trap to 0xA0", pirqLvl)
callVector(0xA0u)
}
}
@@ -1061,10 +1086,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
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)
trapRed()
}
if (runState == RunState.WAIT_FOR_INTERRUPT) {
@@ -1090,7 +1112,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
setTrap(TrapReason.NXM)
}
}
logger.warn("Threw error: $error")
// logger.warn("Threw error: $error")
} catch (_: InvalidOpcodeException) {
setTrap(TrapReason.ILL)
} catch (_: EndCycle) {
@@ -1102,7 +1124,6 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
var ninsn: Long = 0
while (runState == RunState.RUNNING) {
ninsn += run(Long.MAX_VALUE)
step()
}
return ninsn
}
@@ -1111,6 +1132,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
var ninsn: Long = 0
while (runState == RunState.RUNNING && ninsn < nstep) {
ninsn++
if (pc == 0x1F76u.toUShort()) {
pc = pc
}
step()
}
return ninsn
@@ -1162,17 +1186,23 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
fun checkSP(addr: UShort): UShort {
if (cur_mode != 0) return addr
if (sp < stack_limit + 224u) {
// 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)
if (sp >= stack_limit + 256u) {
return addr
} else if (sp >= stack_limit + 224u) {
// if (cpu_err and CPU_ERR_STK_YLW == 0.toUShort()) {
logger.warn("Stack YLW")
// stack limit yellow
cpu_err = cpu_err or CPU_ERR_STK_YLW
setTrap(TrapReason.YEL)
// }
} else {
// if (cpu_err and CPU_ERR_STK_RED == 0.toUShort()) {
// Don't trigger if already triggered
logger.warn("Stack RED")
// we can always assume that this is running in kernel mode, as no stack check occurs in exec or user mode
trapRed()
throw EndCycle()
// stack limit red
} else if (sp < stack_limit + 256u) {
// stack limit yellow
cpu_err = cpu_err or CPU_ERR_STK_YLW
// setTRAP(TRAP_YELLOW)
}
return addr
}

View File

@@ -1,10 +1,7 @@
package com.thequux.mcpdp.core
import com.thequux.mcpdp.ext.bit.bic
import com.thequux.mcpdp.ext.bit.bit
import com.thequux.mcpdp.ext.bit.shr
import com.thequux.mcpdp.ext.bit.toOctal
import com.thequux.mcpdp.ext.bit.*
import com.thequux.mcpdp.peripheral.Unibus
import com.thequux.mcpdp.util.ProgrammerError
import kotlin.math.max
@@ -14,29 +11,19 @@ enum class AccessAction {
Trap,
Abort,
}
enum class ACF(val bin: UShort, val read: AccessAction, val write: AccessAction) {
NonResident(0U, AccessAction.Abort, AccessAction.Abort),
ReadTrap(1U, AccessAction.Trap, AccessAction.Abort),
ReadOnly(2U, AccessAction.Abort, AccessAction.Allow),
Unused1(3U, AccessAction.Abort, AccessAction.Abort),
TrapAlways(4U, AccessAction.Trap, AccessAction.Trap),
WriteTrap(5U, AccessAction.Allow, AccessAction.Trap),
ReadWrite(6U, AccessAction.Allow, AccessAction.Allow),
Unused2(7U, AccessAction.Abort, AccessAction.Abort);
enum class ACF(val read: AccessAction, val write: AccessAction) {
NonResident(AccessAction.Abort, AccessAction.Abort),
ReadTrap(AccessAction.Trap, AccessAction.Abort),
ReadOnly(AccessAction.Abort, AccessAction.Allow),
Unused1(AccessAction.Abort, AccessAction.Abort),
TrapAlways(AccessAction.Trap, AccessAction.Trap),
WriteTrap(AccessAction.Allow, AccessAction.Trap),
ReadWrite(AccessAction.Allow, AccessAction.Allow),
Unused2(AccessAction.Abort, AccessAction.Abort);
fun action(writep: Boolean): AccessAction = if (writep) write else read
companion object {
fun fromField(value: UShort): ACF = when ((value and 0x7U).toInt()) {
0 -> NonResident
1 -> ReadTrap
2 -> ReadOnly
3 -> Unused1
4 -> TrapAlways
5 -> WriteTrap
6 -> ReadWrite
7 -> Unused2
else -> throw ProgrammerError("Unreachable")
}
fun fromField(value: UShort): ACF = ACF.entries[value.toInt() and 0x7]
}
}
private data class PDR(val plf: UShort, var A: Boolean, var W: Boolean, var ed: Boolean, var acf: ACF) {
@@ -70,7 +57,14 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
}
private var mmr = UShortArray(4)
private var mmr0: UShort = 0u
set(value) {
mmrLocked = value and 0xE000u != 0u.toUShort()
field = value
}
private var mmr1: UShort = 0u
private var mmr2: UShort = 0u
private var mmrLocked: Boolean = false
private var enableUnibusMap: Boolean = false
private var enable22bit: Boolean = false
var memErrorReg: UShort = 0u
@@ -85,15 +79,16 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
fun setMmr0(causeBit: Int, apf: Int, completed: Boolean = false) {
val reg = mmr[0].toInt()
val reg = mmr0.toInt()
val cause_flag = 1 shl causeBit
if (reg and 0xF000 and -cause_flag == 0) {
// no higher priority flags
mmr[0] = (reg and 0xF200
mmr0 = (reg and 0xF200
or 0x4000 // trap: page length
or mode.shl(5)
or (if (completed) 0x80 else 0)
or (if (dspace) 8 else 0)
or cause_flag
or apf).toUShort()
}
}
@@ -137,7 +132,7 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
fun updateMode() {
mapMode = when {
!(mmr[0] bit 0) -> MManMode.MM_16
!(mmr0 bit 0) -> MManMode.MM_16
enable22bit -> MManMode.MM_22
else -> MManMode.MM_18
}
@@ -146,6 +141,19 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
fun getSpace(mode: Int): VAddressSpace = if (mapMode.mmanEnable) modeVTabs[max(mode, 2)] else noMmanSpace
fun logIncrement(register: Int, amount: Int) {
assert(register in 0..7)
assert(amount in -16..15)
if (!mmrLocked)
mmr1 = mmr1 shl 8 or register.toUShort() or (amount.toUShort() and 0x1Fu shl 3)
}
fun newInsn(addr: UShort) {
if (!mmrLocked) {
mmr1 = 0u
mmr2 = addr
}
}
private val itabs: Array<PageTable> = Array(3) { PageTable(if (it == 2) 3 else it, dspace = false) }
private val dtabs: Array<PageTable> = Array(3) { PageTable(if (it == 2) 3 else it, dspace = true) }
private val modeVTabs: Array<ModeVSpace> = Array(3) { ModeVSpace(itabs[it], dtabs[it], false) }
@@ -206,9 +214,9 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
override fun getw(addr: UInt): UShort {
return when (addr) {
MMR0 -> mmr[0]
MMR1 -> mmr[1]
MMR2 -> mmr[2]
MMR0 -> mmr0
MMR1 -> mmr0
MMR2 -> mmr0
MMR3 -> {
(0U.bit(0, modeVTabs[2].useDSpace)
.bit(1, modeVTabs[1].useDSpace)
@@ -267,13 +275,13 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
}
}
fun reset() {
enable22bit = false
enableUnibusMap = false
modeVTabs[0].useDSpace = false
modeVTabs[1].useDSpace = false
modeVTabs[2].useDSpace = false
mmr[3] = 0u
updateMode()
}
@@ -287,7 +295,7 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
return if (uaddr > 0x3_0000u) {
// I/O page is always mapped as follows
addr or 0x3FC000u
} else if (mmr[3] bit 5) {
} else if (enableUnibusMap) {
unibusTable[(uaddr shr 13).toInt()] + (uaddr and 0x1FFFu) or 0x3C0000U
} else {
addr