From 9d2397d80976e7bbb9950574ce7e4be0cdb7efb4 Mon Sep 17 00:00:00 2001 From: TQ Hirsch Date: Sat, 16 Sep 2023 14:17:56 +0200 Subject: [PATCH] Started RL0x device, debugged failures in DZQKA --- src/main/kotlin/Main.kt | 4 +- .../kotlin/com/thequux/mcpdp/RT11Loader.kt | 3 +- src/main/kotlin/com/thequux/mcpdp/core/CPU.kt | 12 +- .../com/thequux/mcpdp/peripheral/DL11.kt | 7 + .../com/thequux/mcpdp/peripheral/MemBus.kt | 19 ++- .../com/thequux/mcpdp/peripheral/RL11.kt | 161 ++++++++++++++++++ 6 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/com/thequux/mcpdp/peripheral/RL11.kt diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index d4eb0f5..2f62983 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -7,7 +7,7 @@ import java.io.File fun main(args: Array) { val tb = org.jline.terminal.TerminalBuilder.terminal() - var mbus = MemBus(65536) + var mbus = MemBus(32768) var cpu = CPU(mbus) val console = DL11(mbus.unibus, tb.input(), tb.output()).apply { mount(mbus.unibus) } try { @@ -17,7 +17,9 @@ fun main(args: Array) { cpu.loadAbs(File(args[0])) cpu.runState = CPU.RunState.RUNNING + cpu.pc = 0x80u cpu.run() + System.err.println("Halted at 0${cpu.pc.toString(8)}") } finally { println("Exiting") console.stop() diff --git a/src/main/kotlin/com/thequux/mcpdp/RT11Loader.kt b/src/main/kotlin/com/thequux/mcpdp/RT11Loader.kt index 9906d01..74e48c0 100644 --- a/src/main/kotlin/com/thequux/mcpdp/RT11Loader.kt +++ b/src/main/kotlin/com/thequux/mcpdp/RT11Loader.kt @@ -44,7 +44,7 @@ fun CPU.loadAbs(infile: File) { if (buf[0] != 1.toByte() || buf[1] != 0.toByte()) { throw Exception("Invalid block header at $pos: ${buf[0]},${buf[1]}") } - len = buf[3].toInt().shl(8) + buf[2].toInt() - 6 + len = buf[3].toUByte().toInt().shl(8) + buf[2].toUByte().toInt() - 6 addr = (buf[5].toUByte().toUShort().shl(8) + buf[4].toUByte().toUShort()).toUShort() cksum = 0 for (i in 0..<6) { @@ -53,6 +53,7 @@ fun CPU.loadAbs(infile: File) { if (buf.size < len+1) { buf = ByteArray(len+1) } + System.err.println("Reading ${len+1} bytes into ${buf.size}") inStream.read(buf, 0, len+1) for (i in 0..len) { cksum += buf[i] diff --git a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt index 51b3e1c..71eaf29 100644 --- a/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt +++ b/src/main/kotlin/com/thequux/mcpdp/core/CPU.kt @@ -270,12 +270,13 @@ class CPU(val mbus: MemBus) { 0x0700 -> br_rel(Z or (N xor V), opcode) // BLE 0x0800, 0x0900 -> { // JSR val r = opcode shr 6 and 0x7 - val dst = opc_dst(r) + val dst = opc_dst(opcode) if (is_paddr_reg(dst)) { trap(4u) // illegal opcode } stack_push(registers[r]) registers[r] = pc + System.err.println("JSR to ${dst.toString(16)}: ${opcode.toString(8)}") pc = dst.toUShort() } // JSR 0x0A00 -> when (opcode shr 6 and 3) { @@ -772,7 +773,7 @@ class CPU(val mbus: MemBus) { } } if (runState == RunState.WAIT_FOR_INTERRUPT) return - +// System.err.println("Executing insn at ${pc.toString(8)}") step_int() // TODO: handle T bit } catch (error: MemoryError) { @@ -803,8 +804,11 @@ class CPU(val mbus: MemBus) { } override fun setw(addr: UInt, value: UShort) = when (addr) { - 0x3FFFEu -> psw = value - else -> throw BusTimeoutError(addr) + 0x3_FFFEu -> psw = value + else -> { + println("Bus error at ${addr.toString(16)}") + throw BusTimeoutError(addr) + } } } diff --git a/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt b/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt index 470683b..64205e8 100644 --- a/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt +++ b/src/main/kotlin/com/thequux/mcpdp/peripheral/DL11.kt @@ -76,6 +76,13 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re } } + override fun setb(addr: UInt, value: UByte) { + when (addr.toInt() and 7) { + 0 -> setw(0u, value.toUShort()) // high bits are unaffected + + } + } + private fun recvByte(data: Int) { val overrun = rcsr bit 7 rcsr = rcsr and 0xC3FFu diff --git a/src/main/kotlin/com/thequux/mcpdp/peripheral/MemBus.kt b/src/main/kotlin/com/thequux/mcpdp/peripheral/MemBus.kt index b78d76c..fb716c6 100644 --- a/src/main/kotlin/com/thequux/mcpdp/peripheral/MemBus.kt +++ b/src/main/kotlin/com/thequux/mcpdp/peripheral/MemBus.kt @@ -42,7 +42,7 @@ class MemBus(val size: Int) : PAddressSpace { throw OddAddressError(addr) } return when (addr.toInt()) { - in 0x3c0000..0x3FFFFF -> unibus.getw(addr) + in 0x3c0000..0x3FFFFF -> unibus.getw(addr and 0x3_FFFFu) in 0..size.dec() -> data[addr.toInt() shr 1] else -> throw NonExistentMemoryError(addr) } @@ -53,9 +53,24 @@ class MemBus(val size: Int) : PAddressSpace { throw OddAddressError(addr) } return when (addr.toInt()) { - in 0x3c0000..0x3FFFFF -> unibus.setw(addr, value) + in 0x3c0000..0x3FFFFF -> unibus.setw(addr and 0x3_FFFFu, value) in 0..size.dec() -> data[addr.toInt() shr 1] = value else -> throw NonExistentMemoryError(addr) } } + + + override fun getb(addr: UInt): UByte { + return when (addr.toInt()) { + in 0x3c0000..0x3FFFFF -> unibus.getb(addr and 0x3_FFFFu) + else -> super.getb(addr) + } + } + + override fun setb(addr: UInt, value: UByte) { + return when (addr.toInt()) { + in 0x3c0000..0x3FFFFF -> unibus.setb(addr and 0x3_FFFFu, value) + else -> super.setb(addr, value) + } + } } diff --git a/src/main/kotlin/com/thequux/mcpdp/peripheral/RL11.kt b/src/main/kotlin/com/thequux/mcpdp/peripheral/RL11.kt new file mode 100644 index 0000000..d41935c --- /dev/null +++ b/src/main/kotlin/com/thequux/mcpdp/peripheral/RL11.kt @@ -0,0 +1,161 @@ +package com.thequux.mcpdp.peripheral + +import com.thequux.mcpdp.core.OddAddressError +import com.thequux.mcpdp.core.PAddressSpace +import com.thequux.mcpdp.ext.bit.bis +import com.thequux.mcpdp.ext.bit.bit +import com.thequux.mcpdp.ext.bit.maskSet +import com.thequux.mcpdp.ext.bit.shr +import java.io.RandomAccessFile + +// Only one can be attached, so no need for configurable addresses +class RL11(val unibus: Unibus): PAddressSpace, Peripheral { + val reg_base: UInt = 0x3F900u + val vector: UShort = 0x70u + + val silo: ArrayDeque = ArrayDeque(16) + + // registers + var csr: UShort = 0x8080u + set(value) { + field = value bis 7 + cur_disk = (value shr 8 and 0x3u).toInt() + bus_addr = bus_addr and 0xFFFFu or (value.toUInt() and 0x30u shl 12) + } + get() { + var newf = field.bit(14, disks[cur_disk].error) + newf = newf.bit(15, (field and 0x7C00u) != 0.toUShort()) + return newf + } + + var cur_disk: Int = 0 + var bus_addr: UInt = 0u + var disk_addr: UShort = 0u + var mpr: UShort = 0u + + private val interruptSource = InterruptSource(unibus, 5, vector, true) + + companion object { + val RLCS_DRDY: UShort = 0x0001u + val RLCS_IE: UShort = 0x0040u + val RLCS_CRDY: UShort = 0x0080u + val RLCS_OPI: UShort = 0x0400u + val RLCS_CKE: UShort = 0x0800u // Also called DCRC, HCRC, or WCE + val RLCS_DLT: UShort = 0x1000u // also called HNF + val RLCS_NXM: UShort = 0x2000u + val RLCS_DE: UShort = 0x4000u + val RLCS_ERR: UShort = 0x8000u + } + + private class Disk { + + companion object { + } + var media: RandomAccessFile? = null + var isRL02: Boolean = true + + var cyl: Int = 0 // 0..256/512 + var sector: Int = 0 // 0..=39 + var head: Boolean = false + var error: Boolean = false + + val status: UShort + get() { + return 0u + } + + fun seek(dar: UShort) { + var dcyl = dar.toInt() shr 7 + if (dar bit 2) { + // dir? + dcyl = -dcyl + } + head = dar bit 4 + cyl = (cyl + dcyl).coerceIn(0..39) + } + } + + private val disks: Array = Array(4) { Disk() } + override fun getw(addr: UInt): UShort = + when (addr.toInt() and 7) { + 0 -> csr + 2 -> bus_addr.toUShort() + 4 -> disk_addr + 6 -> siloPop() + else -> throw OddAddressError(addr) + } + + + override fun setw(addr: UInt, value: UShort) { + when (addr.toInt() and 7) { + 0 -> { + // It would be 0x3FE, but we want to always keep crdy set because all commands complete instantly + csr = csr.maskSet(value, 0x37Eu) + if (value.inv() bit 7) { + // go command sent + execCommand() + } + } + 2 -> bus_addr = bus_addr and 0x3_0000u or value.toUInt() + 4 -> disk_addr = value + 6 -> mpr = value + else -> throw OddAddressError(addr) + } + } + + + override fun mount(bus: Unibus) { + unibus.attach(reg_base, 3, this) + } + + // TODO: make this a general purpose interface + override fun service() { + TODO("Not yet implemented") + } + + fun siloPush(value: UShort) { + if (silo.size >= 16) { + return + } + silo.addLast(value) + } + fun siloPop(): UShort { + return if (silo.isEmpty()) { + 0u + } else { + silo.removeFirst() + } + } + + /// Execute the command in csr + private fun execCommand() { + val command = csr.toInt() shr 1 and 0x7 + when (command) { + 0 -> {} // No-op + 1 -> { + + } // write check + 2 -> { + + } // get status + 3 -> { + if (disk_addr and 0x6bu != 1u.toUShort()) { + csr = csr or RLCS_OPI + } + } // seek + 4 -> { + + } // read header + 5 -> { + + } // write data + 6 -> { + + } // read data + 7 -> { + + } // read data without header check + } + } + +} \ No newline at end of file