Mostly through stack yellow testing
This commit is contained in:
@@ -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)}")
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user