Got it to the point that it can run a simple echo test

This commit is contained in:
2023-09-12 15:39:13 +02:00
parent 0d3dfb5a37
commit 96038abeb1
12 changed files with 173 additions and 53 deletions

View File

@@ -17,6 +17,9 @@ dependencies {
// For the utilities classes
kapt("info.picocli:picocli-codegen:4.7.5")
implementation("info.picocli:picocli:4.7.5")
implementation("org.jline:jline-terminal:3.23.0")
implementation("org.jline:jline-terminal-jansi:3.23.0")
}
tasks.test {

View File

@@ -1,3 +1,22 @@
import com.thequux.mcpdp.core.CPU
import com.thequux.mcpdp.loadAbs
import com.thequux.mcpdp.peripheral.DL11
import com.thequux.mcpdp.peripheral.MemBus
import java.io.File
fun main(args: Array<String>) {
val tb = org.jline.terminal.TerminalBuilder.terminal()
try {
tb.enterRawMode()
var mbus = MemBus(65536)
var cpu = CPU(mbus)
val console = DL11(tb.input(), tb.output()).apply { mount(mbus.unibus) }
console.start()
cpu.loadAbs(File(args[0]))
cpu.runState = CPU.RunState.RUNNING
cpu.run()
} finally {
}
}

View File

@@ -2,21 +2,56 @@ package com.thequux.mcpdp
import com.thequux.mcpdp.core.CPU
import com.thequux.mcpdp.core.PAddressSpace
import com.thequux.mcpdp.ext.bit.bit
import com.thequux.mcpdp.ext.bit.shl
import java.io.File
/// Loads an RT-11 object file into memory
fun CPU.loadAbs(infile: File) {
val core = this.core.modeSpace
val inStream = infile.inputStream().buffered()
val buf = ByteArray(6)
val
var buf = ByteArray(6)
var offset = 0
var addr: UShort = 0u
var len: Int = 0
var cksum: Int = 0
while (true) {
var read = inStream.read(buf, 0, 6)
if (read == 0) {
if (read == -1) {
return
} else if (read < 6) {
}
if (hdr[0] != 0)
if (read < 6) {
// TODO: report the error
throw Exception("Short record: $read bytes")
}
if (buf[0] != 1.toByte() || buf[1] != 0.toByte()) {
throw Exception("Invalid block header")
}
len = buf[3].toInt().shl(8) + buf[2].toInt() - 6
addr = (buf[5].toUByte().toUShort().shl(8) + buf[4].toUByte().toUShort()).toUShort()
cksum = 0
for (i in 0..<6) {
cksum += buf[i]
}
if (buf.size < len+1) {
buf = ByteArray(len+1)
}
inStream.read(buf, 0, len+1)
for (i in 0..len) {
cksum += buf[i]
}
cksum = cksum and 0xFF
// TODO: validate checksum == 0
if (len == 0) {
// end of file
if (!(addr bit 0)) this.pc = addr
return
} else {
// copy data to memory
// TODO: copy a word at a time
for (i in 0..<len) {
core.setb(addr++, buf[i].toUByte())
}
}
}
}

View File

@@ -22,9 +22,9 @@ interface PAddressSpace {
val waddr = addr bic 0
val load = getw(waddr)
val store = if (addr bit 0) {
load and 0xFF00u or value.toUShort()
} else {
load and 0x00FFu or (value.toUShort() shl 8)
} else {
load and 0xFF00u or value.toUShort()
}
setw(waddr, store)
}

View File

@@ -12,8 +12,17 @@ import com.thequux.mcpdp.util.ProgrammerError
/// xxxx: rest
@OptIn(ExperimentalUnsignedTypes::class)
class CPU(val mbus: MemBus) {
private val flagStr: String
get() {
val chars = CharArray(4) { '-' }
if (N) chars[0] = 'N'
if (Z) chars[1] = 'Z'
if (V) chars[2] = 'V'
if (C) chars[3] = 'C'
return String(chars)
}
private val registers = UShortArray(8)
private val general_registers = Array<UShortArray>(2) { UShortArray(5) }
private val general_registers = Array<UShortArray>(2) { UShortArray(6) }
private val shadow_r6 = UShortArray(4) //
val core = PagingUnit(mbus)
@@ -54,7 +63,7 @@ class CPU(val mbus: MemBus) {
}
private var prv_mode: Int = 0
private var psw_pl: Int = 0
private var pc: UShort
var pc: UShort
get() = registers[7]
set(value) {
registers[7] = value
@@ -181,8 +190,8 @@ class CPU(val mbus: MemBus) {
private fun opc_dst(opcode: Int): UInt = op_resolve(opcode and 0x3F)
private fun opc_dstb(opcode: Int): UInt = op_resolve(opcode and 0x3F, byte_mode = true)
private fun opc_src(opcode: Int): UInt = op_resolve(opcode shr 6 and 0xFC0)
private fun opc_srcb(opcode: Int): UInt = op_resolve(opcode shr 6 and 0xFC0, byte_mode = true)
private fun opc_src(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F)
private fun opc_srcb(opcode: Int): UInt = op_resolve(opcode shr 6 and 0x3F, byte_mode = true)
private fun stack_pop(): UShort = core.getw(sp).also { sp = (sp + 2u).toUShort() }
private fun stack_push(value: UShort) {
@@ -195,7 +204,9 @@ class CPU(val mbus: MemBus) {
pc = (pc + 2u * (opcode and 0xFF).toByte().toShort().toUShort()).toUShort()
}
}
fun step() {
/// Internal step; evaluates one opcode, but does not handle errors, interrupts, or updating system registers
fun step_int() {
val opcode = core.getw(pc).toInt()
pc = (pc + 2u).toUShort()
when (opcode and 0xF000) {
@@ -219,7 +230,7 @@ class CPU(val mbus: MemBus) {
// jmp
val dst = opc_dst(opcode)
if (is_paddr_reg(dst)) {
throw InvalidOpcodeException()
trap(4u); // Illegal instruction
}
pc = dst.toUShort()
} // JMP
@@ -259,10 +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 = op_loadw(opc_dst(r))
val dst = opc_dst(r)
if (is_paddr_reg(dst)) {
trap(4u) // illegal opcode
}
stack_push(registers[r])
registers[r] = pc
pc = dst
pc = dst.toUShort()
} // JSR
0x0A00 -> when (opcode shr 6 and 3) {
0 -> { // CLR
@@ -541,7 +555,7 @@ class CPU(val mbus: MemBus) {
0x3000 -> { // BIT
val src = opc_src(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)
N = res bit 15
Z = res != 0u.toUShort()
V = false
@@ -660,13 +674,13 @@ class CPU(val mbus: MemBus) {
} // ASHC
4 -> {
val r = opcode shr 6 and 7
val dst = opc_dst(r)
val dst = opc_dst(opcode)
val res = op_loadw(dst) xor registers[r]
op_storw(dst, res)
N = res bit 15
Z = res == 0.toUShort()
V = false
}
} // XOR
5,6 -> throw InvalidOpcodeException()
7 -> {
val reg = opcode shr 6 and 7
@@ -695,7 +709,7 @@ class CPU(val mbus: MemBus) {
val src = op_loadb(opc_srcb(opcode))
val src2 = op_loadb(opc_dstb(opcode))
val res = (src - src2) and 0xFFu
N = res bit 8
N = res bit 7
Z = res == 0U
C = src < src2 // unsigned lt
V = ((src bit 8) xor (src2 bit 8)) and ((src2 bit 8) == (res bit 8))
@@ -703,9 +717,9 @@ class CPU(val mbus: MemBus) {
0xB000 -> { // BITB
val src = opc_srcb(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)
N = res bit 7
Z = res != 0u.toUByte()
Z = res == 0u.toUByte()
V = false
} // BITB
0xC000 -> { // BICB
@@ -741,6 +755,28 @@ class CPU(val mbus: MemBus) {
}
}
fun step() {
if (runState == RunState.HALTED) return
// TODO: check for interrupts
if (runState == RunState.WAIT_FOR_INTERRUPT) return
try {
step_int()
// TODO: handle T bit
} catch (error: MemoryError) {
// TODO: fill in memory manager fields
trap(4u)
} catch (_: InvalidOpcodeException) {
trap(10u)
}
}
fun run() {
while (runState == RunState.RUNNING) {
step()
}
}
fun trap(vector: UShort) {
stack_push(psw)
stack_push(pc)
@@ -751,12 +787,12 @@ class CPU(val mbus: MemBus) {
private inner class Registers: PAddressSpace {
override fun getw(addr: UInt): UShort = when (addr) {
0x3FFFEu -> psw
else -> throw MemoryError(MemoryErrorType.BusTimeout, addr)
else -> throw BusTimeoutError(addr)
}
override fun setw(addr: UInt, value: UShort) = when (addr) {
0x3FFFEu -> psw = value
else -> throw MemoryError(MemoryErrorType.BusTimeout, addr)
else -> throw BusTimeoutError(addr)
}
}

View File

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

View File

@@ -109,13 +109,18 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
var mode: Int = 0
set(value) {
field = value
modeSpace = modeVTabs[if (field == 3) 2 else field]
modeSpace = if (mmr[0] bit 0) {
modeVTabs[if (field == 3) 2 else field]
} else {
noMmanSpace
}
}
private val itabs: Array<PageTable> = Array(3) { PageTable(if (it == 2) 3 else it, dspace = false) }
private val dtabs: Array<PageTable> = Array(3) { PageTable(if (it == 2) 3 else it, dspace = true) }
private val modeVTabs: Array<ModeVSpace> = Array(3) { ModeVSpace(itabs[it], dtabs[it], false) }
var modeSpace: VAddressSpace = modeVTabs[0]
private var noMmanSpace = NoMmanSpace()
var modeSpace: VAddressSpace = noMmanSpace
private set
private val unibusTable = UIntArray(32)
@@ -135,6 +140,14 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
}
}
private inner class NoMmanSpace: VAddressSpace {
private fun map(addr: UShort): UInt = if ((addr shr 13) == 7u.toUShort()) addr.toUInt().or(0x3F0000u) else addr.toUInt()
override fun getb(addr: UShort): UByte = pspace.getb(map(addr))
override fun getw(addr: UShort, dspace: Boolean): UShort = pspace.getw(map(addr))
override fun setb(addr: UShort, value: UByte) = pspace.setb(map(addr), value)
override fun setw(addr: UShort, value: UShort, dspace: Boolean) = pspace.setw(map(addr), value)
}
companion object {
private val MMR3: UInt = 0x3F54Eu
}
@@ -158,7 +171,7 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
.bit(5, enableUnibusMap)
.toUShort())
} // 772516 MMR3
else -> throw MemoryError(MemoryErrorType.BusTimeout, addr)
else -> throw BusTimeoutError(addr)
}
override fun setw(addr: UInt, value: UShort): Unit = when(addr) {
@@ -178,7 +191,7 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
unibusTable[uaddr] = unibusTable[uaddr] and 0xFFFF_0000u or (value.toUInt() bic 0)
}
} // 770200..770377 Unibus map
else -> throw MemoryError(MemoryErrorType.BusTimeout, addr)
else -> throw BusTimeoutError(addr)
}
}
@@ -204,10 +217,12 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
private fun map(addr: UInt): UInt {
val uaddr = addr and 0x3_FFFFu
return if (uaddr > 0x3_0000u) {
// I/O page is always mapped as follows
addr or 0x3FC000u
} else {
unibusTable[(uaddr shr 13).toInt()] + (uaddr and 0x1FFFu) or 0x3C0000U
// I/O page is always mapped as follows
addr or 0x3FC000u
} else if (mmr[3] bit 5) {
unibusTable[(uaddr shr 13).toInt()] + (uaddr and 0x1FFFu) or 0x3C0000U
} else {
addr
}
}

View File

@@ -4,8 +4,9 @@ package com.thequux.mcpdp.ext.bit
import kotlin.experimental.and
import kotlin.math.max
import kotlin.math.min
inline infix fun Byte.shr(qty: Int): Byte = this.toInt().shr(max(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.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
@@ -16,7 +17,7 @@ inline infix fun Byte.sex(n: Int): Byte {
return ((this and sign.dec()) - (this and sign)).toByte()
}
inline infix fun UByte.shr(qty: Int): UByte = this.toUInt().shr(max(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.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
@@ -27,7 +28,7 @@ inline infix fun UByte.sex(n: Int): UByte {
return ((this and sign.dec()) - (this and sign)).toUByte()
}
inline infix fun Short.shr(qty: Int): Short = this.toInt().shr(max(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.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
@@ -38,7 +39,7 @@ inline infix fun Short.sex(n: Int): Short {
return ((this and sign.dec()) - (this and sign)).toShort()
}
inline infix fun UShort.shr(qty: Int): UShort = this.toUInt().shr(max(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.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

View File

@@ -2,6 +2,7 @@ package com.thequux.mcpdp.peripheral
import com.thequux.mcpdp.core.MemoryError
import com.thequux.mcpdp.core.MemoryErrorType
import com.thequux.mcpdp.core.OddAddressError
import com.thequux.mcpdp.core.PAddressSpace
import com.thequux.mcpdp.ext.bit.bic
import com.thequux.mcpdp.ext.bit.bis
@@ -33,7 +34,7 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re
2 -> { rcsr = rcsr bic 7; rbuf }
4 -> xcsr
6 -> 0u
else -> throw MemoryError(MemoryErrorType.OddAddress, addr)
else -> throw OddAddressError(addr)
}
}
@@ -52,7 +53,7 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re
}
ostr.write(b)
}
else -> throw MemoryError(MemoryErrorType.OddAddress, addr)
else -> throw OddAddressError(addr)
}
}
@@ -87,6 +88,6 @@ class DL11(private var istr: InputStream, private val ostr: OutputStream, val re
super.start()
reader = thread(block = this::readThread)
reader?.interrupt()
// reader?.interrupt()
}
}

View File

@@ -1,8 +1,6 @@
package com.thequux.mcpdp.peripheral
import com.thequux.mcpdp.core.MemoryError
import com.thequux.mcpdp.core.MemoryErrorType
import com.thequux.mcpdp.core.PAddressSpace
import com.thequux.mcpdp.core.*
import com.thequux.mcpdp.ext.bit.bit
import com.thequux.mcpdp.util.ConfigurationError
@@ -32,7 +30,7 @@ class MemBus(val size: Int) : PAddressSpace {
override fun getw(addr: UInt): UShort = when (addr) {
0x3FFF2u -> 0u // upper size
0x3FFF0u -> (size shr 6).toUShort()
else -> throw MemoryError(MemoryErrorType.BusTimeout, addr)
else -> throw BusTimeoutError(addr)
}
// Both registers are write-only
@@ -41,18 +39,23 @@ class MemBus(val size: Int) : PAddressSpace {
override fun getw(addr: UInt): UShort {
if (size bit 0) {
throw MemoryError(MemoryErrorType.OddAddress, addr)
throw OddAddressError(addr)
}
return when (addr.toInt()) {
in 0..size.dec() -> data[addr.toInt() shr 1]
in 0x3c0000..0x3FFFFF -> unibus.getw(addr)
else -> throw MemoryError(MemoryErrorType.NonExistent, addr)
in 0..size.dec() -> data[addr.toInt() shr 1]
else -> throw NonExistentMemoryError(addr)
}
}
override fun setw(addr: UInt, value: UShort) {
if (size bit 0) {
throw MemoryError(MemoryErrorType.OddAddress, addr)
throw OddAddressError(addr)
}
return when (addr.toInt()) {
in 0x3c0000..0x3FFFFF -> unibus.setw(addr, value)
in 0..size.dec() -> data[addr.toInt() shr 1] = value
else -> throw NonExistentMemoryError(addr)
}
}
}

View File

@@ -1,5 +1,6 @@
package com.thequux.mcpdp.peripheral
import com.thequux.mcpdp.core.BusTimeoutError
import com.thequux.mcpdp.core.MemoryError
import com.thequux.mcpdp.core.MemoryErrorType
import com.thequux.mcpdp.core.PAddressSpace
@@ -35,7 +36,7 @@ open class Subregion(private val suffix: Int, private val size: Int): Region {
}
override fun attach(address: UInt, suffix: Int, device: PAddressSpace) {
val loc = (address shr suffix and mask).toInt()
val loc = (address shr this.suffix and mask).toInt()
if (suffix <= 0) throw ProgrammerError("Suffix must be positive")
if (suffix < this.suffix) {
// needs a submap
@@ -64,7 +65,7 @@ class Unibus: PAddressSpace, Subregion(12, 6) {
var deviceView: PAddressSpace = this
internal set
override fun map(address: UInt): PAddressSpace =
super.map(address) ?: throw MemoryError(MemoryErrorType.BusTimeout, address)
super.map(address) ?: throw BusTimeoutError(address)
override fun getw(addr: UInt): UShort = map(addr).getw(addr)
override fun getb(addr: UInt): UByte = map(addr).getb(addr)
override fun setw(addr: UInt, value: UShort) = map(addr).setw(addr, value)

View File

@@ -6,11 +6,14 @@ xbuf = 177566
.psect .txt
.title echo
start:
mov #40,r3
tstb @#rcsr
bmi start
bge start
mov @#rbuf,r1
xor r1,#40
mov r1,@#xbuf
cmpb r1,#40
ble noxor
xor r3,r1
noxor: mov r1,@#xbuf
br start
.byte 123
.end start