diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 744c6c5..d4eb0f5 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -9,7 +9,7 @@ fun main(args: Array) { val tb = org.jline.terminal.TerminalBuilder.terminal() var mbus = MemBus(65536) var cpu = CPU(mbus) - val console = DL11(tb.input(), tb.output()).apply { mount(mbus.unibus) } + val console = DL11(mbus.unibus, tb.input(), tb.output()).apply { mount(mbus.unibus) } try { tb.enterRawMode() diff --git a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt index 8781b4e..51b3e1c 100644 --- a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt +++ b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt @@ -35,7 +35,7 @@ class CPU(val mbus: MemBus) { if (Z) res = res or 0x0004 if (N) res = res or 0x0008 if (T) res = res or 0x0010 - res = res or (psw_pl shl 5) + res = res or (psw_priority shl 5) res = res or (cur_mode shl 14) or (prv_mode shl 12) res = res or (registerSet shl 11) return res.toUShort() @@ -47,7 +47,7 @@ class CPU(val mbus: MemBus) { Z = value bit 3 N = value bit 4 T = value bit 5 // TODO: handle suspended trap - psw_pl = newpsw shr 5 and 7 + psw_priority = newpsw shr 5 and 7 registerSet = newpsw shr 11 and 1 cur_mode = newpsw shr 14 @@ -62,7 +62,7 @@ class CPU(val mbus: MemBus) { core.mode = value } private var prv_mode: Int = 0 - private var psw_pl: Int = 0 + private var psw_priority: Int = 0 var pc: UShort get() = registers[7] set(value) { @@ -240,7 +240,7 @@ class CPU(val mbus: MemBus) { registers[reg] = stack_pop() } // RTS in 0x98..0x9F -> { - psw_pl = opcode and 0x7 + psw_priority = opcode and 0x7 } // SPL in 0xA0..0xBF -> { val flag = opcode bit 4 @@ -756,11 +756,23 @@ class CPU(val mbus: MemBus) { } fun step() { - if (runState == RunState.HALTED) return - // TODO: check for interrupts - if (runState == RunState.WAIT_FOR_INTERRUPT) return try { + if (runState == RunState.HALTED) return + + // TODO: handle PIRQ + for (i in (psw_priority + 1..<7).reversed()) { + val source = mbus.unibus.checkInterrupt(i) + if (source != null) { + // we might have been waiting for an interrupt + runState = RunState.RUNNING + trap(source.vector) + source.handled() + break + } + } + if (runState == RunState.WAIT_FOR_INTERRUPT) return + step_int() // TODO: handle T bit } catch (error: MemoryError) { diff --git a/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt b/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt index aae39c6..454231e 100644 --- a/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt +++ b/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt @@ -14,13 +14,25 @@ import java.util.concurrent.Semaphore import kotlin.concurrent.thread -class DL11(private var istr: InputStream, private val ostr: OutputStream, val reg_base: UInt, val vector: UShort): PAddressSpace, Peripheral { +class DL11(private var istr: InputStream, private val ostr: OutputStream, val reg_base: UInt, val vector: UShort, val unibus: Unibus): PAddressSpace, Peripheral { private var reader: Thread? = null private var rcsr: UShort = 0x0u + set(value) { + /// Propagate status bits + val setBits = field.inv().and(value).toInt() + val changedBits = (field xor value).toInt() + val datasetStatusChanged = (setBits.and(0x4000) or changedBits.and(0x3400)) != 0 + val newValue = value.bit(15, datasetStatusChanged) + intrRcv.level = intrRcv.level || (newValue bit 5 && datasetStatusChanged && !(field bit 15)) || (newValue bit 6 && setBits bit 7) + field = newValue + } private var rbuf: UShort = 0u - private var xcsr: UShort = 0u + private var xcsr: UShort = 0x80u - constructor(istr: InputStream, ostr: OutputStream): this(istr, ostr, 0x3FF70u, 0x30u) + private var intrRcv = InterruptSource(unibus, 4, vector, edgeTriggered = true) + private var intrXmit = InterruptSource(unibus, 4, (vector+4u).toUShort(), edgeTriggered = true) + + constructor(unibus: Unibus, istr: InputStream, ostr: OutputStream): this(istr, ostr, 0x3FF70u, 0x30u, unibus) override fun mount(bus: Unibus) { bus.attach(reg_base, 3, this) @@ -30,7 +42,11 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re override fun getw(addr: UInt): UShort = synchronized(this) { when (addr.toInt() and 7) { - 0 -> rcsr + 0 -> { + val oldRcsr = rcsr + rcsr = rcsr bic 15 + oldRcsr + } 2 -> { rcsr = rcsr bic 7; rbuf } 4 -> xcsr 6 -> 0u @@ -48,9 +64,10 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re 4 -> xcsr = xcsr.maskSet(value, 0x0045u) 6 -> { val b = value.toInt() and 0xFF - if (xcsr bit 1) { + if (xcsr bit 2) { recvByte(b) } + if (xcsr bit 6) intrXmit.level = true ostr.write(b) } else -> throw OddAddressError(addr) diff --git a/src/main/kotlin/com/thequux/mcpdp/peripheral/Unibus.kt b/src/main/kotlin/com/thequux/mcpdp/peripheral/Unibus.kt index 2bbaa15..f40ff27 100644 --- a/src/main/kotlin/com/thequux/mcpdp/peripheral/Unibus.kt +++ b/src/main/kotlin/com/thequux/mcpdp/peripheral/Unibus.kt @@ -7,6 +7,9 @@ import com.thequux.mcpdp.core.PAddressSpace import com.thequux.mcpdp.util.ConfigurationError import com.thequux.mcpdp.util.ProgrammerError import java.lang.Integer.min +import java.util.Collections +import java.util.LinkedList +import java.util.Vector interface Region { fun attach(address: UInt, suffix: Int, device: PAddressSpace) @@ -14,6 +17,11 @@ interface Region { fun map(address: UInt): PAddressSpace? } +interface Device { + // TODO: Add runtime config interface + val vector: UShort +} + @JvmInline private value class MappedDevice(val device: PAddressSpace): Region { override fun attach(address: UInt, suffix: Int, device: PAddressSpace) { @@ -64,6 +72,9 @@ class Unibus: PAddressSpace, Subregion(12, 6) { /// The view of the unibus from a device var deviceView: PAddressSpace = this internal set + + private val queue: Array> = Array(8) { Vector(4) } + override fun map(address: UInt): PAddressSpace = super.map(address) ?: throw BusTimeoutError(address) override fun getw(addr: UInt): UShort = map(addr).getw(addr) @@ -71,7 +82,33 @@ class Unibus: PAddressSpace, Subregion(12, 6) { override fun setw(addr: UInt, value: UShort) = map(addr).setw(addr, value) override fun setb(addr: UInt, value: UByte) = map(addr).setb(addr, value) - fun interrupt(vector: UShort) { - throw NotImplementedError("Interrupts not yet implemented") + /// Request that the processor service an interrupt. When the interrupt is handled, calls getVector() on the device + /// to fetch the currently desired interrupt vector + fun assertInterrupt(priority: Int, device: InterruptSource) { + val list = queue[priority] + if (!list.contains(device)) list.add(device) } + + fun deassertInterrupt(priority: Int, device: InterruptSource) { + queue[priority].remove(device) + } + + // Checks for an interrupt. The handler receives (priority, device) + fun checkInterrupt(priority: Int): InterruptSource? = queue[priority].firstOrNull() } + +class InterruptSource(val unibus: Unibus, val priority: Int, val vector: UShort, val edgeTriggered: Boolean = false) { + fun handled() { + if (edgeTriggered) level = false + } + + var level: Boolean = false + set(value) { + when(value) { + field -> {} + true -> unibus.assertInterrupt(priority, this) + false -> unibus.deassertInterrupt(priority, this) + } + field = value + } +} \ No newline at end of file