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 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(17405)
// cpu.tracer = tracer // cpu.tracer = tracer
// ninsn += cpu.run(100) // ninsn += cpu.run(30)
val end = System.nanoTime() val end = System.nanoTime()
System.err.println("Halted at 0${cpu.pc.toString(8)}") 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 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 handlingStackRed: Boolean = false
private var trapReq: Int = 0 private var trapReq: Int = 0
val core = PagingUnit(mbus) 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_reg(addr: UInt): Boolean = addr bit PADDR_REG_BIT
private fun is_paddr_ispace(addr: UInt): Boolean = addr bit PADDR_ISPACE_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 { 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 reg = operand and 0x7
val is_pc = reg == 7 val is_pc = reg == 7
val increment = if (byte_mode && reg < 6) 1U else 2U val increment = if (byte_mode && reg < 6) 1 else 2
val decoded = 0x80_FFFFu and when (mode and 0x7) { val decoded = 0x80_FFFFu and when (mode) {
0 -> reg.toUInt() bis PADDR_REG_BIT 0 -> reg.toUInt() bis PADDR_REG_BIT
1 -> registers[reg].toUInt() 1 -> registers[reg].toUInt()
2 -> { 2 -> {
val addr = registers[reg] val addr = registers[reg]
if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false)) 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) addr.toUInt().bit(PADDR_ISPACE_BIT, is_pc)
} }
3 -> { 3 -> {
val addr = registers[reg] val addr = registers[reg]
if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false)) 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() core.getw(addr, dspace = !is_pc).toUInt()
} }
4 -> { 4 -> {
registers[reg] = (registers[reg] - increment).toUShort() adjust_reg(reg, -increment)
return registers[reg].toUInt().bit(PADDR_ISPACE_BIT, is_pc) // if (reg == 6) checkSP(sp)
registers[reg].toUInt().bit(PADDR_ISPACE_BIT, is_pc)
} }
5 -> { 5 -> {
registers[reg] = (registers[reg] - 2U).toUShort() adjust_reg(reg, -2)
// if (reg == 6) checkSP(sp)
core.getw(registers[reg], dspace = !is_pc).toUInt() core.getw(registers[reg], dspace = !is_pc).toUInt()
} }
6 -> { 6 -> {
@@ -924,6 +933,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
} }
else -> throw InvalidOpcodeException() // unreachable else -> throw InvalidOpcodeException() // unreachable
} }
if (mode >= 1 && reg == 6) {
checkSP(sp)
}
return decoded.bit(PADDR_DST_BIT, !src) bis PADDR_ARG_BIT 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_pop(): UShort = core.getw(sp).also { sp = (sp + 2u).toUShort() }
private fun stack_push(value: UShort) { private fun stack_push(value: UShort) {
sp = (sp-2u).toUShort() sp = checkSP((sp-2u).toUShort())
core.setw(sp, value) 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() { fun step() {
if (runState == RunState.HALTED) return if (runState == RunState.HALTED) return
@@ -1021,9 +1045,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
if (trapReq != 0) { if (trapReq != 0) {
for (cause in TrapReason.entries.reversed()) { for (cause in TrapReason.entries.reversed()) {
if (trapReq and cause.mask != 0) { if (trapReq and cause.mask != 0) {
// logger.warn("Trapping because $cause")
callVector(cause.vector)
trapReq = trapReq and cause.clear.inv() and cause.mask.inv() trapReq = trapReq and cause.clear.inv() and cause.mask.inv()
// TODO: load MMR2 if necessary // TODO: load MMR2 if necessary
callVector(cause.vector)
break break
} }
} }
@@ -1033,7 +1058,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
else if (mbus.unibus.interruptPending) { 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)
callVector(0xA0u) callVector(0xA0u)
break break
} }
@@ -1042,7 +1067,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// we might have been waiting for an interrupt // we might have been waiting for an interrupt
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)
callVector(vector) callVector(vector)
source.handled() source.handled()
break break
@@ -1051,7 +1076,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
} else { } else {
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)
callVector(0xA0u) callVector(0xA0u)
} }
} }
@@ -1061,10 +1086,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
return return
} catch (_: MemoryError) { } catch (_: MemoryError) {
// Failed to transfer to vector, treat as STK_RED // Failed to transfer to vector, treat as STK_RED
cur_mode = 0 trapRed()
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) {
@@ -1090,7 +1112,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
setTrap(TrapReason.NXM) setTrap(TrapReason.NXM)
} }
} }
logger.warn("Threw error: $error") // logger.warn("Threw error: $error")
} catch (_: InvalidOpcodeException) { } catch (_: InvalidOpcodeException) {
setTrap(TrapReason.ILL) setTrap(TrapReason.ILL)
} catch (_: EndCycle) { } catch (_: EndCycle) {
@@ -1102,7 +1124,6 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
var ninsn: Long = 0 var ninsn: Long = 0
while (runState == RunState.RUNNING) { while (runState == RunState.RUNNING) {
ninsn += run(Long.MAX_VALUE) ninsn += run(Long.MAX_VALUE)
step()
} }
return ninsn return ninsn
} }
@@ -1111,6 +1132,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
var ninsn: Long = 0 var ninsn: Long = 0
while (runState == RunState.RUNNING && ninsn < nstep) { while (runState == RunState.RUNNING && ninsn < nstep) {
ninsn++ ninsn++
if (pc == 0x1F76u.toUShort()) {
pc = pc
}
step() step()
} }
return ninsn return ninsn
@@ -1162,17 +1186,23 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
fun checkSP(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 + 256u) {
// we can always assume that this is running in kernel mode, as no stack check occurs in exec or user mode return addr
sp = 4u } else if (sp >= stack_limit + 224u) {
cpu_err = cpu_err or CPU_ERR_STK_RED // if (cpu_err and CPU_ERR_STK_YLW == 0.toUShort()) {
throw Trap(0x04) logger.warn("Stack YLW")
// stack limit red
} else if (sp < stack_limit + 256u) {
// stack limit yellow // stack limit yellow
cpu_err = cpu_err or CPU_ERR_STK_YLW cpu_err = cpu_err or CPU_ERR_STK_YLW
setTrap(TrapReason.YEL)
// setTRAP(TRAP_YELLOW) // }
} 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
} }
return addr return addr
} }

View File

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