Compare commits

...

8 Commits

9 changed files with 288 additions and 89 deletions

View File

@@ -33,5 +33,5 @@ kotlin {
}
application {
mainClass.set("MainKt")
mainClass.set("com.thequux.mcpdp.cli.Cli")
}

16
doc/diagnostics.md Normal file
View File

@@ -0,0 +1,16 @@
# Running ekba
This will run for a total of 541450515 instructions. To just run the first pass (132198 insns), break at 10742.
```
# Start address of 0200, no switch settings, break at end of second pass.
jdp11 -l diag/ekba.pt -g 200 -b 10722
```
# Running ekbb
This will run for around 1144728 instructions.
```
# Start address of 0200, switch set to 161 to disable unibus trap tests, break at 36170, which is the end of the test
jdp11 -l diag/ekbb.pt -g 200 -s 161 -b 36170
```

View File

@@ -65,13 +65,13 @@ fun CPU.loadAbs(infile: File) {
if (cksum != 0) {
throw Exception("Incorrect checksum: 0x${cksum.toString(16)}")
}
logger.debug("Loading 0x${len.toString(16)} bytes at 0x${addr.toString(16)}")
logger.trace("Loading 0x${len.toString(16)} bytes at 0x${addr.toString(16)}")
if (len == 0) {
// end of file
logger.debug("Tape ended at ${pos+len+7}")
logger.trace("Tape ended at ${pos+len+7}")
if (!(addr bit 0)){
this.pc = addr
logger.debug("Ready to run at ${addr.toString(8)}")
logger.trace("Ready to run at ${addr.toString(8)}")
}
return
} else {

View File

@@ -0,0 +1,111 @@
package com.thequux.mcpdp.cli
import ch.qos.logback.classic.LoggerContext
import com.thequux.mcpdp.core.CPU
import com.thequux.mcpdp.debug.FlightRecorder
import com.thequux.mcpdp.debug.LoggingCollector
import com.thequux.mcpdp.debug.NullTracer
import com.thequux.mcpdp.debug.Tracer
import com.thequux.mcpdp.loadAbs
import com.thequux.mcpdp.peripheral.DL11
import com.thequux.mcpdp.peripheral.MemBus
import org.jline.utils.Log
import org.slf4j.LoggerFactory
import picocli.CommandLine
import picocli.CommandLine.Command
import picocli.CommandLine.ITypeConverter
import picocli.CommandLine.Option
import picocli.CommandLine.Parameters
import java.io.File
import java.util.concurrent.Callable
import kotlin.system.exitProcess
@Command(name="jdp11", mixinStandardHelpOptions = true)
class Cli: Callable<Int> {
@Option(names = ["-l", "--load"], description = ["The paper tape image to load"])
private var load: File? = null
@Option(names = ["-t", "--trace"])
private var traceLength: Int = 0
@Option(names = ["--live"])
private var liveTrace = false
@Option(names = ["-g", "--go"], description = ["Start address in octal. Ignored if odd"], converter = [ OctalParamConverter::class ])
private var startAddress: Int = 1
@Option(names = ["-b", "--break"], description = ["Breakpoint address"], converter = [ OctalParamConverter::class ])
private var breakpoint: Int = 1
@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)
val tracer = Tracer()
if (CPU.debugMode)
tracer.addCollector(LoggingCollector())
val recorder = FlightRecorder(traceLength)
if (traceLength > 0) {
tracer.addCollector(recorder)
}
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 {
tb.enterRawMode()
console.start()
if (load != null) {
cpu.loadAbs(load!!)
}
// cpu.core.setw(0x3C78u, 0u) // halt instead of restart
if (startAddress % 2 == 0) {
cpu.pc = startAddress.toUShort()
cpu.runState = CPU.RunState.RUNNING
}
cpu.breakpoint = breakpoint.toUShort()
// var ninsn = cpu.run(60000000)
if (cpu.runState == CPU.RunState.RUNNING) {
val start = System.nanoTime()
var ninsn = if (maxInsns > 0) cpu.run(maxInsns) else cpu.run()
// var ninsn = cpu.run(13300)
// ninsn += cpu.run(10)
recorder.dump(System.out)
cpu.dumpReg()
val end = System.nanoTime()
System.err.println("Halted at 0${cpu.pc.toString(8)}")
val time = (end - start).toDouble() / 1_000_000_000.0
System.err.println("Executed ${ninsn} in ${time}s: ${ninsn / time / 1_000_000} MIPS")
}
} finally {
println("Exiting")
console.stop()
}
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
loggerContext.stop()
return 0
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
exitProcess(CommandLine(Cli()).execute(*args))
}
}
}
class OctalParamConverter: ITypeConverter<Int> {
override fun convert(value: String): Int {
return value.toInt(8)
}
}

View File

@@ -111,7 +111,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
registers[reg] = stack_pop()
} // RTS
in 0x98..0x9F -> {
if (cur_mode == 0) psw_priority = opcode and 0x7
if (cur_mode == 0) psw_priority_next = opcode and 0x7
} // SPL
in 0xA0..0xBF -> {
val flag = opcode bit 4
@@ -125,11 +125,11 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dst = opc_dst(opcode)
val v = op_loadw(dst)
val res = (v shl 8) or (v shr 8)
op_storw(dst, res)
V = false
C = false
N = res bit 7
Z = (res and 0xFFu) == 0.toUShort()
op_storw(dst, res)
} // SWAB
else -> throw InvalidOpcodeException()
} }
@@ -154,35 +154,37 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
insnTable[0x0A] = {opcode ->
when (opcode shr 6 and 3) {
0 -> { // CLR
op_storw(opc_dst(opcode), 0U)
N = false
V = false
C = false
Z = true
op_storw(opc_dst(opcode), 0U)
} // CLR
1 -> { // COM
val dst = opc_dst(opcode)
val res = op_loadw(dst).inv().also { op_storw(dst, it) }
val res = op_loadw(dst).inv()
N = res bit 15
Z = res == 0U.toUShort()
C = true
V = false
op_storw(dst, res)
} // COM
2 -> { // INC
val dst = opc_dst(opcode)
val src = op_loadw(dst)
val res = src.inc()
op_storw(dst, res)
N = res bit 15
Z = res == 0.toUShort()
V = res == 0x8000.toUShort()
op_storw(dst, res)
} // INC
3 -> { // DEC
val dst = opc_dst(opcode)
val res = op_loadw(dst).dec().also { op_storw(dst, it) }
val res = op_loadw(dst).dec()
N = res bit 15
Z = res == 0.toUShort()
V = res == 0x7FFF.toUShort()
op_storw(dst, res)
} // DEC
}
} // CLR, COM, INC, DEC
@@ -191,31 +193,31 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
0 -> { // NEG
val dst = opc_dst(opcode)
val res = op_loadw(dst).inv().inc()
op_storw(dst, res)
N = res bit 15
Z = res == 0.toUShort()
V = res == 0x8000.toUShort()
C = !Z
op_storw(dst, res)
} // NEG
1 -> { // ADC
val dst = opc_dst(opcode)
val c: UShort = if (C) 1u else 0u
val res = (op_loadw(dst) + c).toUShort()
op_storw(dst, res)
N = res bit 15
Z = res == 0u.toUShort()
V = (res == 0x8000u.toUShort()) and C
C = Z and C
op_storw(dst, res)
} // ADC
2 -> {
val dst = opc_dst(opcode)
val src = op_loadw(dst)
val res = if (C) src.dec() else src
op_storw(dst, res)
N = res bit 15
Z = res == 0.toUShort()
V = res == 0x8000.toUShort()
C = C and (src == 0.toUShort())
op_storw(dst, res)
} // SBC
3 -> {
val dst = op_loadw(opc_dst(opcode))
@@ -232,41 +234,41 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dst = opc_dst(opcode)
val src = op_loadw(dst)
val res = (src shr 1).bit(15, C)
op_storw(dst, res)
C = src bit 0
N = res bit 15
Z = res == 0.toUShort()
V = N xor C
op_storw(dst, res)
} // ROR
1 -> {
val dst = opc_dst(opcode)
val src = op_loadw(dst)
val res = (src shl 1).bit(0, C)
op_storw(dst, res)
C = src bit 15
N = res bit 15
Z = res == 0.toUShort()
V = N xor C
op_storw(dst, res)
} // ROL
2 -> { // ASR
val dst = opc_dst(opcode)
val src = op_loadw(dst).toShort()
val res = (src shr 1).toUShort()
op_storw(dst, res)
N = res bit 15
Z = res == 0.toUShort()
C = src bit 0
V = N xor C
op_storw(dst, res)
} // ASR
3 -> { // ASL
val dst = opc_dst(opcode)
val src = op_loadw(dst)
val res = src shl 1
op_storw(dst, res.toUShort())
N = res bit 15
Z = res == 0.toUShort()
C = src and 0x8000u != 0u.toUShort()
V = N xor C
op_storw(dst, res)
} // ASL
}
} // ROR, ROL, ASR, ASL
@@ -289,14 +291,17 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref
core.getSpace(prv_mode).getw(src.toUShort(), dspace = false)
}
stack_push(v)
N = v bit 15
Z = v == 0u.toUShort()
V = false
stack_push(v)
} // MFPI // TODO
2 -> {
val v = stack_pop()
val dest = opc_dst(opcode)
N = v bit 15
Z = v == 0u.toUShort()
V = false
if (is_paddr_reg(dest)) {
if (dest and 7u == 6u && prv_mode != cur_mode) {
shadow_r6[prv_mode] = v
@@ -306,14 +311,11 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref
core.getSpace(prv_mode).setw(dest.toUShort(), v, dspace = false)
}
N = v bit 15
Z = v == 0u.toUShort()
V = false
} // MTPI // TODO
3 -> {
op_storw(opc_dst(opcode), if (N) (-1).toUShort() else 0.toUShort())
Z = !N
V = false
op_storw(opc_dst(opcode), if (N) (-1).toUShort() else 0.toUShort())
} // SXT
}
} // MARK, MFPI, MTPI, SXT
@@ -325,10 +327,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val src = opc_src(opcode)
val dst = opc_dst(opcode)
op_loadw(src).also {
op_storw(dst, it)
N = it bit 15
Z = it == 0.toUShort()
V = false
op_storw(dst, it)
}
} // MOV
for (i in 0x20..0x2F) insnTable[i] = { opcode -> // CMP
@@ -352,19 +354,19 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadw(dst) and op_loadw(src).inv()
op_storw(dst, res)
N = res bit 15
Z = res == 0u.toUShort()
V = false
op_storw(dst, res)
} // BIC
for (i in 0x50..0x5F) insnTable[i] = { opcode -> // BIS
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadw(dst) or op_loadw(src)
op_storw(dst, res)
N = res and 0x8000u != 0u.toUShort()
Z = res == 0u.toUShort()
V = false
op_storw(dst, res)
} // BIS
for (i in 0x60..0x6F) insnTable[i] = { opcode -> // ADD
val src = opc_src(opcode)
@@ -373,12 +375,12 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dstv = op_loadw(dst)
val res = (srcv + dstv)
val resw = res.toUShort()
op_storw(dst, res.toUShort())
N = resw > 0x7FFFu
Z = resw == 0.toUShort()
val src_sign = srcv and 0x8000u
V = (src_sign == dstv and 0x8000u) && (src_sign != resw and 0x8000u)
C = (res >= 0x10000u)
op_storw(dst, res.toUShort())
} // ADD
insnTable[0x70] = { opcode -> // MUL
val r = opcode shr 6 and 0x7
@@ -493,10 +495,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val r = opcode shr 6 and 7
val dst = opc_dst(opcode)
val res = op_loadw(dst) xor registers[r]
op_storw(dst, res)
N = res bit 15
Z = res == 0.toUShort()
V = false
op_storw(dst, res)
} // XOR
// 0x7A and 0x7C are undefined
insnTable[0x7E] = { opcode -> // SOB
@@ -526,33 +528,36 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
insnTable[0x8A] = { opcode ->
when (opcode shr 6 and 3) {
0 -> { // CLRB
op_storb(opc_dstb(opcode), 0U)
N = false
V = false
C = false
Z = true
op_storb(opc_dstb(opcode), 0U)
} // CLRB
1 -> { // COMB
val dst = opc_dstb(opcode)
val res = op_loadb(dst).inv().also { op_storb(dst, it) }
val res = op_loadb(dst).inv()
N = res bit 7
Z = res == 0U.toUByte()
C = true
V = false
op_storb(dst, res)
} // COMB
2 -> { // INC
2 -> { // INCB
val dst = opc_dstb(opcode)
val res = op_loadb(dst).inc().also { op_storb(dst, it) }
val res = op_loadb(dst).inc()
N = res bit 7
Z = res == 0.toUByte()
V = res == 0x80.toUByte()
op_storb(dst, res)
} // INCB
3 -> { // DEC
val dst = opc_dstb(opcode)
val res = op_loadb(dst).dec().also { op_storb(dst, it) }
val res = op_loadb(dst).dec()
N = res bit 7
Z = res == 0.toUByte()
V = res == 0x7F.toUByte()
op_storb(dst, res)
} // DECB
}
} // CLRB, COMB, INCB, DECB
@@ -561,31 +566,31 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
0 -> { // NEGB
val dst = opc_dstb(opcode)
val res = op_loadb(dst).inv().inc()
op_storb(dst, res)
N = res bit 7
Z = res == 0.toUByte()
V = res == 0x8000.toUByte()
C = !Z
op_storb(dst, res)
} // NEGB
1 -> { // ADCB
val dst = opc_dstb(opcode)
val c: UShort = if (C) 1u else 0u
val res = (op_loadb(dst) + c).toUByte()
op_storb(dst, res)
N = res bit 7
Z = res == 0u.toUByte()
V = (res == 0x80u.toUByte()) and C
C = Z and C
op_storb(dst, res)
} // ADCB
2 -> {
val dst = opc_dstb(opcode)
val src = op_loadb(dst)
val res = if (C) src.dec() else src
op_storb(dst, res)
N = res bit 8
Z = res == 0.toUByte()
V = res == 0x80.toUByte()
C = C and (src == 0.toUByte())
op_storb(dst, res)
} // SBCB
3 -> {
val dst = op_loadb(opc_dstb(opcode))
@@ -602,41 +607,41 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dst = opc_dstb(opcode)
val src = op_loadb(dst)
val res = (src shr 1).bit(7, C)
op_storb(dst, res)
C = src bit 0
N = res bit 7
Z = res == 0.toUByte()
V = N xor C
op_storb(dst, res)
} // RORB
1 -> {
val dst = opc_dstb(opcode)
val src = op_loadb(dst)
val res = (src shl 1).bit(0, C)
op_storb(dst, res)
C = src bit 7
N = res bit 7
Z = res == 0.toUByte()
V = N xor C
op_storb(dst, res)
} // ROLB
2 -> { // ASRB
val dst = opc_dstb(opcode)
val src = op_loadb(dst).toByte()
val res = (src shr 1).toUByte()
op_storb(dst, res)
N = res bit 7
Z = res == 0.toUByte()
C = src bit 0
V = N xor C
op_storb(dst, res)
} // ASRB
3 -> { // ASLB
val dst = opc_dstb(opcode)
val src = op_loadb(dst)
val res = (src shl 1).toUByte()
op_storw(dst, res.toUShort())
N = res bit 7
Z = res == 0.toUByte()
C = src bit 7
V = N xor C
op_storw(dst, res.toUShort())
} // ASLB
}
} // RORB, ROLB, ASRB, ASLB
@@ -653,14 +658,17 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref
core.getSpace(prv_mode).getw(src.toUShort(), dspace = true)
}
stack_push(v)
N = v bit 15
Z = v == 0u.toUShort()
V = false
stack_push(v)
} // MFPD // TODO
2 -> {
val v = stack_pop()
val dest = opc_dst(opcode)
N = v bit 15
Z = v == 0u.toUShort()
V = false
if (is_paddr_reg(dest)) {
if (dest and 7u == 6u && prv_mode != cur_mode) {
shadow_r6[prv_mode] = v
@@ -670,9 +678,6 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref
core.getSpace(prv_mode).setw(dest.toUShort(), v, dspace = true)
}
N = v bit 15
Z = v == 0u.toUShort()
V = false
} // MTPD // TODO
else -> { throw InvalidOpcodeException() } // Reserved
}
@@ -683,6 +688,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dst = opc_dstb(opcode)
val dstv = op_loadb(src)
N = dstv bit 7
Z = dstv == 0.toUByte()
V = false
if (is_paddr_reg(dst)) {
op_storw(dst, dstv.toUShort() sex 8)
dstv.toUShort() sex 8
@@ -690,9 +698,6 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
op_storb(dst, dstv)
dstv.toUShort()
}
N = dstv bit 7
Z = dstv == 0.toUByte()
V = false
} // MOVB
for (i in 0xA0..0xAF) insnTable[i] = { opcode -> // CMPB
@@ -716,19 +721,19 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val src = opc_srcb(opcode)
val dst = opc_dstb(opcode)
val res = op_loadb(dst) and op_loadb(src).inv()
op_storb(dst, res)
N = res bit 7
Z = res == 0u.toUByte()
V = false
op_storb(dst, res)
} // BICB
for (i in 0xD0..0xDF) insnTable[i] = { opcode -> // BISB
val src = opc_srcb(opcode)
val dst = opc_dstb(opcode)
val res = op_loadb(dst) or op_loadb(src)
op_storb(dst, res)
N = res bit 7
Z = res == 0u.toUByte()
V = false
op_storb(dst, res)
} // BISB
for (i in 0xE0..0xEF) insnTable[i] = { opcode ->
val src = opc_src(opcode)
@@ -736,17 +741,19 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val srcv = op_loadw(src)
val dstv = op_loadw(dst)
val res = (dstv.toShort() - srcv.toShort())
op_storw(dst, res.toUShort())
N = res < 0
Z = res == 0
V = ((srcv bit 31) xor (dstv bit 15)) and ((srcv bit 15) == (res bit 31))
C = (dst.toInt() + src.inv().inc().toInt()) < 0x1_0000
op_storw(dst, res.toUShort())
} // SUB
// insnTable[0x0E] = // TODO: check this
// insnTable[0x0F] = // TODO: check this
}
}
var switchReg: UShort = 0u
private var atBreakpoint: Boolean = false
private var allowT: Boolean = true
private var control_reg: UShort = 0u
val logger: Logger = LoggerFactory.getLogger(this.javaClass)
@@ -765,6 +772,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
private var stack_limit: UShort = 0u
private var handlingStackRed: Boolean = false
private var trapReq: Int = 0
var breakpoint: UShort = 1u
val core = PagingUnit(mbus, this)
var runState: RunState = RunState.HALTED
@@ -793,7 +801,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
registerSet = newpsw shr 11 and 1
cur_mode = newpsw shr 14
prv_mode = newpsw shr 12 and 3
psw_priority = newpsw shr 5 and 7
psw_priority_next = newpsw shr 5 and 7
} else {
registerSet = registerSet or (newpsw shr 11 and 1)
cur_mode = cur_mode or (newpsw shr 14 and 3)
@@ -811,6 +819,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
}
private var prv_mode: Int = 0
private var psw_priority: Int = 0
private var psw_priority_next: Int = 0
var pc: UShort
get() = registers[7]
set(value) {
@@ -977,17 +986,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)
@@ -1042,15 +1055,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() {
@@ -1059,9 +1078,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 {
@@ -1086,7 +1105,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)
mbus.unibus.logger.trace("DATIP: {} @ {}", vector.toOctal(), i)
callVector(vector)
source.handled()
break
@@ -1095,7 +1114,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)
}
}
@@ -1109,8 +1128,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
}
if (runState == RunState.WAIT_FOR_INTERRUPT) {
// Note that it is impossible to have a pending priority change at the same time as a WAIT insn
return
}
psw_priority = psw_priority_next
// Proceed to handling instruction
try {
@@ -1128,9 +1149,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)
}
@@ -1145,29 +1168,35 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
fun run(): Long {
var ninsn: Long = 0
while (runState == RunState.RUNNING) {
atBreakpoint = false
while (runState == RunState.RUNNING && !atBreakpoint) {
ninsn += run(Long.MAX_VALUE)
}
return ninsn
}
fun run(nstep: Long): Long {
atBreakpoint = false
var ninsn: Long = 0
while (runState == RunState.RUNNING && ninsn < nstep) {
ninsn++
if (pc == 0x3224.toUShort()) {
runState = runState
runState = RunState.HALTED
while (runState == RunState.RUNNING && ninsn < nstep && !atBreakpoint) {
if (pc == breakpoint) {
atBreakpoint = true
ninsn = ninsn // Dummy statement to set debugger bkpt on
// continue
}
if (!atBreakpoint) {
ninsn++
step()
}
step()
}
return ninsn
}
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)
psw_priority = psw_priority_next // calling a vector sets priority immediately
stack_push(old_psw)
stack_push(pc)
pc = core.getw(vector)
@@ -1179,7 +1208,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
@@ -1193,7 +1222,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
@@ -1214,10 +1243,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
@@ -1233,7 +1262,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
enum class RunState(val handleInterrupt: Boolean) {
RUNNING(handleInterrupt = true),
HALTED(handleInterrupt = false),
WAIT_FOR_INTERRUPT(handleInterrupt = true)
WAIT_FOR_INTERRUPT(handleInterrupt = true),
}
}

View File

@@ -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)

View File

@@ -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
@@ -314,8 +349,8 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
val regs = ConfigRegisters()
unibus.run {
deviceView = UnibusMap()
attach(0x3_F480u, 6, PdPair(itabs[0], dtabs[0]))
attach(0x3_F4C0u, 6, PdPair(itabs[1], dtabs[1]))
attach(0x3_F480u, 6, PdPair(itabs[1], dtabs[1]))
attach(0x3_F4C0u, 6, PdPair(itabs[0], dtabs[0]))
attach(0x3_FF80u, 6, PdPair(itabs[2], dtabs[2])) // User PAR/PDR
attach(0x3_FF7Au, 1, regs) // MMR0
attach(0x3_FF7Cu, 2, regs) // MMR1-2

View File

@@ -42,7 +42,7 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re
override fun reset() {
rcsr = 0x0u
rcsr = 0x0u
// xcsr = 0x80u
xcsr = 0x80u
intrRcv.level = false
intrXmit.level = false
@@ -76,7 +76,10 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re
if (value bit 0) {rcsr = rcsr bic 7 }
}
2 -> { rcsr = rcsr bic 7 }
4 -> xcsr = xcsr.maskSet(value, 0x0045u)
4 -> {
xcsr = xcsr.maskSet(value, 0x0045u)
if (!(xcsr bit 6)) intrXmit.level = false
}
6 -> {
val b = value.toInt() and 0xFF
if (xcsr bit 2) {

View File

@@ -74,7 +74,7 @@ class Unibus: PAddressSpace, Subregion(12, 6) {
/// The view of the unibus from a device
var deviceView: PAddressSpace = this
internal set
private val logger = LoggerFactory.getLogger(this.javaClass)
internal val logger = LoggerFactory.getLogger(this.javaClass)
private val queue: Array<MutableList<InterruptSource>> = Array(8) { Vector(4) }
private val devices: HashSet<Peripheral> = HashSet()