From 473a12798e20d371f0253d750672c9d262278e15 Mon Sep 17 00:00:00 2001 From: TQ Hirsch Date: Thu, 28 Sep 2023 20:03:55 +0200 Subject: [PATCH] Mostly through stack yellow testing --- src/main/kotlin/Main.kt | 4 +- src/main/kotlin/com/thequux/mcpdp/core/CPU.kt | 88 +++++++++++++------ .../com/thequux/mcpdp/core/PagingUnit.kt | 74 +++++++++------- 3 files changed, 102 insertions(+), 64 deletions(-) diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index f887541..22c414e 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -38,9 +38,9 @@ fun main(args: Array) { 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)}") diff --git a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt index cbf654f..259e0af 100644 --- a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt +++ b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt @@ -762,6 +762,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) { private val general_registers = Array(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 } diff --git a/src/main/kotlin/com/thequux/mcpdp/core/PagingUnit.kt b/src/main/kotlin/com/thequux/mcpdp/core/PagingUnit.kt index 45fe6b6..e61ad79 100644 --- a/src/main/kotlin/com/thequux/mcpdp/core/PagingUnit.kt +++ b/src/main/kotlin/com/thequux/mcpdp/core/PagingUnit.kt @@ -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 = Array(3) { PageTable(if (it == 2) 3 else it, dspace = false) } private val dtabs: Array = Array(3) { PageTable(if (it == 2) 3 else it, dspace = true) } private val modeVTabs: Array = 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