Added new trace infrastructure
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
import ch.qos.logback.classic.LoggerContext
|
import ch.qos.logback.classic.LoggerContext
|
||||||
import com.thequux.mcpdp.core.CPU
|
import com.thequux.mcpdp.core.CPU
|
||||||
|
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.loadAbs
|
||||||
import com.thequux.mcpdp.peripheral.DL11
|
import com.thequux.mcpdp.peripheral.DL11
|
||||||
import com.thequux.mcpdp.peripheral.MemBus
|
import com.thequux.mcpdp.peripheral.MemBus
|
||||||
@@ -10,10 +13,14 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
val tb = org.jline.terminal.TerminalBuilder.terminal()
|
val tb = org.jline.terminal.TerminalBuilder.terminal()
|
||||||
var mbus = MemBus(32768)
|
var mbus = MemBus(32768)
|
||||||
var cpu = CPU(mbus)
|
val tracer = Tracer()
|
||||||
|
val loggingCollector = LoggingCollector()
|
||||||
|
tracer.addCollector(loggingCollector)
|
||||||
|
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 {
|
||||||
|
|
||||||
|
|
||||||
tb.enterRawMode()
|
tb.enterRawMode()
|
||||||
console.start()
|
console.start()
|
||||||
|
|
||||||
@@ -21,7 +28,7 @@ fun main(args: Array<String>) {
|
|||||||
cpu.runState = CPU.RunState.RUNNING
|
cpu.runState = CPU.RunState.RUNNING
|
||||||
cpu.pc = 0x80u
|
cpu.pc = 0x80u
|
||||||
val start = System.nanoTime()
|
val start = System.nanoTime()
|
||||||
val ninsn = cpu.run(200000000)
|
val ninsn = cpu.run(600000000)
|
||||||
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)}")
|
||||||
|
@@ -2,7 +2,9 @@ package com.thequux.mcpdp.core
|
|||||||
|
|
||||||
import com.thequux.mcpdp.ext.bit.*
|
import com.thequux.mcpdp.ext.bit.*
|
||||||
import com.thequux.mcpdp.peripheral.MemBus
|
import com.thequux.mcpdp.peripheral.MemBus
|
||||||
import com.thequux.mcpdp.util.Disassembler
|
import com.thequux.mcpdp.debug.Disassembler
|
||||||
|
import com.thequux.mcpdp.debug.ITracer
|
||||||
|
import com.thequux.mcpdp.debug.NullTracer
|
||||||
import com.thequux.mcpdp.util.ProgrammerError
|
import com.thequux.mcpdp.util.ProgrammerError
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -15,9 +17,14 @@ import java.lang.StringBuilder
|
|||||||
/// Exxx: ROM
|
/// Exxx: ROM
|
||||||
/// xxxx: rest
|
/// xxxx: rest
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
class CPU(val mbus: MemBus) {
|
class CPU(val mbus: MemBus, val tracer: ITracer = NullTracer()) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
val PADDR_REG_BIT: Int = 23
|
||||||
|
val PADDR_ISPACE_BIT: Int = 22
|
||||||
|
val PADDR_DST_BIT: Int = 24
|
||||||
|
val PADDR_ARG_BIT: Int = 25
|
||||||
|
|
||||||
val debugMode = System.getProperty("jdp11.itrace").toBoolean()
|
val debugMode = System.getProperty("jdp11.itrace").toBoolean()
|
||||||
|
|
||||||
private val insnTable: Array<CPU.(Int) -> Unit> = Array(256) {{throw InvalidOpcodeException()}}
|
private val insnTable: Array<CPU.(Int) -> Unit> = Array(256) {{throw InvalidOpcodeException()}}
|
||||||
@@ -536,9 +543,9 @@ class CPU(val mbus: MemBus) {
|
|||||||
for (i in 0x90..0x9F) insnTable[i] = { opcode -> // MOVB
|
for (i in 0x90..0x9F) insnTable[i] = { opcode -> // MOVB
|
||||||
val src = opc_srcb(opcode)
|
val src = opc_srcb(opcode)
|
||||||
val dst = opc_dstb(opcode)
|
val dst = opc_dstb(opcode)
|
||||||
var dstv = op_loadb(src)
|
val dstv = op_loadb(src)
|
||||||
|
|
||||||
var dstw = 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
|
||||||
} else {
|
} else {
|
||||||
@@ -595,14 +602,13 @@ class CPU(val mbus: MemBus) {
|
|||||||
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
|
||||||
} // SUB
|
} // SUB
|
||||||
// insnTable[0x0E] = // TODO: check this
|
// insnTable[0x0E] = // TODO: check this
|
||||||
// insnTable[0x0F] = // TODO: check this
|
// insnTable[0x0F] = // TODO: check this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
private val flagStr: String
|
private val flagStr: String
|
||||||
@@ -618,7 +624,6 @@ class CPU(val mbus: MemBus) {
|
|||||||
private val general_registers = Array<UShortArray>(2) { UShortArray(6) }
|
private val general_registers = Array<UShortArray>(2) { UShortArray(6) }
|
||||||
private val shadow_r6 = UShortArray(4) //
|
private val shadow_r6 = UShortArray(4) //
|
||||||
val core = PagingUnit(mbus)
|
val core = PagingUnit(mbus)
|
||||||
private val dasm = Disassembler(core)
|
|
||||||
|
|
||||||
var runState: RunState = RunState.HALTED
|
var runState: RunState = RunState.HALTED
|
||||||
var psw: UShort
|
var psw: UShort
|
||||||
@@ -662,7 +667,7 @@ class CPU(val mbus: MemBus) {
|
|||||||
set(value) {
|
set(value) {
|
||||||
registers[7] = value
|
registers[7] = value
|
||||||
}
|
}
|
||||||
private var sp: UShort
|
var sp: UShort
|
||||||
get() = registers[6]
|
get() = registers[6]
|
||||||
set(value) {
|
set(value) {
|
||||||
registers[6] = value
|
registers[6] = value
|
||||||
@@ -708,8 +713,6 @@ class CPU(val mbus: MemBus) {
|
|||||||
private var Z: Boolean = false
|
private var Z: Boolean = false
|
||||||
private var V: Boolean = false
|
private var V: Boolean = false
|
||||||
private var T: Boolean = false
|
private var T: Boolean = false
|
||||||
private val PADDR_REG_BIT: Int = 23
|
|
||||||
private val PADDR_ISPACE_BIT: Int = 22
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val regs = Registers()
|
val regs = Registers()
|
||||||
@@ -724,21 +727,23 @@ class CPU(val mbus: MemBus) {
|
|||||||
|
|
||||||
private fun is_paddr_reg(addr: UInt): Boolean = addr bit PADDR_REG_BIT
|
private fun is_paddr_reg(addr: UInt): Boolean = addr bit PADDR_REG_BIT
|
||||||
private fun is_paddr_ispace(addr: UInt): Boolean = addr bit PADDR_ISPACE_BIT
|
private fun is_paddr_ispace(addr: UInt): Boolean = addr bit PADDR_ISPACE_BIT
|
||||||
private fun op_resolve(operand: Int, byte_mode: Boolean = false): UInt {
|
private fun op_resolve(operand: Int, src: Boolean, byte_mode: Boolean = false): UInt {
|
||||||
val mode = operand shr 3
|
val mode = operand shr 3
|
||||||
val reg = operand and 0x7
|
val reg = operand and 0x7
|
||||||
val is_pc = reg == 7
|
val is_pc = reg == 7
|
||||||
val increment = if (byte_mode && !is_pc) 1U else 2U
|
val increment = if (byte_mode && !is_pc) 1U else 2U
|
||||||
return 0x80_FFFFu and when (mode and 0x7) {
|
val decoded = 0x80_FFFFu and when (mode and 0x7) {
|
||||||
0 -> reg.toUInt() bis PADDR_REG_BIT
|
0 -> reg.toUInt() bis PADDR_REG_BIT
|
||||||
1 -> registers[reg].toUInt()
|
1 -> registers[reg].toUInt()
|
||||||
2 -> {
|
2 -> {
|
||||||
val addr = registers[reg]
|
val addr = registers[reg]
|
||||||
|
if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false))
|
||||||
registers[reg] = (addr + increment).toUShort()
|
registers[reg] = (addr + increment).toUShort()
|
||||||
addr.toUInt().bit(PADDR_ISPACE_BIT, is_pc)
|
addr.toUInt().bit(PADDR_ISPACE_BIT, is_pc)
|
||||||
}
|
}
|
||||||
3 -> {
|
3 -> {
|
||||||
val addr = registers[reg]
|
val addr = registers[reg]
|
||||||
|
if (reg == 7) tracer.noteArgument(op_loadw(addr.toUInt() bis PADDR_ISPACE_BIT, false))
|
||||||
registers[reg] = (addr + 2U).toUShort()
|
registers[reg] = (addr + 2U).toUShort()
|
||||||
core.getw(addr, dspace = !is_pc).toUInt()
|
core.getw(addr, dspace = !is_pc).toUInt()
|
||||||
}
|
}
|
||||||
@@ -752,16 +757,19 @@ class CPU(val mbus: MemBus) {
|
|||||||
}
|
}
|
||||||
6 -> {
|
6 -> {
|
||||||
val idx = core.getw(pc, dspace = false)
|
val idx = core.getw(pc, dspace = false)
|
||||||
|
tracer.noteArgument(idx)
|
||||||
pc = (pc + 2u).toUShort()
|
pc = (pc + 2u).toUShort()
|
||||||
(idx + registers[reg]) and 0xFFFFu
|
(idx + registers[reg]) and 0xFFFFu
|
||||||
}
|
}
|
||||||
7 -> {
|
7 -> {
|
||||||
val idx = core.getw(pc, dspace = false)
|
val idx = core.getw(pc, dspace = false)
|
||||||
|
tracer.noteArgument(idx)
|
||||||
pc = (pc+2u).toUShort()
|
pc = (pc+2u).toUShort()
|
||||||
core.getw((idx+registers[reg]).toUShort()).toUInt()
|
core.getw((idx+registers[reg]).toUShort()).toUInt()
|
||||||
}
|
}
|
||||||
else -> throw InvalidOpcodeException() // unreachable
|
else -> throw InvalidOpcodeException() // unreachable
|
||||||
}
|
}
|
||||||
|
return decoded.bit(PADDR_DST_BIT, !src) bis PADDR_ARG_BIT
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun op_storb(spec: UInt, value: UByte) {
|
private fun op_storb(spec: UInt, value: UByte) {
|
||||||
@@ -776,11 +784,13 @@ class CPU(val mbus: MemBus) {
|
|||||||
|
|
||||||
private fun op_loadb(spec: UInt): UByte {
|
private fun op_loadb(spec: UInt): UByte {
|
||||||
val addr = (spec and 0xFFFFu).toUShort()
|
val addr = (spec and 0xFFFFu).toUShort()
|
||||||
return if (is_paddr_reg(spec)) {
|
val value = if (is_paddr_reg(spec)) {
|
||||||
(registers[addr.toInt()] and 0xFFu).toUByte()
|
(registers[addr.toInt()] and 0xFFu).toUByte()
|
||||||
} else {
|
} else {
|
||||||
core.getb(addr)
|
core.getb(addr)
|
||||||
}
|
}
|
||||||
|
tracer.noteReference(spec, value.toUShort())
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun op_storw(spec: UInt, value: UShort, dspace: Boolean = true) {
|
private fun op_storw(spec: UInt, value: UShort, dspace: Boolean = true) {
|
||||||
@@ -795,17 +805,20 @@ class CPU(val mbus: MemBus) {
|
|||||||
|
|
||||||
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).toUShort()
|
||||||
return if (is_paddr_reg(spec)) {
|
val value = if (is_paddr_reg(spec)) {
|
||||||
registers[addr.toInt()]
|
registers[addr.toInt()]
|
||||||
} else {
|
} else {
|
||||||
core.getw(addr, dspace)
|
core.getw(addr, dspace)
|
||||||
}
|
}
|
||||||
|
if (spec bit PADDR_ARG_BIT)
|
||||||
|
tracer.noteReference(spec, value)
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun opc_dst(opcode: Int): UInt = op_resolve(opcode and 0x3F)
|
private fun opc_dst(opcode: Int): UInt = op_resolve(opcode and 0x3F, false)
|
||||||
private fun opc_dstb(opcode: Int): UInt = op_resolve(opcode and 0x3F, byte_mode = true)
|
private fun opc_dstb(opcode: Int): UInt = op_resolve(opcode and 0x3F, false, byte_mode = true)
|
||||||
private fun opc_src(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F)
|
private fun opc_src(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F, true)
|
||||||
private fun opc_srcb(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F, byte_mode = true)
|
private fun opc_srcb(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F, true, byte_mode = true)
|
||||||
|
|
||||||
private fun stack_pop(): UShort = core.getw(sp).also { sp = (sp + 2u).toUShort() }
|
private fun stack_pop(): UShort = core.getw(sp).also { sp = (sp + 2u).toUShort() }
|
||||||
private fun stack_push(value: UShort) {
|
private fun stack_push(value: UShort) {
|
||||||
@@ -821,12 +834,15 @@ class CPU(val mbus: MemBus) {
|
|||||||
|
|
||||||
/// Internal step; evaluates one opcode, but does not handle errors, interrupts, or updating system registers
|
/// Internal step; evaluates one opcode, but does not handle errors, interrupts, or updating system registers
|
||||||
fun step_int() {
|
fun step_int() {
|
||||||
if (debugMode) {
|
try {
|
||||||
logger.debug("${pc.toString(8).padStart(6, '0')}: ${dasm.dasm_at(pc)}")
|
tracer.noteBegin(this)
|
||||||
|
val opcode = core.getw(pc).toInt()
|
||||||
|
tracer.noteOpcode(opcode.toUShort())
|
||||||
|
pc = (pc + 2u).toUShort()
|
||||||
|
this.(insnTable[opcode shr 8])(opcode)
|
||||||
|
} finally {
|
||||||
|
tracer.noteEnd()
|
||||||
}
|
}
|
||||||
val opcode = core.getw(pc).toInt()
|
|
||||||
pc = (pc + 2u).toUShort()
|
|
||||||
this.(insnTable[opcode shr 8])(opcode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun debugFlags() {
|
private fun debugFlags() {
|
||||||
@@ -907,7 +923,7 @@ class CPU(val mbus: MemBus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun trap(vector: UShort) {
|
fun trap(vector: UShort) {
|
||||||
logger.info("Trap to {}", vector.toString(8))
|
// logger.info("Trap to {}", vector.toString(8))
|
||||||
val old_psw = psw
|
val old_psw = psw
|
||||||
// update PSW first so that this gets pushed to the
|
// update PSW first so that this gets pushed to the
|
||||||
psw = core.getw((vector + 2u).toUShort())
|
psw = core.getw((vector + 2u).toUShort())
|
||||||
|
@@ -1,37 +1,28 @@
|
|||||||
package com.thequux.mcpdp.util
|
package com.thequux.mcpdp.debug
|
||||||
|
|
||||||
import com.thequux.mcpdp.core.CPU
|
|
||||||
import com.thequux.mcpdp.core.InvalidOpcodeException
|
import com.thequux.mcpdp.core.InvalidOpcodeException
|
||||||
import com.thequux.mcpdp.core.VAddressSpace
|
import com.thequux.mcpdp.ext.bit.bit
|
||||||
import com.thequux.mcpdp.ext.bit.*
|
|
||||||
|
|
||||||
object DebugTools {
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
|
class Disassembler {
|
||||||
|
|
||||||
|
var istream: UShortArray = UShortArray(0)
|
||||||
}
|
var vpc: Int = 0
|
||||||
|
|
||||||
class NullDisassembler() {
|
|
||||||
fun dasm_at(loc: UShort): String = ""
|
|
||||||
}
|
|
||||||
class Disassembler(val core: VAddressSpace) {
|
|
||||||
|
|
||||||
var vpc: UShort = 0u
|
|
||||||
var opc: UShort = 0u
|
var opc: UShort = 0u
|
||||||
var opcode: Int = 0
|
var opcode: Int = 0
|
||||||
|
|
||||||
private fun fmt(opcode: String, vararg args: String): String {
|
private fun fmt(opcode: String, vararg args: String): String {
|
||||||
val res = StringBuilder()
|
val res = StringBuilder()
|
||||||
|
|
||||||
var tpc = opc
|
|
||||||
var bytes = 0
|
var bytes = 0
|
||||||
while (tpc != vpc) {
|
for (word in istream) {
|
||||||
|
if (bytes != 0)
|
||||||
|
res.append('.')
|
||||||
bytes += 2
|
bytes += 2
|
||||||
res.append(core.getw(tpc, dspace = false).toString(16).padStart(4, '0'))
|
res.append(word.toString(8).padStart(6, '0'))
|
||||||
res.append('.')
|
|
||||||
tpc = tpc.inc().inc()
|
|
||||||
}
|
}
|
||||||
while (bytes < 6) {
|
while (bytes < 6) {
|
||||||
res.append("----.")
|
res.append(".------")
|
||||||
bytes += 2
|
bytes += 2
|
||||||
}
|
}
|
||||||
res.append('\t')
|
res.append('\t')
|
||||||
@@ -56,11 +47,7 @@ class Disassembler(val core: VAddressSpace) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getw(): UShort {
|
private fun getw(): UShort = istream[vpc++]
|
||||||
val ret = core.getw(vpc, dspace = false)
|
|
||||||
vpc = vpc.inc().inc()
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
private fun arg_at(offset: Int): String {
|
private fun arg_at(offset: Int): String {
|
||||||
val rv = opcode shr offset and 0x3F
|
val rv = opcode shr offset and 0x3F
|
||||||
val reg = rv and 0x7
|
val reg = rv and 0x7
|
||||||
@@ -72,7 +59,7 @@ class Disassembler(val core: VAddressSpace) {
|
|||||||
2 -> if (reg == 7) "$ind#${getw().toString(8)}" else "$ind($rn)+"
|
2 -> if (reg == 7) "$ind#${getw().toString(8)}" else "$ind($rn)+"
|
||||||
4 -> "$ind-($rn)"
|
4 -> "$ind-($rn)"
|
||||||
6 -> if (reg == 7) {
|
6 -> if (reg == 7) {
|
||||||
ind + (getw() + vpc).toShort().toString(8)
|
ind + (getw() + opc + (vpc * 2).toUInt()).toShort().toString(8)
|
||||||
} else {
|
} else {
|
||||||
"$ind${getw().toString(8)}($rn)"
|
"$ind${getw().toString(8)}($rn)"
|
||||||
}
|
}
|
||||||
@@ -86,22 +73,22 @@ class Disassembler(val core: VAddressSpace) {
|
|||||||
|
|
||||||
private fun br_rel(opc: String): String {
|
private fun br_rel(opc: String): String {
|
||||||
val rel = (opcode and 0xFF) - (opcode and 0x80 shl 1)
|
val rel = (opcode and 0xFF) - (opcode and 0x80 shl 1)
|
||||||
val dst = (vpc.toInt() + rel.shl(1)).toUShort()
|
val dst = (this.opc.toInt() + (vpc * 2) + rel.shl(1)).toUShort()
|
||||||
return fmt(opc, dst.toString(8))
|
return fmt(opc, dst.toString(8))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rel6(): String {
|
private fun rel6(): String {
|
||||||
val rel = (opcode and 0x3F) - (opcode and 0x20 shl 1)
|
val rel = (opcode and 0x3F) - (opcode and 0x20 shl 1)
|
||||||
val dst = vpc.toInt() + rel
|
val dst = opc.toInt() + vpc*2 + rel
|
||||||
return dst.toUShort().toString(8)
|
return dst.toUShort().toString(8)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dasm_at(loc: UShort): String {
|
fun dasm_at(loc: UShort, istream: UShortArray): String {
|
||||||
vpc = loc
|
|
||||||
opc = loc
|
opc = loc
|
||||||
|
this.istream = istream
|
||||||
|
opcode = istream[0].toInt()
|
||||||
|
|
||||||
opcode = core.getw(vpc, dspace = false).toInt()
|
vpc = 1
|
||||||
vpc = (vpc + 2u).toUShort()
|
|
||||||
try {
|
try {
|
||||||
return when (opcode and 0xF000) {
|
return when (opcode and 0xF000) {
|
||||||
0x0, 0x8000 -> when (opcode and 0xFF00) {
|
0x0, 0x8000 -> when (opcode and 0xFF00) {
|
||||||
@@ -225,7 +212,8 @@ class Disassembler(val core: VAddressSpace) {
|
|||||||
2 -> {
|
2 -> {
|
||||||
throw InvalidOpcodeException()
|
throw InvalidOpcodeException()
|
||||||
} // MTPD // TODO
|
} // MTPD // TODO
|
||||||
else -> throw InvalidOpcodeException()// Reserved
|
else -> throw InvalidOpcodeException()
|
||||||
|
// Reserved
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw InvalidOpcodeException()
|
else -> throw InvalidOpcodeException()
|
15
src/main/kotlin/com/thequux/mcpdp/debug/LoggingCollector.kt
Normal file
15
src/main/kotlin/com/thequux/mcpdp/debug/LoggingCollector.kt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package com.thequux.mcpdp.debug
|
||||||
|
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
class LoggingCollector(val logger: Logger = LoggerFactory.getLogger("ITrace")): Collector {
|
||||||
|
|
||||||
|
init {
|
||||||
|
logger.debug("PC SP PSW |SRC DST |ISTREAM INSN")
|
||||||
|
}
|
||||||
|
private val disassembler = Disassembler()
|
||||||
|
override fun invoke(record: TraceRecord) {
|
||||||
|
logger.debug(record.report(disassembler))
|
||||||
|
}
|
||||||
|
}
|
36
src/main/kotlin/com/thequux/mcpdp/debug/TraceRecord.kt
Normal file
36
src/main/kotlin/com/thequux/mcpdp/debug/TraceRecord.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
@file:OptIn(ExperimentalUnsignedTypes::class)
|
||||||
|
|
||||||
|
package com.thequux.mcpdp.debug
|
||||||
|
|
||||||
|
import com.thequux.mcpdp.ext.bit.toOctal
|
||||||
|
|
||||||
|
data class TraceRecord(
|
||||||
|
val pc: UShort,
|
||||||
|
val sp: UShort,
|
||||||
|
val psw: UShort,
|
||||||
|
val opc: UShort,
|
||||||
|
val args: UShortArray,
|
||||||
|
val src: Pair<UInt, UShort>? = null,
|
||||||
|
val dst: Pair<UInt, UShort>? = null,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
|
companion object {
|
||||||
|
fun default(): TraceRecord = TraceRecord(0u, 0u, 0u, 0u, UShortArray(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatRef(ref: Pair<UInt, UShort>?): String {
|
||||||
|
return if (ref == null) {
|
||||||
|
" ".repeat(15)
|
||||||
|
} else {
|
||||||
|
"${ref.second.toOctal()}@${ref.first.toOctal(8)}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun report(disassembler: Disassembler = Disassembler()): String {
|
||||||
|
return "${pc.toOctal()} ${sp.toOctal()} ${psw.toOctal()}|" +
|
||||||
|
"${formatRef(src)} ${formatRef(dst)}|" +
|
||||||
|
disassembler.dasm_at(pc, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
101
src/main/kotlin/com/thequux/mcpdp/debug/Tracer.kt
Normal file
101
src/main/kotlin/com/thequux/mcpdp/debug/Tracer.kt
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
@file:OptIn(ExperimentalUnsignedTypes::class)
|
||||||
|
|
||||||
|
package com.thequux.mcpdp.debug
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.LoggerContext
|
||||||
|
import com.thequux.mcpdp.core.CPU
|
||||||
|
import com.thequux.mcpdp.ext.bit.bit
|
||||||
|
import com.thequux.mcpdp.ext.bit.toOctal
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.util.Arrays
|
||||||
|
|
||||||
|
typealias Collector = (TraceRecord) -> Unit
|
||||||
|
|
||||||
|
interface ITracer {
|
||||||
|
fun noteBegin(cpu: CPU)
|
||||||
|
fun noteOpcode(opcode: UShort)
|
||||||
|
fun noteArgument(arg: UShort)
|
||||||
|
fun noteEnd()
|
||||||
|
abstract fun noteReference(spec: UInt, value: UShort)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation of Tracer that does nothing, without need for a null check.
|
||||||
|
/// This should get JITed out, so should have no runtime overhead
|
||||||
|
class NullTracer: ITracer {
|
||||||
|
override fun noteBegin(cpu: CPU) {}
|
||||||
|
|
||||||
|
override fun noteOpcode(opcode: UShort) {}
|
||||||
|
|
||||||
|
override fun noteArgument(arg: UShort) {}
|
||||||
|
|
||||||
|
override fun noteReference(spec: UInt, value: UShort) {}
|
||||||
|
|
||||||
|
override fun noteEnd() {}
|
||||||
|
}
|
||||||
|
class Tracer : ITracer {
|
||||||
|
private val logger = LoggerFactory.getLogger(this.javaClass)
|
||||||
|
private var src: Pair<UInt, UShort>? = null
|
||||||
|
private var dst: Pair<UInt, UShort>? = null
|
||||||
|
private var inInsn: Boolean = false
|
||||||
|
|
||||||
|
private var pc: UShort = 0u
|
||||||
|
private var psw: UShort = 0u
|
||||||
|
private var sp: UShort = 0u
|
||||||
|
private var opcode: UShort = 0u
|
||||||
|
private var opargs: UShortArray = UShortArray(3)
|
||||||
|
private var oparg_ct: Int = 0
|
||||||
|
|
||||||
|
private val collectors: HashSet<(TraceRecord) -> Unit> = HashSet()
|
||||||
|
|
||||||
|
override fun noteBegin(cpu: CPU) {
|
||||||
|
if (inInsn) {
|
||||||
|
logger.warn("Began collection a new insn before committing the previous one. PC=${pc.toOctal()}")
|
||||||
|
}
|
||||||
|
pc = cpu.pc
|
||||||
|
psw = cpu.psw
|
||||||
|
sp = cpu.sp
|
||||||
|
src = null
|
||||||
|
dst = null
|
||||||
|
oparg_ct = 0
|
||||||
|
inInsn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noteOpcode(opcode: UShort) {
|
||||||
|
this.opcode = opcode
|
||||||
|
noteArgument(opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noteArgument(arg: UShort) {
|
||||||
|
opargs[oparg_ct++] = arg
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noteReference(spec: UInt, value: UShort) {
|
||||||
|
val ref = (spec and 0xFFFFu) to value
|
||||||
|
if (spec bit CPU.PADDR_DST_BIT) {
|
||||||
|
dst = ref
|
||||||
|
} else {
|
||||||
|
src = ref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noteEnd() {
|
||||||
|
val args = UShortArray(oparg_ct) {opargs[it]}
|
||||||
|
oparg_ct = 0
|
||||||
|
val record = TraceRecord(
|
||||||
|
pc, sp, psw, opcode, args,
|
||||||
|
src, dst,
|
||||||
|
)
|
||||||
|
for (coll in collectors) {
|
||||||
|
coll(record)
|
||||||
|
}
|
||||||
|
inInsn = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addCollector(c: Collector) {
|
||||||
|
collectors.add(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeCollector(c: Collector) {
|
||||||
|
collectors.remove(c)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("NOTHING_TO_INLINE")
|
@file:Suppress("NOTHING_TO_/*inline*/")
|
||||||
|
|
||||||
package com.thequux.mcpdp.ext.bit
|
package com.thequux.mcpdp.ext.bit
|
||||||
|
|
||||||
@@ -6,82 +6,82 @@ import kotlin.experimental.and
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
inline infix fun Byte.shr(qty: Int): Byte = this.toInt().shr(min(qty, 7)).toByte()
|
/*inline*/ infix fun Byte.shr(qty: Int): Byte = this.toInt().shr(min(qty, 7)).toByte()
|
||||||
inline infix fun Byte.shl(qty: Int): Byte = if (qty > 7) 0 else this.toInt().shl(qty).toByte()
|
/*inline*/ infix fun Byte.shl(qty: Int): Byte = if (qty > 7) 0 else this.toInt().shl(qty).toByte()
|
||||||
inline infix fun Byte.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0
|
/*inline*/ infix fun Byte.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0
|
||||||
inline fun Byte.bit(n: Int, v: Boolean): Byte = if (v) this bis n else this bic n
|
/*inline*/ fun Byte.bit(n: Int, v: Boolean): Byte = if (v) this bis n else this bic n
|
||||||
inline infix fun Byte.bis(n: Int): Byte = this.toInt().or(1 shl n).toByte()
|
/*inline*/ infix fun Byte.bis(n: Int): Byte = this.toInt().or(1 shl n).toByte()
|
||||||
inline infix fun Byte.bic(n: Int): Byte = this.toInt().and(1.shl(n).inv()).toByte()
|
/*inline*/ infix fun Byte.bic(n: Int): Byte = this.toInt().and(1.shl(n).inv()).toByte()
|
||||||
inline infix fun Byte.sex(n: Int): Byte {
|
/*inline*/ infix fun Byte.sex(n: Int): Byte {
|
||||||
val sign = 1.toByte() shl n+1
|
val sign = 1.toByte() shl n+1
|
||||||
return ((this and sign.dec()) - (this and sign)).toByte()
|
return ((this and sign.dec()) - (this and sign)).toByte()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline infix fun UByte.shr(qty: Int): UByte = this.toUInt().shr(min(qty, 7)).toUByte()
|
/*inline*/ infix fun UByte.shr(qty: Int): UByte = this.toUInt().shr(min(qty, 7)).toUByte()
|
||||||
inline infix fun UByte.shl(qty: Int): UByte = if (qty > 7) 0U else this.toInt().shl(qty).toUByte()
|
/*inline*/ infix fun UByte.shl(qty: Int): UByte = if (qty > 7) 0U else this.toInt().shl(qty).toUByte()
|
||||||
inline infix fun UByte.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U
|
/*inline*/ infix fun UByte.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U
|
||||||
inline fun UByte.bit(n: Int, v: Boolean): UByte = if (v) this bis n else this bic n
|
/*inline*/ fun UByte.bit(n: Int, v: Boolean): UByte = if (v) this bis n else this bic n
|
||||||
inline infix fun UByte.bis(n: Int): UByte = this.toUInt().or(1U shl n).toUByte()
|
/*inline*/ infix fun UByte.bis(n: Int): UByte = this.toUInt().or(1U shl n).toUByte()
|
||||||
inline infix fun UByte.bic(n: Int): UByte = this.toUInt().and(1U.shl(n).inv()).toUByte()
|
/*inline*/ infix fun UByte.bic(n: Int): UByte = this.toUInt().and(1U.shl(n).inv()).toUByte()
|
||||||
inline infix fun UByte.sex(n: Int): UByte {
|
/*inline*/ infix fun UByte.sex(n: Int): UByte {
|
||||||
val sign = 1.toUByte() shl n+1
|
val sign = 1.toUByte() shl n+1
|
||||||
return ((this and sign.dec()) - (this and sign)).toUByte()
|
return ((this and sign.dec()) - (this and sign)).toUByte()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline infix fun Short.shr(qty: Int): Short = this.toInt().shr(min(qty, 15)).toShort()
|
/*inline*/ infix fun Short.shr(qty: Int): Short = this.toInt().shr(min(qty, 15)).toShort()
|
||||||
inline infix fun Short.shl(qty: Int): Short = if (qty > 15) 0 else this.toInt().shl(qty).toShort()
|
/*inline*/ infix fun Short.shl(qty: Int): Short = if (qty > 15) 0 else this.toInt().shl(qty).toShort()
|
||||||
inline infix fun Short.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0
|
/*inline*/ infix fun Short.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0
|
||||||
inline fun Short.bit(n: Int, v: Boolean): Short = if (v) this bis n else this bic n
|
/*inline*/ fun Short.bit(n: Int, v: Boolean): Short = if (v) this bis n else this bic n
|
||||||
inline infix fun Short.bis(n: Int): Short = this.toInt().or(1 shl n).toShort()
|
/*inline*/ infix fun Short.bis(n: Int): Short = this.toInt().or(1 shl n).toShort()
|
||||||
inline infix fun Short.bic(n: Int): Short = this.toInt().and(1.shl(n).inv()).toShort()
|
/*inline*/ infix fun Short.bic(n: Int): Short = this.toInt().and(1.shl(n).inv()).toShort()
|
||||||
inline infix fun Short.sex(n: Int): Short {
|
/*inline*/ infix fun Short.sex(n: Int): Short {
|
||||||
val sign = 1.toShort() shl n+1
|
val sign = 1.toShort() shl n+1
|
||||||
return ((this and sign.dec()) - (this and sign)).toShort()
|
return ((this and sign.dec()) - (this and sign)).toShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline infix fun UShort.shr(qty: Int): UShort = this.toUInt().shr(min(qty, 15)).toUShort()
|
/*inline*/ infix fun UShort.shr(qty: Int): UShort = this.toUInt().shr(min(qty, 15)).toUShort()
|
||||||
inline infix fun UShort.shl(qty: Int): UShort = if (qty > 15) 0U else this.toInt().shl(qty).toUShort()
|
/*inline*/ infix fun UShort.shl(qty: Int): UShort = if (qty > 15) 0U else this.toInt().shl(qty).toUShort()
|
||||||
inline infix fun UShort.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U
|
/*inline*/ infix fun UShort.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U
|
||||||
inline fun UShort.bit(n: Int, v: Boolean): UShort = if (v) this bis n else this bic n
|
/*inline*/ fun UShort.bit(n: Int, v: Boolean): UShort = if (v) this bis n else this bic n
|
||||||
inline infix fun UShort.bis(n: Int): UShort = this.toUInt().or(1U shl n).toUShort()
|
/*inline*/ infix fun UShort.bis(n: Int): UShort = this.toUInt().or(1U shl n).toUShort()
|
||||||
inline infix fun UShort.bic(n: Int): UShort = this.toUInt().and(1U.shl(n).inv()).toUShort()
|
/*inline*/ infix fun UShort.bic(n: Int): UShort = this.toUInt().and(1U.shl(n).inv()).toUShort()
|
||||||
inline infix fun UShort.sex(n: Int): UShort {
|
/*inline*/ infix fun UShort.sex(n: Int): UShort {
|
||||||
val sign = 1.toUShort() shl (n-1)
|
val sign = 1.toUShort() shl (n-1)
|
||||||
return (this - (this and sign shl 1)).toUShort()
|
return (this - (this and sign shl 1)).toUShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun UShort.maskSet(v: UShort, mask: UShort) = this and mask.inv() or (v and mask)
|
/*inline*/ fun UShort.maskSet(v: UShort, mask: UShort) = this and mask.inv() or (v and mask)
|
||||||
|
|
||||||
inline infix fun Int.bit(n: Int): Boolean = this shr n and 1 != 0
|
/*inline*/ infix fun Int.bit(n: Int): Boolean = this shr n and 1 != 0
|
||||||
inline fun Int.bit(n: Int, v: Boolean): Int = if (v) this bis n else this bic n
|
/*inline*/ fun Int.bit(n: Int, v: Boolean): Int = if (v) this bis n else this bic n
|
||||||
inline infix fun Int.bis(n: Int): Int = this.or(1 shl n)
|
/*inline*/ infix fun Int.bis(n: Int): Int = this.or(1 shl n)
|
||||||
inline infix fun Int.bic(n: Int): Int = this and (1 shl n).inv()
|
/*inline*/ infix fun Int.bic(n: Int): Int = this and (1 shl n).inv()
|
||||||
inline infix fun Int.sex(n: Int): Int {
|
/*inline*/ infix fun Int.sex(n: Int): Int {
|
||||||
val sign = 1 shl n+1
|
val sign = 1 shl n+1
|
||||||
return ((this and sign.dec()) - (this and sign))
|
return ((this and sign.dec()) - (this and sign))
|
||||||
}
|
}
|
||||||
inline infix fun UInt.bit(n: Int): Boolean = this shr n and 1U != 0U
|
/*inline*/ infix fun UInt.bit(n: Int): Boolean = this shr n and 1U != 0U
|
||||||
inline fun UInt.bit(n: Int, v: Boolean): UInt = if (v) this bis n else this bic n
|
/*inline*/ fun UInt.bit(n: Int, v: Boolean): UInt = if (v) this bis n else this bic n
|
||||||
inline infix fun UInt.bis(n: Int): UInt = this.or(1U shl n)
|
/*inline*/ infix fun UInt.bis(n: Int): UInt = this.or(1U shl n)
|
||||||
inline infix fun UInt.bic(n: Int): UInt = this.and(1U.shl(n).inv())
|
/*inline*/ infix fun UInt.bic(n: Int): UInt = this.and(1U.shl(n).inv())
|
||||||
inline infix fun UInt.sex(n: Int): UInt {
|
/*inline*/ infix fun UInt.sex(n: Int): UInt {
|
||||||
val sign = 1U shl n+1
|
val sign = 1U shl n+1
|
||||||
return ((this and sign.dec()) - (this and sign))
|
return ((this and sign.dec()) - (this and sign))
|
||||||
}
|
}
|
||||||
|
|
||||||
inline infix fun Long.bit(n: Int): Boolean = this shr n and 1L != 0L
|
/*inline*/ infix fun Long.bit(n: Int): Boolean = this shr n and 1L != 0L
|
||||||
inline fun Long.bit(n: Int, v: Boolean): Long = if (v) this bis n else this bic n
|
/*inline*/ fun Long.bit(n: Int, v: Boolean): Long = if (v) this bis n else this bic n
|
||||||
inline infix fun Long.bis(n: Int): Long = this.or(1L shl n)
|
/*inline*/ infix fun Long.bis(n: Int): Long = this.or(1L shl n)
|
||||||
inline infix fun Long.bic(n: Int): Long = this and (1L shl n).inv()
|
/*inline*/ infix fun Long.bic(n: Int): Long = this and (1L shl n).inv()
|
||||||
inline infix fun Long.sex(n: Int): Long {
|
/*inline*/ infix fun Long.sex(n: Int): Long {
|
||||||
val sign = 1.toLong() shl n+1
|
val sign = 1.toLong() shl n+1
|
||||||
return (this and sign.dec()) - (this and sign)
|
return (this and sign.dec()) - (this and sign)
|
||||||
}
|
}
|
||||||
inline infix fun ULong.bit(n: Int): Boolean = this shr n and 1UL != 0UL
|
/*inline*/ infix fun ULong.bit(n: Int): Boolean = this shr n and 1UL != 0UL
|
||||||
inline fun ULong.bit(n: Int, v: Boolean): ULong = if (v) this bis n else this bic n
|
/*inline*/ fun ULong.bit(n: Int, v: Boolean): ULong = if (v) this bis n else this bic n
|
||||||
inline infix fun ULong.bis(n: Int): ULong = this.or(1UL shl n)
|
/*inline*/ infix fun ULong.bis(n: Int): ULong = this.or(1UL shl n)
|
||||||
inline infix fun ULong.bic(n: Int): ULong = this.and(1UL.shl(n).inv())
|
/*inline*/ infix fun ULong.bic(n: Int): ULong = this.and(1UL.shl(n).inv())
|
||||||
inline infix fun ULong.sex(n: Int): ULong {
|
/*inline*/ infix fun ULong.sex(n: Int): ULong {
|
||||||
val sign = 1UL shl n+1
|
val sign = 1UL shl n+1
|
||||||
return ((this and (sign.dec())) - (this and sign))
|
return ((this and (sign.dec())) - (this and sign))
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
package com.thequux.mcpdp.ext.bit
|
package com.thequux.mcpdp.ext.bit
|
||||||
|
|
||||||
fun UShort.toOctal(): String = this.toString(8).padStart(6,'0')
|
fun UShort.toOctal(len: Int = 6): String = this.toString(8).padStart(len,'0')
|
||||||
fun UByte.toOctal(): String = this.toString(8).padStart(3, '0')
|
fun UByte.toOctal(len: Int = 3): String = this.toString(8).padStart(len, '0')
|
||||||
fun UInt.toOctal(): String = this.toString(8).padStart(11, '0')
|
fun UInt.toOctal(len: Int = 11): String = this.toString(8).padStart(len, '0')
|
||||||
|
Reference in New Issue
Block a user