Compare commits

...

11 Commits

12 changed files with 369 additions and 117 deletions

View File

@@ -33,5 +33,5 @@ kotlin {
} }
application { 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

@@ -1,6 +1,7 @@
import ch.qos.logback.classic.LoggerContext import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.core.util.StatusPrinter import ch.qos.logback.core.util.StatusPrinter
import com.thequux.mcpdp.core.CPU import com.thequux.mcpdp.core.CPU
import com.thequux.mcpdp.debug.FlightRecorder
import com.thequux.mcpdp.debug.LoggingCollector import com.thequux.mcpdp.debug.LoggingCollector
import com.thequux.mcpdp.debug.NullTracer import com.thequux.mcpdp.debug.NullTracer
import com.thequux.mcpdp.debug.Tracer import com.thequux.mcpdp.debug.Tracer
@@ -24,7 +25,9 @@ fun main(args: Array<String>) {
var mbus = MemBus(65536) var mbus = MemBus(65536)
val tracer = Tracer() val tracer = Tracer()
val loggingCollector = LoggingCollector() val loggingCollector = LoggingCollector()
tracer.addCollector(loggingCollector) // tracer.addCollector(loggingCollector)
val recorder = FlightRecorder(3000)
tracer.addCollector(recorder)
var cpu = CPU(mbus, if (CPU.debugMode) tracer else NullTracer()) var cpu = CPU(mbus, if (CPU.debugMode) tracer else NullTracer())
val console = DL11(mbus.unibus, tb.input(), tb.output()).apply { mount(mbus.unibus) } val console = DL11(mbus.unibus, tb.input(), tb.output()).apply { mount(mbus.unibus) }
try { try {
@@ -34,19 +37,22 @@ fun main(args: Array<String>) {
console.start() console.start()
cpu.loadAbs(File(args[0])) cpu.loadAbs(File(args[0]))
cpu.core.setw(0x3C78u, 0u) // halt instead of restart
cpu.runState = CPU.RunState.RUNNING cpu.runState = CPU.RunState.RUNNING
cpu.pc = 0x80u cpu.pc = 0x80u
val start = System.nanoTime() val start = System.nanoTime()
// var ninsn = cpu.run(600000000) // var ninsn = cpu.run(60000000)
var ninsn = cpu.run(376670)
cpu.tracer = tracer cpu.tracer = tracer
ninsn += cpu.run(30) var ninsn = cpu.run(323227)
// var ninsn = cpu.run(13300)
// ninsn += cpu.run(10)
recorder.dump(System.out)
cpu.dumpReg() cpu.dumpReg()
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)}")
val time = (end - start).toDouble() / 1_000_000_000.0 val time = (end - start).toDouble() / 1_000_000_000.0
println("Executed ${ninsn} in ${time}s: ${ninsn / time / 1_000_000} MIPS") System.err.println("Executed ${ninsn} in ${time}s: ${ninsn / time / 1_000_000} MIPS")
} finally { } finally {
println("Exiting") println("Exiting")
console.stop() console.stop()

View File

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

@@ -82,7 +82,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
0x0001 -> runState = RunState.WAIT_FOR_INTERRUPT // WAIT 0x0001 -> runState = RunState.WAIT_FOR_INTERRUPT // WAIT
0x0002 -> { // RTI 0x0002 -> { // RTI
pc = stack_pop() pc = stack_pop()
psw = stack_pop() // TODO: check privilege on mode; check psw[11] setPSW(stack_pop(), cur_mode == 0) // TODO: check privilege on mode; check psw[11]
} // RTI } // RTI
0x0003 -> setTrap(TrapReason.BPT) // BPT 0x0003 -> setTrap(TrapReason.BPT) // BPT
0x0004 -> setTrap(TrapReason.IOT) // IOT 0x0004 -> setTrap(TrapReason.IOT) // IOT
@@ -94,7 +94,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
} // bus reset TODO: bus init } // bus reset TODO: bus init
0x0006 -> { 0x0006 -> {
pc = stack_pop() pc = stack_pop()
psw = stack_pop() setPSW(stack_pop(), cur_mode == 0)
allowT = false allowT = false
} // RTT } // RTT
in 0x40..0x7f -> { in 0x40..0x7f -> {
@@ -111,7 +111,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
registers[reg] = stack_pop() registers[reg] = stack_pop()
} // RTS } // RTS
in 0x98..0x9F -> { in 0x98..0x9F -> {
psw_priority = opcode and 0x7 if (cur_mode == 0) psw_priority_next = opcode and 0x7
} // SPL } // SPL
in 0xA0..0xBF -> { in 0xA0..0xBF -> {
val flag = opcode bit 4 val flag = opcode bit 4
@@ -125,11 +125,11 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val v = op_loadw(dst) val v = op_loadw(dst)
val res = (v shl 8) or (v shr 8) val res = (v shl 8) or (v shr 8)
op_storw(dst, res)
V = false V = false
C = false C = false
N = res bit 7 N = res bit 7
Z = (res and 0xFFu) == 0.toUShort() Z = (res and 0xFFu) == 0.toUShort()
op_storw(dst, res)
} // SWAB } // SWAB
else -> throw InvalidOpcodeException() else -> throw InvalidOpcodeException()
} } } }
@@ -154,35 +154,37 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
insnTable[0x0A] = {opcode -> insnTable[0x0A] = {opcode ->
when (opcode shr 6 and 3) { when (opcode shr 6 and 3) {
0 -> { // CLR 0 -> { // CLR
op_storw(opc_dst(opcode), 0U)
N = false N = false
V = false V = false
C = false C = false
Z = true Z = true
op_storw(opc_dst(opcode), 0U)
} // CLR } // CLR
1 -> { // COM 1 -> { // COM
val dst = opc_dst(opcode) 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 N = res bit 15
Z = res == 0U.toUShort() Z = res == 0U.toUShort()
C = true C = true
V = false V = false
op_storw(dst, res)
} // COM } // COM
2 -> { // INC 2 -> { // INC
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val src = op_loadw(dst) val src = op_loadw(dst)
val res = src.inc() val res = src.inc()
op_storw(dst, res)
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
V = res == 0x8000.toUShort() V = res == 0x8000.toUShort()
op_storw(dst, res)
} // INC } // INC
3 -> { // DEC 3 -> { // DEC
val dst = opc_dst(opcode) 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 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
V = res == 0x7FFF.toUShort() V = res == 0x7FFF.toUShort()
op_storw(dst, res)
} // DEC } // DEC
} }
} // CLR, COM, INC, DEC } // CLR, COM, INC, DEC
@@ -191,31 +193,31 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
0 -> { // NEG 0 -> { // NEG
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val res = op_loadw(dst).inv().inc() val res = op_loadw(dst).inv().inc()
op_storw(dst, res)
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
V = res == 0x8000.toUShort() V = res == 0x8000.toUShort()
C = !Z C = !Z
op_storw(dst, res)
} // NEG } // NEG
1 -> { // ADC 1 -> { // ADC
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val c: UShort = if (C) 1u else 0u val c: UShort = if (C) 1u else 0u
val res = (op_loadw(dst) + c).toUShort() val res = (op_loadw(dst) + c).toUShort()
op_storw(dst, res)
N = res bit 15 N = res bit 15
Z = res == 0u.toUShort() Z = res == 0u.toUShort()
V = (res == 0x8000u.toUShort()) and C V = (res == 0x8000u.toUShort()) and C
C = Z and C C = Z and C
op_storw(dst, res)
} // ADC } // ADC
2 -> { 2 -> {
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val src = op_loadw(dst) val src = op_loadw(dst)
val res = if (C) src.dec() else src val res = if (C) src.dec() else src
op_storw(dst, res)
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
V = res == 0x8000.toUShort() V = res == 0x8000.toUShort()
C = C and (src == 0.toUShort()) C = C and (src == 0.toUShort())
op_storw(dst, res)
} // SBC } // SBC
3 -> { 3 -> {
val dst = op_loadw(opc_dst(opcode)) 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 dst = opc_dst(opcode)
val src = op_loadw(dst) val src = op_loadw(dst)
val res = (src shr 1).bit(15, C) val res = (src shr 1).bit(15, C)
op_storw(dst, res)
C = src bit 0 C = src bit 0
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
V = N xor C V = N xor C
op_storw(dst, res)
} // ROR } // ROR
1 -> { 1 -> {
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val src = op_loadw(dst) val src = op_loadw(dst)
val res = (src shl 1).bit(0, C) val res = (src shl 1).bit(0, C)
op_storw(dst, res)
C = src bit 15 C = src bit 15
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
V = N xor C V = N xor C
op_storw(dst, res)
} // ROL } // ROL
2 -> { // ASR 2 -> { // ASR
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val src = op_loadw(dst).toShort() val src = op_loadw(dst).toShort()
val res = (src shr 1).toUShort() val res = (src shr 1).toUShort()
op_storw(dst, res)
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
C = src bit 0 C = src bit 0
V = N xor C V = N xor C
op_storw(dst, res)
} // ASR } // ASR
3 -> { // ASL 3 -> { // ASL
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val src = op_loadw(dst) val src = op_loadw(dst)
val res = src shl 1 val res = src shl 1
op_storw(dst, res.toUShort())
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
C = src and 0x8000u != 0u.toUShort() C = src and 0x8000u != 0u.toUShort()
V = N xor C V = N xor C
op_storw(dst, res)
} // ASL } // ASL
} }
} // ROR, ROL, ASR, ASL } // ROR, ROL, ASR, ASL
@@ -289,14 +291,17 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref // memory ref
core.getSpace(prv_mode).getw(src.toUShort(), dspace = false) core.getSpace(prv_mode).getw(src.toUShort(), dspace = false)
} }
stack_push(v)
N = v bit 15 N = v bit 15
Z = v == 0u.toUShort() Z = v == 0u.toUShort()
V = false V = false
stack_push(v)
} // MFPI // TODO } // MFPI // TODO
2 -> { 2 -> {
val v = stack_pop() val v = stack_pop()
val dest = opc_dst(opcode) val dest = opc_dst(opcode)
N = v bit 15
Z = v == 0u.toUShort()
V = false
if (is_paddr_reg(dest)) { if (is_paddr_reg(dest)) {
if (dest and 7u == 6u && prv_mode != cur_mode) { if (dest and 7u == 6u && prv_mode != cur_mode) {
shadow_r6[prv_mode] = v shadow_r6[prv_mode] = v
@@ -306,14 +311,11 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref // memory ref
core.getSpace(prv_mode).setw(dest.toUShort(), v, dspace = false) core.getSpace(prv_mode).setw(dest.toUShort(), v, dspace = false)
} }
N = v bit 15
Z = v == 0u.toUShort()
V = false
} // MTPI // TODO } // MTPI // TODO
3 -> { 3 -> {
op_storw(opc_dst(opcode), if (N) (-1).toUShort() else 0.toUShort())
Z = !N Z = !N
V = false V = false
op_storw(opc_dst(opcode), if (N) (-1).toUShort() else 0.toUShort())
} // SXT } // SXT
} }
} // MARK, MFPI, MTPI, SXT } // MARK, MFPI, MTPI, SXT
@@ -325,10 +327,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val src = opc_src(opcode) val src = opc_src(opcode)
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
op_loadw(src).also { op_loadw(src).also {
op_storw(dst, it)
N = it bit 15 N = it bit 15
Z = it == 0.toUShort() Z = it == 0.toUShort()
V = false V = false
op_storw(dst, it)
} }
} // MOV } // MOV
for (i in 0x20..0x2F) insnTable[i] = { opcode -> // CMP 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 src = opc_src(opcode)
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val res = op_loadw(dst) and op_loadw(src).inv() val res = op_loadw(dst) and op_loadw(src).inv()
op_storw(dst, res)
N = res bit 15 N = res bit 15
Z = res == 0u.toUShort() Z = res == 0u.toUShort()
V = false V = false
op_storw(dst, res)
} // BIC } // BIC
for (i in 0x50..0x5F) insnTable[i] = { opcode -> // BIS for (i in 0x50..0x5F) insnTable[i] = { opcode -> // BIS
val src = opc_src(opcode) val src = opc_src(opcode)
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val res = op_loadw(dst) or op_loadw(src) val res = op_loadw(dst) or op_loadw(src)
op_storw(dst, res)
N = res and 0x8000u != 0u.toUShort() N = res and 0x8000u != 0u.toUShort()
Z = res == 0u.toUShort() Z = res == 0u.toUShort()
V = false V = false
op_storw(dst, res)
} // BIS } // BIS
for (i in 0x60..0x6F) insnTable[i] = { opcode -> // ADD for (i in 0x60..0x6F) insnTable[i] = { opcode -> // ADD
val src = opc_src(opcode) val src = opc_src(opcode)
@@ -373,12 +375,12 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dstv = op_loadw(dst) val dstv = op_loadw(dst)
val res = (srcv + dstv) val res = (srcv + dstv)
val resw = res.toUShort() val resw = res.toUShort()
op_storw(dst, res.toUShort())
N = resw > 0x7FFFu N = resw > 0x7FFFu
Z = resw == 0.toUShort() Z = resw == 0.toUShort()
val src_sign = srcv and 0x8000u val src_sign = srcv and 0x8000u
V = (src_sign == dstv and 0x8000u) && (src_sign != resw and 0x8000u) V = (src_sign == dstv and 0x8000u) && (src_sign != resw and 0x8000u)
C = (res >= 0x10000u) C = (res >= 0x10000u)
op_storw(dst, res.toUShort())
} // ADD } // ADD
insnTable[0x70] = { opcode -> // MUL insnTable[0x70] = { opcode -> // MUL
val r = opcode shr 6 and 0x7 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 r = opcode shr 6 and 7
val dst = opc_dst(opcode) val dst = opc_dst(opcode)
val res = op_loadw(dst) xor registers[r] val res = op_loadw(dst) xor registers[r]
op_storw(dst, res)
N = res bit 15 N = res bit 15
Z = res == 0.toUShort() Z = res == 0.toUShort()
V = false V = false
op_storw(dst, res)
} // XOR } // XOR
// 0x7A and 0x7C are undefined // 0x7A and 0x7C are undefined
insnTable[0x7E] = { opcode -> // SOB insnTable[0x7E] = { opcode -> // SOB
@@ -526,33 +528,36 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
insnTable[0x8A] = { opcode -> insnTable[0x8A] = { opcode ->
when (opcode shr 6 and 3) { when (opcode shr 6 and 3) {
0 -> { // CLRB 0 -> { // CLRB
op_storb(opc_dstb(opcode), 0U)
N = false N = false
V = false V = false
C = false C = false
Z = true Z = true
op_storb(opc_dstb(opcode), 0U)
} // CLRB } // CLRB
1 -> { // COMB 1 -> { // COMB
val dst = opc_dstb(opcode) 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 N = res bit 7
Z = res == 0U.toUByte() Z = res == 0U.toUByte()
C = true C = true
V = false V = false
op_storb(dst, res)
} // COMB } // COMB
2 -> { // INC 2 -> { // INCB
val dst = opc_dstb(opcode) 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 N = res bit 7
Z = res == 0.toUByte() Z = res == 0.toUByte()
V = res == 0x80.toUByte() V = res == 0x80.toUByte()
op_storb(dst, res)
} // INCB } // INCB
3 -> { // DEC 3 -> { // DEC
val dst = opc_dstb(opcode) 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 N = res bit 7
Z = res == 0.toUByte() Z = res == 0.toUByte()
V = res == 0x7F.toUByte() V = res == 0x7F.toUByte()
op_storb(dst, res)
} // DECB } // DECB
} }
} // CLRB, COMB, INCB, DECB } // CLRB, COMB, INCB, DECB
@@ -561,31 +566,31 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
0 -> { // NEGB 0 -> { // NEGB
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val res = op_loadb(dst).inv().inc() val res = op_loadb(dst).inv().inc()
op_storb(dst, res)
N = res bit 7 N = res bit 7
Z = res == 0.toUByte() Z = res == 0.toUByte()
V = res == 0x8000.toUByte() V = res == 0x8000.toUByte()
C = !Z C = !Z
op_storb(dst, res)
} // NEGB } // NEGB
1 -> { // ADCB 1 -> { // ADCB
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val c: UShort = if (C) 1u else 0u val c: UShort = if (C) 1u else 0u
val res = (op_loadb(dst) + c).toUByte() val res = (op_loadb(dst) + c).toUByte()
op_storb(dst, res)
N = res bit 7 N = res bit 7
Z = res == 0u.toUByte() Z = res == 0u.toUByte()
V = (res == 0x80u.toUByte()) and C V = (res == 0x80u.toUByte()) and C
C = Z and C C = Z and C
op_storb(dst, res)
} // ADCB } // ADCB
2 -> { 2 -> {
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val src = op_loadb(dst) val src = op_loadb(dst)
val res = if (C) src.dec() else src val res = if (C) src.dec() else src
op_storb(dst, res)
N = res bit 8 N = res bit 8
Z = res == 0.toUByte() Z = res == 0.toUByte()
V = res == 0x80.toUByte() V = res == 0x80.toUByte()
C = C and (src == 0.toUByte()) C = C and (src == 0.toUByte())
op_storb(dst, res)
} // SBCB } // SBCB
3 -> { 3 -> {
val dst = op_loadb(opc_dstb(opcode)) 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 dst = opc_dstb(opcode)
val src = op_loadb(dst) val src = op_loadb(dst)
val res = (src shr 1).bit(7, C) val res = (src shr 1).bit(7, C)
op_storb(dst, res)
C = src bit 0 C = src bit 0
N = res bit 7 N = res bit 7
Z = res == 0.toUByte() Z = res == 0.toUByte()
V = N xor C V = N xor C
op_storb(dst, res)
} // RORB } // RORB
1 -> { 1 -> {
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val src = op_loadb(dst) val src = op_loadb(dst)
val res = (src shl 1).bit(0, C) val res = (src shl 1).bit(0, C)
op_storb(dst, res)
C = src bit 7 C = src bit 7
N = res bit 7 N = res bit 7
Z = res == 0.toUByte() Z = res == 0.toUByte()
V = N xor C V = N xor C
op_storb(dst, res)
} // ROLB } // ROLB
2 -> { // ASRB 2 -> { // ASRB
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val src = op_loadb(dst).toByte() val src = op_loadb(dst).toByte()
val res = (src shr 1).toUByte() val res = (src shr 1).toUByte()
op_storb(dst, res)
N = res bit 7 N = res bit 7
Z = res == 0.toUByte() Z = res == 0.toUByte()
C = src bit 0 C = src bit 0
V = N xor C V = N xor C
op_storb(dst, res)
} // ASRB } // ASRB
3 -> { // ASLB 3 -> { // ASLB
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val src = op_loadb(dst) val src = op_loadb(dst)
val res = (src shl 1).toUByte() val res = (src shl 1).toUByte()
op_storw(dst, res.toUShort())
N = res bit 7 N = res bit 7
Z = res == 0.toUByte() Z = res == 0.toUByte()
C = src bit 7 C = src bit 7
V = N xor C V = N xor C
op_storw(dst, res.toUShort())
} // ASLB } // ASLB
} }
} // RORB, ROLB, ASRB, ASLB } // RORB, ROLB, ASRB, ASLB
@@ -653,14 +658,17 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref // memory ref
core.getSpace(prv_mode).getw(src.toUShort(), dspace = true) core.getSpace(prv_mode).getw(src.toUShort(), dspace = true)
} }
stack_push(v)
N = v bit 15 N = v bit 15
Z = v == 0u.toUShort() Z = v == 0u.toUShort()
V = false V = false
stack_push(v)
} // MFPD // TODO } // MFPD // TODO
2 -> { 2 -> {
val v = stack_pop() val v = stack_pop()
val dest = opc_dst(opcode) val dest = opc_dst(opcode)
N = v bit 15
Z = v == 0u.toUShort()
V = false
if (is_paddr_reg(dest)) { if (is_paddr_reg(dest)) {
if (dest and 7u == 6u && prv_mode != cur_mode) { if (dest and 7u == 6u && prv_mode != cur_mode) {
shadow_r6[prv_mode] = v shadow_r6[prv_mode] = v
@@ -670,9 +678,6 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
// memory ref // memory ref
core.getSpace(prv_mode).setw(dest.toUShort(), v, dspace = true) core.getSpace(prv_mode).setw(dest.toUShort(), v, dspace = true)
} }
N = v bit 15
Z = v == 0u.toUShort()
V = false
} // MTPD // TODO } // MTPD // TODO
else -> { throw InvalidOpcodeException() } // Reserved else -> { throw InvalidOpcodeException() } // Reserved
} }
@@ -683,6 +688,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val dstv = op_loadb(src) val dstv = op_loadb(src)
N = dstv bit 7
Z = dstv == 0.toUByte()
V = false
if (is_paddr_reg(dst)) { if (is_paddr_reg(dst)) {
op_storw(dst, dstv.toUShort() sex 8) op_storw(dst, dstv.toUShort() sex 8)
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) op_storb(dst, dstv)
dstv.toUShort() dstv.toUShort()
} }
N = dstv bit 7
Z = dstv == 0.toUByte()
V = false
} // MOVB } // MOVB
for (i in 0xA0..0xAF) insnTable[i] = { opcode -> // CMPB 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 src = opc_srcb(opcode)
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val res = op_loadb(dst) and op_loadb(src).inv() val res = op_loadb(dst) and op_loadb(src).inv()
op_storb(dst, res)
N = res bit 7 N = res bit 7
Z = res == 0u.toUByte() Z = res == 0u.toUByte()
V = false V = false
op_storb(dst, res)
} // BICB } // BICB
for (i in 0xD0..0xDF) insnTable[i] = { opcode -> // BISB for (i in 0xD0..0xDF) insnTable[i] = { opcode -> // BISB
val src = opc_srcb(opcode) val src = opc_srcb(opcode)
val dst = opc_dstb(opcode) val dst = opc_dstb(opcode)
val res = op_loadb(dst) or op_loadb(src) val res = op_loadb(dst) or op_loadb(src)
op_storb(dst, res)
N = res bit 7 N = res bit 7
Z = res == 0u.toUByte() Z = res == 0u.toUByte()
V = false V = false
op_storb(dst, res)
} // BISB } // BISB
for (i in 0xE0..0xEF) insnTable[i] = { opcode -> for (i in 0xE0..0xEF) insnTable[i] = { opcode ->
val src = opc_src(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 srcv = op_loadw(src)
val dstv = op_loadw(dst) val dstv = op_loadw(dst)
val res = (dstv.toShort() - srcv.toShort()) val res = (dstv.toShort() - srcv.toShort())
op_storw(dst, res.toUShort())
N = res < 0 N = res < 0
Z = res == 0 Z = res == 0
V = ((srcv bit 31) xor (dstv bit 15)) and ((srcv bit 15) == (res bit 31)) V = ((srcv bit 31) xor (dstv bit 15)) and ((srcv bit 15) == (res bit 31))
C = (dst.toInt() + src.inv().inc().toInt()) < 0x1_0000 C = (dst.toInt() + src.inv().inc().toInt()) < 0x1_0000
op_storw(dst, res.toUShort())
} // SUB } // SUB
// insnTable[0x0E] = // TODO: check this // insnTable[0x0E] = // TODO: check this
// insnTable[0x0F] = // TODO: check this // insnTable[0x0F] = // TODO: check this
} }
} }
var switchReg: UShort = 0u
private var atBreakpoint: Boolean = false
private var allowT: Boolean = true private var allowT: Boolean = true
private var control_reg: UShort = 0u private var control_reg: UShort = 0u
val logger: Logger = LoggerFactory.getLogger(this.javaClass) val logger: Logger = LoggerFactory.getLogger(this.javaClass)
@@ -765,10 +772,11 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
private var stack_limit: UShort = 0u private var stack_limit: UShort = 0u
private var handlingStackRed: Boolean = false private var handlingStackRed: Boolean = false
private var trapReq: Int = 0 private var trapReq: Int = 0
var breakpoint: UShort = 1u
val core = PagingUnit(mbus, this) val core = PagingUnit(mbus, this)
var runState: RunState = RunState.HALTED var runState: RunState = RunState.HALTED
var psw: UShort val psw: UShort
get() { get() {
var res = 0 var res = 0
if (C) { res = res or 0x0001 } if (C) { res = res or 0x0001 }
@@ -781,29 +789,37 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
res = res or (registerSet shl 11) res = res or (registerSet shl 11)
return res.toUShort() return res.toUShort()
} }
set(value) {
var newpsw = value.toInt() fun setPSW(value: UShort, allowEsc: Boolean) {
C = value bit 0 var newpsw = value.toInt()
V = value bit 1 C = value bit 0
Z = value bit 2 V = value bit 1
N = value bit 3 Z = value bit 2
T = value bit 4 // TODO: handle suspended trap N = value bit 3
psw_priority = newpsw shr 5 and 7 T = value bit 4 // TODO: handle suspended trap
if (allowEsc) {
registerSet = newpsw shr 11 and 1 registerSet = newpsw shr 11 and 1
cur_mode = newpsw shr 14 cur_mode = newpsw shr 14
prv_mode = newpsw shr 12 and 3
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)
prv_mode = prv_mode or (newpsw shr 12 and 3)
} }
}
private var cur_mode: Int = 0 private var cur_mode: Int = 0
set(value) { set(value) {
prv_mode = field // prv_mode = field
shadow_r6[field] = sp shadow_r6[field] = sp
field = if (value == 2) 3 else value field = if (value == 2) 3 else value
sp = shadow_r6[field] sp = shadow_r6[field]
core.mode = value core.mode = field
} }
private var prv_mode: Int = 0 private var prv_mode: Int = 0
private var psw_priority: Int = 0 private var psw_priority: Int = 0
private var psw_priority_next: Int = 0
var pc: UShort var pc: UShort
get() = registers[7] get() = registers[7]
set(value) { set(value) {
@@ -817,6 +833,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
private var registerSet: Int = 0 private var registerSet: Int = 0
set(value) { set(value) {
if (value == field) return
if (value !in 0..1) { if (value !in 0..1) {
throw ProgrammerError("Invalid register set number") throw ProgrammerError("Invalid register set number")
} }
@@ -830,9 +847,12 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
} }
private var cc: UShort private var cc: UShort
get() = psw and 0xFu get() = 0.bit(3, N).bit(2, Z).bit(1, C).bit(0, C).toUShort()
set(value) { set(value) {
psw = (psw and 0xFFFu) or (value and 0xFu) N = value bit 3
Z = value bit 2
V = value bit 1
C = value bit 0
} }
private var pirq: UShort = 0u private var pirq: UShort = 0u
set(value) { set(value) {
@@ -966,17 +986,21 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
if (is_paddr_reg(spec)) { if (is_paddr_reg(spec)) {
// register // register
registers[addr.toInt()] = value registers[addr.toInt()] = value
} else if (addr and 1u != 0u) {
throw OddAddressError(addr)
} else { } else {
core.setw(addr.toUShort(), value, dspace) core.setw(addr.toUShort(), value, dspace)
} }
} }
private fun op_loadw(spec: UInt, dspace: Boolean=true): UShort { 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)) { val value = if (is_paddr_reg(spec)) {
registers[addr.toInt()] registers[addr.toInt()]
} else if (addr and 1u != 0u) {
throw OddAddressError(addr)
} else { } else {
core.getw(addr, dspace) core.getw(addr.toUShort(), dspace)
} }
if (spec bit PADDR_ARG_BIT) if (spec bit PADDR_ARG_BIT)
tracer.noteReference(spec, value) tracer.noteReference(spec, value)
@@ -1031,15 +1055,21 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
fun trapRed() { fun trapRed() {
// This is handled separately because otherwise the stack push // This is handled separately because otherwise the stack push
// would itself trigger a red trap // would itself trigger a red trap
logger.warn("Stack RED") logger.trace("Stack RED")
cpu_err = cpu_err or CPU_ERR_STK_RED cpu_err = cpu_err or CPU_ERR_STK_RED
val old_psw = psw val old_psw = psw
psw = core.getw(6u) setPSW(core.getSpace(0).getw(6u), true)
core.setw(2u, psw) if (cur_mode == 0) {
core.setw(0u, pc) sp = 4u
sp = 0u } 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() trapReq = trapReq and TrapReason.RED.clear.inv()
pc = core.getw(4u) pc = core.getSpace(0).getw(4u)
} }
fun step() { fun step() {
@@ -1048,9 +1078,9 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
try { try {
// Check early traps // Check early traps
if (trapReq != 0) { if (trapReq != 0) {
for (cause in TrapReason.entries.reversed()) { for (cause in TrapReason.entries.asReversed()) {
if (trapReq and cause.mask != 0) { if (trapReq and cause.mask != 0) {
// logger.warn("Trapping because $cause") logger.trace("Trapping because {}", cause)
try { try {
callVector(cause.vector) callVector(cause.vector)
} finally { } finally {
@@ -1075,7 +1105,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) mbus.unibus.logger.trace("DATIP: {} @ {}", vector.toOctal(), i)
callVector(vector) callVector(vector)
source.handled() source.handled()
break break
@@ -1098,8 +1128,10 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
} }
if (runState == RunState.WAIT_FOR_INTERRUPT) { 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 return
} }
psw_priority = psw_priority_next
// Proceed to handling instruction // Proceed to handling instruction
try { try {
@@ -1117,14 +1149,16 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
} }
is NonExistentMemoryError -> { is NonExistentMemoryError -> {
cpu_err = cpu_err or CPU_ERR_NXM cpu_err = cpu_err or CPU_ERR_NXM
logger.trace("NXM at {}", error.addr.toOctal())
setTrap(TrapReason.NXM) setTrap(TrapReason.NXM)
} }
is BusTimeoutError -> { is BusTimeoutError -> {
logger.trace("TMO at {}", error.addr.toOctal())
cpu_err = cpu_err or CPU_ERR_UNIBUS_TIMEOUT cpu_err = cpu_err or CPU_ERR_UNIBUS_TIMEOUT
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) {
@@ -1134,28 +1168,35 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
fun run(): Long { fun run(): Long {
var ninsn: Long = 0 var ninsn: Long = 0
while (runState == RunState.RUNNING) { atBreakpoint = false
while (runState == RunState.RUNNING && !atBreakpoint) {
ninsn += run(Long.MAX_VALUE) ninsn += run(Long.MAX_VALUE)
} }
return ninsn return ninsn
} }
fun run(nstep: Long): Long { fun run(nstep: Long): Long {
atBreakpoint = false
var ninsn: Long = 0 var ninsn: Long = 0
while (runState == RunState.RUNNING && ninsn < nstep) { while (runState == RunState.RUNNING && ninsn < nstep && !atBreakpoint) {
ninsn++ if (pc == breakpoint) {
if (pc == 0x344A.toUShort()) { atBreakpoint = true
pc = pc ninsn = ninsn // Dummy statement to set debugger bkpt on
// continue
}
if (!atBreakpoint) {
ninsn++
step()
} }
step()
} }
return ninsn return ninsn
} }
fun callVector(vector: UShort) { fun callVector(vector: UShort) {
val old_psw = psw val old_psw = psw
// update PSW first so that this gets pushed to the val newPSW = core.getSpace(0).getw((vector + 2u).toUShort()) and 0xCFFFu or (cur_mode shl 12).toUShort()
psw = core.getw((vector + 2u).toUShort()) setPSW(newPSW, true)
psw_priority = psw_priority_next // calling a vector sets priority immediately
stack_push(old_psw) stack_push(old_psw)
stack_push(pc) stack_push(pc)
pc = core.getw(vector) pc = core.getw(vector)
@@ -1167,7 +1208,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
private inner class Registers: PAddressSpace { private inner class Registers: PAddressSpace {
override fun getw(addr: UInt): UShort = when (addr) { override fun getw(addr: UInt): UShort = when (addr) {
0x3FF78u -> 0x70u // Console switch/display 0x3FF78u -> switchReg // Console switch/display
0x3FFE6u -> control_reg 0x3FFE6u -> control_reg
0x3FFF0u -> (mbus.size shr 6).toUShort() 0x3FFF0u -> (mbus.size shr 6).toUShort()
0x3FFF2u -> 0u // upper size 0x3FFF2u -> 0u // upper size
@@ -1181,14 +1222,18 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
override fun setw(addr: UInt, value: UShort) = when (addr) { override fun setw(addr: UInt, value: UShort) = when (addr) {
0x3_FF78u -> { 0x3_FF78u -> {
System.err.print("\u001b]0;${value.toOctal()}\u0007") // System.err.print("\u001b]0;${value.toOctal()}\u0007")
} // console switch/display reg } // console switch/display reg
0x3_FFE6u -> control_reg = value 0x3_FFE6u -> control_reg = value
0x3_FFF0u , 0x3_FFF2u , 0x3_FFF4u, 0x3_FFF8u -> {} // read-only registers 0x3_FFF0u , 0x3_FFF2u , 0x3_FFF4u, 0x3_FFF8u -> {} // read-only registers
0x3_FFF6u -> cpu_err = value 0x3_FFF6u -> cpu_err = value
0x3_FFFAu -> pirq = value 0x3_FFFAu -> pirq = value
0x3_FFFCu -> stack_limit = value and 0xFF00u // stack limit 0x3_FFFCu -> stack_limit = value and 0xFF00u // stack limit
0x3_FFFEu -> psw = value or (cur_mode.toUShort() shl 14) // writing to PSW can only increase current mode 0x3_FFFEu -> {
setPSW(value, true)
// explicitly switch RS if necessary
// registerSet = (value shr 11 and 1u).toInt()
} // writing to PSW can only increase current mode
else -> { else -> {
println("Bus error at ${addr.toString(16)}: ${value.toString(16)}") println("Bus error at ${addr.toString(16)}: ${value.toString(16)}")
throw BusTimeoutError(addr) throw BusTimeoutError(addr)
@@ -1198,11 +1243,11 @@ 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 || sp.toInt() and 0xFFFE == 0xFFFE) { if (addr < stack_limit + 224u || addr.toInt() and 0xFFFE == 0xFFFE) {
trapRed() trapRed()
throw EndCycle() throw EndCycle()
} else if (sp < stack_limit + 256u) { } else if (addr < stack_limit + 256u) {
logger.warn("Stack YLW") // logger.warn("Stack YLW")
// 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(TrapReason.YEL)
@@ -1217,7 +1262,7 @@ class CPU(val mbus: MemBus, var tracer: ITracer = NullTracer()) {
enum class RunState(val handleInterrupt: Boolean) { enum class RunState(val handleInterrupt: Boolean) {
RUNNING(handleInterrupt = true), RUNNING(handleInterrupt = true),
HALTED(handleInterrupt = false), HALTED(handleInterrupt = false),
WAIT_FOR_INTERRUPT(handleInterrupt = true) WAIT_FOR_INTERRUPT(handleInterrupt = true),
} }
} }

View File

@@ -1,6 +1,11 @@
package com.thequux.mcpdp.core 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 BusTimeoutError(addr: UInt): MemoryError(MemoryErrorType.BusTimeout, addr)
class OddAddressError(addr: UInt): MemoryError(MemoryErrorType.OddAddress, addr) class OddAddressError(addr: UInt): MemoryError(MemoryErrorType.OddAddress, addr)
class NonExistentMemoryError(addr: UInt): MemoryError(MemoryErrorType.NonExistent, addr) class NonExistentMemoryError(addr: UInt): MemoryError(MemoryErrorType.NonExistent, addr)

View File

@@ -4,7 +4,10 @@ package com.thequux.mcpdp.core
import com.thequux.mcpdp.ext.bit.* import com.thequux.mcpdp.ext.bit.*
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 org.slf4j.Logger
import org.slf4j.LoggerFactory
import kotlin.math.max import kotlin.math.max
import kotlin.math.min
enum class AccessAction { enum class AccessAction {
Allow, Allow,
@@ -34,7 +37,7 @@ private data class PDR(val plf: UShort, var A: Boolean, var W: Boolean, var ed:
get() = if (ed) { get() = if (ed) {
(plf.toUInt() shl 6) ..< 0x2000u (plf.toUInt() shl 6) ..< 0x2000u
} else { } else {
0U..< (plf.toUInt() shl 6) 0U.. (plf.toUInt() shl 6+1) // +1 allows the last byte address
} }
val asU16: UShort val asU16: UShort
@@ -59,6 +62,7 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
private val MEMORY_ERROR_REG: UInt = 0x3FFE4u private val MEMORY_ERROR_REG: UInt = 0x3FFE4u
} }
private val logger = LoggerFactory.getLogger(this.javaClass)
private var mmr0: UShort = 0u private var mmr0: UShort = 0u
set(value) { set(value) {
@@ -77,8 +81,8 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
val unibusMap: PAddressSpace = UnibusMap() val unibusMap: PAddressSpace = UnibusMap()
private inner class PageTable(val mode: Int, val dspace: Boolean) { private inner class PageTable(val mode: Int, val dspace: Boolean) {
val par = UShortArray(16) { 0U } val par = UShortArray(8) { 0U }
val pdr = Array(16) { PDR() } val pdr = Array(8) { PDR() }
fun setMmr0(causeBit: Int, apf: Int, completed: Boolean = false) { fun setMmr0(causeBit: Int, apf: Int, completed: Boolean = false) {
@@ -104,7 +108,17 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
// check range // check range
if (addr and 0x1FFFU !in pdr.range) { if (addr and 0x1FFFU !in pdr.range) {
setMmr0(14, apf) 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) cpu.setTrap(TrapReason.MME)
// TODO: check whether this is always an abort // TODO: check whether this is always an abort
throw EndCycle() throw EndCycle()
@@ -113,15 +127,19 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
AccessAction.Allow -> {} AccessAction.Allow -> {}
AccessAction.Trap -> { AccessAction.Trap -> {
setMmr0(12, apf) setMmr0(12, apf)
if (logger.isTraceEnabled)
logger.trace("MME: ${if (write) "write" else "read"} trap")
cpu.setTrap(TrapReason.MME) cpu.setTrap(TrapReason.MME)
} }
AccessAction.Abort -> { AccessAction.Abort -> {
setMmr0(13, apf) setMmr0(13, apf)
cpu.setTrap(TrapReason.MME) cpu.setTrap(TrapReason.MME)
if (logger.isTraceEnabled)
logger.trace("MME: ${if (write) "write" else "read"} ABORT")
throw EndCycle() throw EndCycle()
} }
} }
val tmp1 = addr.toUInt() + (par.toUInt() shl 6) and mapMode.addrMask val tmp1 = addr.toUInt().and(0x1FFFu) + (par.toUInt() shl 6) and mapMode.addrMask
if (tmp1 and mapMode.hipageMask == mapMode.hipageMask) { if (tmp1 and mapMode.hipageMask == mapMode.hipageMask) {
return tmp1 or 0x3C_0000u return tmp1 or 0x3C_0000u
} else { } else {
@@ -142,10 +160,10 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
enable22bit -> MManMode.MM_22 enable22bit -> MManMode.MM_22
else -> MManMode.MM_18 else -> MManMode.MM_18
} }
modeSpace = if (mapMode.mmanEnable) modeVTabs[max(mode, 2)] else noMmanSpace 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) { fun logIncrement(register: Int, amount: Int) {
assert(register in 0..7) assert(register in 0..7)
@@ -162,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 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(charArrayOf('K', 'S', 'U')[it], itabs[it], dtabs[it], false) }
private var noMmanSpace = NoMmanSpace() private var noMmanSpace = NoMmanSpace()
var modeSpace: VAddressSpace = noMmanSpace var modeSpace: VAddressSpace = noMmanSpace
private set private set
@@ -171,18 +189,25 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
private val unibusTable = UIntArray(32) 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 {
private fun getSpace(dspace: Boolean): PageTable = if (dspace) dSpace else iSpace 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 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) { override fun setw(addr: UShort, value: UShort, dspace: Boolean) {
pspace.setw(getSpace(dspace).map(addr, write = true), value) pspace.setw(getSpace(dspace).map(addr, write = true), value)
} }
override fun setb(addr: UShort, value: UByte) { override fun setb(addr: UShort, value: UByte) {
pspace.setb(dSpace.map(addr, write = true), value) pspace.setb(getSpace(true).map(addr, write = true), value)
} }
} }
@@ -216,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) } 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 { override fun getw(addr: UInt): UShort {
val mode = addr.toInt() shr 4 and 3 val mode = addr.toInt() shr 4 and 3
val reg = addr.toInt() shr 1 and 7 val reg = addr.toInt() shr 1 and 7
@@ -232,6 +257,17 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
override fun setw(addr: UInt, value: UShort) { override fun setw(addr: UInt, value: UShort) {
val mode = addr.toInt() shr 4 and 3 val mode = addr.toInt() shr 4 and 3
val reg = addr.toInt() shr 1 and 7 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) { when (mode) {
0 -> ipt.pdr[reg] = PDR(value) // This clears A and W 0 -> ipt.pdr[reg] = PDR(value) // This clears A and W
1 -> dpt.pdr[reg] = PDR(value) // This clears A and W 1 -> dpt.pdr[reg] = PDR(value) // This clears A and W
@@ -313,8 +349,8 @@ class PagingUnit(val pspace: PAddressSpace, val cpu: CPU): VAddressSpace {
val regs = ConfigRegisters() val regs = ConfigRegisters()
unibus.run { unibus.run {
deviceView = UnibusMap() deviceView = UnibusMap()
attach(0x3_F480u, 6, PdPair(itabs[0], dtabs[0])) attach(0x3_F480u, 6, PdPair(itabs[1], dtabs[1]))
attach(0x3_F4C0u, 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_FF80u, 6, PdPair(itabs[2], dtabs[2])) // User PAR/PDR
attach(0x3_FF7Au, 1, regs) // MMR0 attach(0x3_FF7Au, 1, regs) // MMR0
attach(0x3_FF7Cu, 2, regs) // MMR1-2 attach(0x3_FF7Cu, 2, regs) // MMR1-2

View File

@@ -86,7 +86,11 @@ class Disassembler {
fun dasm_at(loc: UShort, istream: UShortArray): String { fun dasm_at(loc: UShort, istream: UShortArray): String {
opc = loc opc = loc
this.istream = istream this.istream = istream
opcode = istream[0].toInt() try {
opcode = istream[0].toInt()
} catch(_: IndexOutOfBoundsException) {
return fmt("????")
}
vpc = 1 vpc = 1
try { try {

View File

@@ -0,0 +1,26 @@
package com.thequux.mcpdp.debug
import java.io.PrintStream
import java.io.Writer
@OptIn(ExperimentalUnsignedTypes::class)
class FlightRecorder(size: Int): Collector {
var trace: Array<TraceRecord?> = Array(size) { null }
var pos = 0
override fun invoke(record: TraceRecord) {
trace[pos++] = record
if (pos >= trace.size) pos -= trace.size
}
fun dump(os: PrintStream) {
val dis = Disassembler()
for (i in trace.indices) {
val i0 = (i + pos) % trace.size
val record = trace[i0]
if (record != null) {
os.println(record.report(dis))
}
}
}
}

View File

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

View File

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