Compare commits
3 Commits
fa7108098b
...
d90304b5fd
| Author | SHA1 | Date | |
|---|---|---|---|
| d90304b5fd | |||
| 6052c9a0c0 | |||
| c8c8297765 |
@@ -40,6 +40,9 @@ class Cli: Callable<Int> {
|
||||
@Option(names = ["-c", "--count"], description = ["Max number of instructions to run. 0 is infinite"])
|
||||
private var maxInsns: Long = 0
|
||||
|
||||
@Option(names = ["-s", "--switch"], description = ["Value of the switch register, in octal"], defaultValue = "0", converter = [OctalParamConverter::class])
|
||||
private var switchReg: Int = 0
|
||||
|
||||
override fun call(): Int {
|
||||
val tb = org.jline.terminal.TerminalBuilder.terminal()
|
||||
var mbus = MemBus(65536)
|
||||
@@ -52,6 +55,7 @@ class Cli: Callable<Int> {
|
||||
}
|
||||
var cpu = CPU(mbus, if (traceLength > 0 || CPU.debugMode) tracer else NullTracer())
|
||||
val console = DL11(mbus.unibus, tb.input(), tb.output()).apply { mount(mbus.unibus) }
|
||||
cpu.switchReg = switchReg.toUShort()
|
||||
try {
|
||||
|
||||
|
||||
|
||||
@@ -747,6 +747,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
||||
}
|
||||
}
|
||||
|
||||
var switchReg: UShort = 0u
|
||||
private var atBreakpoint: Boolean = false
|
||||
private var allowT: Boolean = true
|
||||
private var control_reg: UShort = 0u
|
||||
@@ -979,17 +980,21 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
||||
if (is_paddr_reg(spec)) {
|
||||
// register
|
||||
registers[addr.toInt()] = value
|
||||
} else if (addr and 1u != 0u) {
|
||||
throw OddAddressError(addr)
|
||||
} else {
|
||||
core.setw(addr.toUShort(), value, dspace)
|
||||
}
|
||||
}
|
||||
|
||||
private fun op_loadw(spec: UInt, dspace: Boolean=true): UShort {
|
||||
val addr = (spec and 0xFFFFu).toUShort()
|
||||
val addr = (spec and 0xFFFFu)
|
||||
val value = if (is_paddr_reg(spec)) {
|
||||
registers[addr.toInt()]
|
||||
} else if (addr and 1u != 0u) {
|
||||
throw OddAddressError(addr)
|
||||
} else {
|
||||
core.getw(addr, dspace)
|
||||
core.getw(addr.toUShort(), dspace)
|
||||
}
|
||||
if (spec bit PADDR_ARG_BIT)
|
||||
tracer.noteReference(spec, value)
|
||||
@@ -1044,15 +1049,21 @@ 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")
|
||||
logger.trace("Stack RED")
|
||||
cpu_err = cpu_err or CPU_ERR_STK_RED
|
||||
val old_psw = psw
|
||||
setPSW(core.getw(6u), true)
|
||||
core.setw(2u, old_psw)
|
||||
core.setw(0u, pc)
|
||||
sp = 0u
|
||||
setPSW(core.getSpace(0).getw(6u), true)
|
||||
if (cur_mode == 0) {
|
||||
sp = 4u
|
||||
} else {
|
||||
shadow_r6[0] = 4u
|
||||
}
|
||||
// manually implement stack_push to inhibit red trap
|
||||
core.setw((sp-2u).toUShort(), old_psw)
|
||||
core.setw((sp-4u).toUShort(), pc)
|
||||
sp = (sp - 4u).toUShort()
|
||||
trapReq = trapReq and TrapReason.RED.clear.inv()
|
||||
pc = core.getw(4u)
|
||||
pc = core.getSpace(0).getw(4u)
|
||||
}
|
||||
|
||||
fun step() {
|
||||
@@ -1061,9 +1072,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
||||
try {
|
||||
// Check early traps
|
||||
if (trapReq != 0) {
|
||||
for (cause in TrapReason.entries.reversed()) {
|
||||
for (cause in TrapReason.entries.asReversed()) {
|
||||
if (trapReq and cause.mask != 0) {
|
||||
// logger.warn("Trapping because $cause")
|
||||
logger.trace("Trapping because {}", cause)
|
||||
try {
|
||||
callVector(cause.vector)
|
||||
} finally {
|
||||
@@ -1088,7 +1099,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
|
||||
mbus.unibus.logger.debug("DATIP: {} @ {}", vector.toOctal(), i)
|
||||
mbus.unibus.logger.trace("DATIP: {} @ {}", vector.toOctal(), i)
|
||||
callVector(vector)
|
||||
source.handled()
|
||||
break
|
||||
@@ -1130,9 +1141,11 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
||||
}
|
||||
is NonExistentMemoryError -> {
|
||||
cpu_err = cpu_err or CPU_ERR_NXM
|
||||
logger.trace("NXM at {}", error.addr.toOctal())
|
||||
setTrap(TrapReason.NXM)
|
||||
}
|
||||
is BusTimeoutError -> {
|
||||
logger.trace("TMO at {}", error.addr.toOctal())
|
||||
cpu_err = cpu_err or CPU_ERR_UNIBUS_TIMEOUT
|
||||
setTrap(TrapReason.NXM)
|
||||
}
|
||||
@@ -1173,7 +1186,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
||||
|
||||
fun callVector(vector: UShort) {
|
||||
val old_psw = psw
|
||||
val newPSW = core.getw((vector + 2u).toUShort()) and 0xCFFFu or (cur_mode shl 12).toUShort()
|
||||
val newPSW = core.getSpace(0).getw((vector + 2u).toUShort()) and 0xCFFFu or (cur_mode shl 12).toUShort()
|
||||
setPSW(newPSW, true)
|
||||
stack_push(old_psw)
|
||||
stack_push(pc)
|
||||
@@ -1186,7 +1199,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
||||
|
||||
private inner class Registers: PAddressSpace {
|
||||
override fun getw(addr: UInt): UShort = when (addr) {
|
||||
0x3FF78u -> 0x70u // Console switch/display
|
||||
0x3FF78u -> switchReg // Console switch/display
|
||||
0x3FFE6u -> control_reg
|
||||
0x3FFF0u -> (mbus.size shr 6).toUShort()
|
||||
0x3FFF2u -> 0u // upper size
|
||||
@@ -1200,7 +1213,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
|
||||
|
||||
override fun setw(addr: UInt, value: UShort) = when (addr) {
|
||||
0x3_FF78u -> {
|
||||
System.err.print("\u001b]0;${value.toOctal()}\u0007")
|
||||
// System.err.print("\u001b]0;${value.toOctal()}\u0007")
|
||||
} // console switch/display reg
|
||||
0x3_FFE6u -> control_reg = value
|
||||
0x3_FFF0u , 0x3_FFF2u , 0x3_FFF4u, 0x3_FFF8u -> {} // read-only registers
|
||||
@@ -1221,10 +1234,10 @@ 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 || sp.toInt() and 0xFFFE == 0xFFFE) {
|
||||
if (addr < stack_limit + 224u || addr.toInt() and 0xFFFE == 0xFFFE) {
|
||||
trapRed()
|
||||
throw EndCycle()
|
||||
} else if (sp < stack_limit + 256u) {
|
||||
} else if (addr < stack_limit + 256u) {
|
||||
// logger.warn("Stack YLW")
|
||||
// stack limit yellow
|
||||
cpu_err = cpu_err or CPU_ERR_STK_YLW
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package com.thequux.mcpdp.core
|
||||
|
||||
sealed class MemoryError(val type: MemoryErrorType, var addr: UInt): Exception("Memory error: $type at ${addr.toString(8)}")
|
||||
sealed class MemoryError(val type: MemoryErrorType, var addr: UInt): Exception("Memory error: $type at ${addr.toString(8)}") {
|
||||
override fun fillInStackTrace(): Throwable {
|
||||
// we don't want stack traces here, as they drastically slow down the emulator.
|
||||
return this
|
||||
}
|
||||
}
|
||||
class BusTimeoutError(addr: UInt): MemoryError(MemoryErrorType.BusTimeout, addr)
|
||||
class OddAddressError(addr: UInt): MemoryError(MemoryErrorType.OddAddress, addr)
|
||||
class NonExistentMemoryError(addr: UInt): MemoryError(MemoryErrorType.NonExistent, addr)
|
||||
|
||||
@@ -4,6 +4,8 @@ package com.thequux.mcpdp.core
|
||||
import com.thequux.mcpdp.ext.bit.*
|
||||
import com.thequux.mcpdp.peripheral.Unibus
|
||||
import com.thequux.mcpdp.util.ProgrammerError
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -35,7 +37,7 @@ private data class PDR(val plf: UShort, var A: Boolean, var W: Boolean, var ed:
|
||||
get() = if (ed) {
|
||||
(plf.toUInt() shl 6) ..< 0x2000u
|
||||
} else {
|
||||
0U..< (plf.toUInt() shl 6)
|
||||
0U.. (plf.toUInt() shl 6+1) // +1 allows the last byte address
|
||||
}
|
||||
|
||||
val asU16: UShort
|
||||
@@ -60,6 +62,7 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
private val MEMORY_ERROR_REG: UInt = 0x3FFE4u
|
||||
}
|
||||
|
||||
private val logger = LoggerFactory.getLogger(this.javaClass)
|
||||
|
||||
private var mmr0: UShort = 0u
|
||||
set(value) {
|
||||
@@ -105,7 +108,17 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
// check range
|
||||
if (addr and 0x1FFFU !in pdr.range) {
|
||||
setMmr0(14, apf)
|
||||
// TODO: handle rest of trap
|
||||
if (logger.isTraceEnabled) {
|
||||
val prv = when (mode) {
|
||||
0 -> 'K'
|
||||
1 -> 'S'
|
||||
2, 3 -> 'U'
|
||||
else -> '?'
|
||||
}
|
||||
val spc = if (dspace) 'D' else 'I'
|
||||
|
||||
logger.trace("MME: Page range error on page $apf: ${prv}${spc}PDR${apf}=$pdr")
|
||||
}
|
||||
cpu.setTrap(TrapReason.MME)
|
||||
// TODO: check whether this is always an abort
|
||||
throw EndCycle()
|
||||
@@ -114,11 +127,15 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
AccessAction.Allow -> {}
|
||||
AccessAction.Trap -> {
|
||||
setMmr0(12, apf)
|
||||
if (logger.isTraceEnabled)
|
||||
logger.trace("MME: ${if (write) "write" else "read"} trap")
|
||||
cpu.setTrap(TrapReason.MME)
|
||||
}
|
||||
AccessAction.Abort -> {
|
||||
setMmr0(13, apf)
|
||||
cpu.setTrap(TrapReason.MME)
|
||||
if (logger.isTraceEnabled)
|
||||
logger.trace("MME: ${if (write) "write" else "read"} ABORT")
|
||||
throw EndCycle()
|
||||
}
|
||||
}
|
||||
@@ -146,7 +163,7 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
modeSpace = if (mapMode.mmanEnable) modeVTabs[min(mode, 2)] else noMmanSpace
|
||||
}
|
||||
|
||||
fun getSpace(mode: Int): VAddressSpace = if (mapMode.mmanEnable) modeVTabs[max(mode, 2)] else noMmanSpace
|
||||
fun getSpace(mode: Int): VAddressSpace = if (mapMode.mmanEnable) modeVTabs[min(mode, 2)] else noMmanSpace
|
||||
|
||||
fun logIncrement(register: Int, amount: Int) {
|
||||
assert(register in 0..7)
|
||||
@@ -163,7 +180,7 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
|
||||
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) }
|
||||
private val modeVTabs: Array<ModeVSpace> = Array(3) { ModeVSpace(charArrayOf('K', 'S', 'U')[it], itabs[it], dtabs[it], false) }
|
||||
private var noMmanSpace = NoMmanSpace()
|
||||
var modeSpace: VAddressSpace = noMmanSpace
|
||||
private set
|
||||
@@ -172,18 +189,25 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
private val unibusTable = UIntArray(32)
|
||||
|
||||
|
||||
private inner class ModeVSpace(private val iSpace: PageTable, private val dSpace: PageTable, var useDSpace: Boolean): VAddressSpace {
|
||||
private inner class ModeVSpace(val mode: Char, private val iSpace: PageTable, private val dSpace: PageTable, useDSpace: Boolean): VAddressSpace {
|
||||
var useDSpace = useDSpace
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
logger.trace("$mode ${if (value) "now " else "not "}using dspace")
|
||||
}
|
||||
field = value
|
||||
}
|
||||
private fun getSpace(dspace: Boolean): PageTable = if (dspace && useDSpace) dSpace else iSpace
|
||||
override fun getw(addr: UShort, dspace: Boolean): UShort = pspace.getw(getSpace(dspace).map(addr, write = false))
|
||||
|
||||
override fun getb(addr: UShort): UByte = pspace.getb(dSpace.map(addr, write = false))
|
||||
override fun getb(addr: UShort): UByte = pspace.getb(getSpace(true).map(addr, write = false))
|
||||
|
||||
override fun setw(addr: UShort, value: UShort, dspace: Boolean) {
|
||||
pspace.setw(getSpace(dspace).map(addr, write = true), value)
|
||||
}
|
||||
|
||||
override fun setb(addr: UShort, value: UByte) {
|
||||
pspace.setb(dSpace.map(addr, write = true), value)
|
||||
pspace.setb(getSpace(true).map(addr, write = true), value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +241,7 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
|
||||
override fun setb(addr: UShort, value: UByte) = withRecovery(addr) { modeSpace.setb(addr, value) }
|
||||
|
||||
private class PdPair(val ipt: PageTable, val dpt: PageTable): PAddressSpace {
|
||||
private inner class PdPair(val ipt: PageTable, val dpt: PageTable): PAddressSpace {
|
||||
override fun getw(addr: UInt): UShort {
|
||||
val mode = addr.toInt() shr 4 and 3
|
||||
val reg = addr.toInt() shr 1 and 7
|
||||
@@ -233,6 +257,17 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
|
||||
override fun setw(addr: UInt, value: UShort) {
|
||||
val mode = addr.toInt() shr 4 and 3
|
||||
val reg = addr.toInt() shr 1 and 7
|
||||
if (mode < 2) {
|
||||
val prv = when (ipt.mode) {
|
||||
0 -> 'K'
|
||||
1 -> 'S'
|
||||
2,3 -> 'U'
|
||||
else -> '?'
|
||||
}
|
||||
val spc = if (mode == 0) 'I' else 'D'
|
||||
if (logger.isTraceEnabled)
|
||||
logger.trace("${prv}${spc}PDR${reg} := ${PDR(value)}")
|
||||
}
|
||||
when (mode) {
|
||||
0 -> ipt.pdr[reg] = PDR(value) // This clears A and W
|
||||
1 -> dpt.pdr[reg] = PDR(value) // This clears A and W
|
||||
|
||||
Reference in New Issue
Block a user