Many changes

This commit is contained in:
2023-09-10 20:53:10 +02:00
parent a1daa4c4b3
commit 0d3dfb5a37
12 changed files with 307 additions and 75 deletions

2
.idea/kotlinc.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0" />
<option name="version" value="1.9.10" />
</component>
</project>

View File

@@ -1,6 +1,7 @@
plugins {
kotlin("jvm") version "1.9.0"
application
kotlin("kapt") version "1.9.10"
}
group = "com.thequux"
@@ -12,6 +13,10 @@ repositories {
dependencies {
testImplementation(kotlin("test"))
// For the utilities classes
kapt("info.picocli:picocli-codegen:4.7.5")
implementation("info.picocli:picocli:4.7.5")
}
tasks.test {

View File

@@ -1,7 +1,3 @@
fun main(args: Array<String>) {
println("Hello World!")
// Try adding program arguments via Run/Debug configuration.
// Learn more about running applications: https://www.jetbrains.com/help/idea/running-applications.html.
println("Program arguments: ${args.joinToString()}")
}

View File

@@ -0,0 +1,22 @@
package com.thequux.mcpdp
import com.thequux.mcpdp.core.CPU
import com.thequux.mcpdp.core.PAddressSpace
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
while (true) {
var read = inStream.read(buf, 0, 6)
if (read == 0) {
return
} else if (read < 6) {
}
if (hdr[0] != 0)
}
}

View File

@@ -11,11 +11,11 @@ import com.thequux.mcpdp.util.ProgrammerError
/// Exxx: ROM
/// xxxx: rest
@OptIn(ExperimentalUnsignedTypes::class)
class CPU(private val mbus: MemBus) {
class CPU(val mbus: MemBus) {
private val registers = UShortArray(8)
private val general_registers = Array<UShortArray>(2) { UShortArray(5) }
private val shadow_r6 = UShortArray(4) //
private val core = PagingUnit(mbus)
val core = PagingUnit(mbus)
var runState: RunState = RunState.HALTED
var psw: UShort

View File

@@ -57,7 +57,6 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
private var mmr = UShortArray(4)
private var enableUnibusMap: Boolean = false
private var enable22bit: Boolean = false
private var enableDSpaceByMode = BooleanArray(4)
/// The 18-bit address space exposed to peripherals
val unibusMap: PAddressSpace = UnibusMap()
@@ -108,36 +107,53 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
}
var mode: Int = 0
set(value) {
field = value
modeSpace = modeVTabs[if (field == 3) 2 else field]
}
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 var curIspace: PageTable = itabs[0]
private var curDspace: PageTable = dtabs[0]
private val modeVTabs: Array<ModeVSpace> = Array(3) { ModeVSpace(itabs[it], dtabs[it], false) }
var modeSpace: VAddressSpace = modeVTabs[0]
private set
private val unibusTable = UIntArray(32)
private fun getSpace(dspace: Boolean): PageTable = if (dspace) curDspace else curIspace
override fun getw(addr: UShort, dspace: Boolean): UShort = pspace.getw(getSpace(dspace).map(addr, write = false))
override fun getb(addr: UShort): UByte = pspace.getb(curDspace.map(addr, write = false))
private inner class ModeVSpace(private val iSpace: PageTable, private val dSpace: PageTable, var useDSpace: Boolean): VAddressSpace {
private fun getSpace(dspace: Boolean): PageTable = if (dspace) dSpace else iSpace
override fun getw(addr: UShort, dspace: Boolean): UShort = pspace.getw(getSpace(dspace).map(addr, write = false))
override fun setw(addr: UShort, value: UShort, dspace: Boolean) {
pspace.setw(getSpace(dspace).map(addr, write = true), value)
}
override fun getb(addr: UShort): UByte = pspace.getb(dSpace.map(addr, write = false))
override fun setb(addr: UShort, value: UByte) {
pspace.setb(curDspace.map(addr, write = true), value)
override fun setw(addr: UShort, value: UShort, dspace: Boolean) {
pspace.setw(getSpace(dspace).map(addr, write = true), value)
}
override fun setb(addr: UShort, value: UByte) {
pspace.setb(dSpace.map(addr, write = true), value)
}
}
companion object {
private val MMR3: UInt = 0x3F54Eu
}
override fun getw(addr: UShort, dspace: Boolean): UShort = modeSpace.getw(addr, dspace)
override fun getb(addr: UShort): UByte = modeSpace.getb(addr)
override fun setw(addr: UShort, value: UShort, dspace: Boolean) = modeSpace.setw(addr, value, dspace)
override fun setb(addr: UShort, value: UByte) = modeSpace.setb(addr, value)
private inner class ConfigRegisters: PAddressSpace {
override fun getw(addr: UInt): UShort = when (addr) {
MMR3 -> {
(0U.bit(0, enableDSpaceByMode[3])
.bit(1, enableDSpaceByMode[1])
.bit(2, enableDSpaceByMode[0])
(0U.bit(0, modeVTabs[2].useDSpace)
.bit(1, modeVTabs[1].useDSpace)
.bit(2, modeVTabs[0].useDSpace)
.bit(4, enable22bit)
.bit(5, enableUnibusMap)
.toUShort())
@@ -149,9 +165,9 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
MMR3 -> {
enableUnibusMap = value bit 5
enable22bit = value bit 4
enableDSpaceByMode[0] = value bit 2
enableDSpaceByMode[1] = value bit 1
enableDSpaceByMode[3] = value bit 0
modeVTabs[0].useDSpace = value bit 2
modeVTabs[1].useDSpace = value bit 1
modeVTabs[2].useDSpace = value bit 0
}
in 0x3F080u..0x3F0FFu -> {
val uaddr = (addr shr 1 and 31u).toInt()
@@ -178,10 +194,10 @@ class PagingUnit(val pspace: PAddressSpace): VAddressSpace {
attach(0x3_F4C0u, 6, regs) // Kernal PAR/PDF
attach(0x3_F480u, 6, regs) // Supervisor PAR/PDR
attach(0x3_F080u, 7, regs) // Unibus map
}
}
private inner class UnibusMap: PAddressSpace {
// This implements an *18-bit* address space, for the view of unibus peripherals

View File

@@ -1,82 +1,86 @@
@file:Suppress("NOTHING_TO_INLINE")
package com.thequux.mcpdp.ext.bit
import kotlin.experimental.and
import kotlin.math.max
infix fun Byte.shr(qty: Int): Byte = this.toInt().shr(max(qty, 7)).toByte()
infix fun Byte.shl(qty: Int): Byte = if (qty > 7) 0 else this.toInt().shl(qty).toByte()
infix fun Byte.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0
fun Byte.bit(n: Int, v: Boolean): Byte = if (v) this bis n else this bic n
infix fun Byte.bis(n: Int): Byte = this.toInt().or(1 shl n).toByte()
infix fun Byte.bic(n: Int): Byte = this.toInt().and(1.shl(n).inv()).toByte()
infix fun Byte.sex(n: Int): Byte {
inline infix fun Byte.shr(qty: Int): Byte = this.toInt().shr(max(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
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.sex(n: Int): Byte {
val sign = 1.toByte() shl n+1
return ((this and sign.dec()) - (this and sign)).toByte()
}
infix fun UByte.shr(qty: Int): UByte = this.toUInt().shr(max(qty, 7)).toUByte()
infix fun UByte.shl(qty: Int): UByte = if (qty > 7) 0U else this.toInt().shl(qty).toUByte()
infix fun UByte.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U
fun UByte.bit(n: Int, v: Boolean): UByte = if (v) this bis n else this bic n
infix fun UByte.bis(n: Int): UByte = this.toUInt().or(1U shl n).toUByte()
infix fun UByte.bic(n: Int): UByte = this.toUInt().and(1U.shl(n).inv()).toUByte()
infix fun UByte.sex(n: Int): UByte {
inline infix fun UByte.shr(qty: Int): UByte = this.toUInt().shr(max(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
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.sex(n: Int): UByte {
val sign = 1.toUByte() shl n+1
return ((this and sign.dec()) - (this and sign)).toUByte()
}
infix fun Short.shr(qty: Int): Short = this.toInt().shr(max(qty, 15)).toShort()
infix fun Short.shl(qty: Int): Short = if (qty > 15) 0 else this.toInt().shl(qty).toShort()
infix fun Short.bit(n: Int): Boolean = this.toInt() shr n and 1 != 0
fun Short.bit(n: Int, v: Boolean): Short = if (v) this bis n else this bic n
infix fun Short.bis(n: Int): Short = this.toInt().or(1 shl n).toShort()
infix fun Short.bic(n: Int): Short = this.toInt().and(1.shl(n).inv()).toShort()
infix fun Short.sex(n: Int): Short {
inline infix fun Short.shr(qty: Int): Short = this.toInt().shr(max(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
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.sex(n: Int): Short {
val sign = 1.toShort() shl n+1
return ((this and sign.dec()) - (this and sign)).toShort()
}
infix fun UShort.shr(qty: Int): UShort = this.toUInt().shr(max(qty, 15)).toUShort()
infix fun UShort.shl(qty: Int): UShort = if (qty > 15) 0U else this.toInt().shl(qty).toUShort()
infix fun UShort.bit(n: Int): Boolean = this.toUInt() shr n and 1U != 0U
fun UShort.bit(n: Int, v: Boolean): UShort = if (v) this bis n else this bic n
infix fun UShort.bis(n: Int): UShort = this.toUInt().or(1U shl n).toUShort()
infix fun UShort.bic(n: Int): UShort = this.toUInt().and(1U.shl(n).inv()).toUShort()
infix fun UShort.sex(n: Int): UShort {
inline infix fun UShort.shr(qty: Int): UShort = this.toUInt().shr(max(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
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.sex(n: Int): UShort {
val sign = 1.toUShort() shl n+1
return ((this and sign.dec()) - (this and sign)).toUShort()
}
infix fun Int.bit(n: Int): Boolean = this shr n and 1 != 0
fun Int.bit(n: Int, v: Boolean): Int = if (v) this bis n else this bic n
infix fun Int.bis(n: Int): Int = this.or(1 shl n)
infix fun Int.bic(n: Int): Int = this and (1 shl n).inv()
infix fun Int.sex(n: Int): Int {
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 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.bic(n: Int): Int = this and (1 shl n).inv()
inline infix fun Int.sex(n: Int): Int {
val sign = 1 shl n+1
return ((this and sign.dec()) - (this and sign))
}
infix fun UInt.bit(n: Int): Boolean = this shr n and 1U != 0U
fun UInt.bit(n: Int, v: Boolean): UInt = if (v) this bis n else this bic n
infix fun UInt.bis(n: Int): UInt = this.or(1U shl n)
infix fun UInt.bic(n: Int): UInt = this.and(1U.shl(n).inv())
infix fun UInt.sex(n: Int): UInt {
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 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.sex(n: Int): UInt {
val sign = 1U shl n+1
return ((this and sign.dec()) - (this and sign))
}
infix fun Long.bit(n: Int): Boolean = this shr n and 1L != 0L
fun Long.bit(n: Int, v: Boolean): Long = if (v) this bis n else this bic n
infix fun Long.bis(n: Int): Long = this.or(1L shl n)
infix fun Long.bic(n: Int): Long = this and (1L shl n).inv()
infix fun Long.sex(n: Int): Long {
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 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.sex(n: Int): Long {
val sign = 1.toLong() shl n+1
return (this and sign.dec()) - (this and sign)
}
infix fun ULong.bit(n: Int): Boolean = this shr n and 1UL != 0UL
fun ULong.bit(n: Int, v: Boolean): ULong = if (v) this bis n else this bic n
infix fun ULong.bis(n: Int): ULong = this.or(1UL shl n)
infix fun ULong.bic(n: Int): ULong = this.and(1UL.shl(n).inv())
infix fun ULong.sex(n: Int): ULong {
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 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.sex(n: Int): ULong {
val sign = 1UL shl n+1
return ((this and (sign.dec())) - (this and sign))
}

View File

@@ -0,0 +1,92 @@
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.ext.bit.bic
import com.thequux.mcpdp.ext.bit.bis
import com.thequux.mcpdp.ext.bit.bit
import com.thequux.mcpdp.ext.bit.maskSet
import java.io.InputStream
import java.io.OutputStream
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 {
private var reader: Thread? = null
private var rcsr: UShort = 0x0u
private var rbuf: UShort = 0u
private var xcsr: UShort = 0u
constructor(istr: InputStream, ostr: OutputStream): this(istr, ostr, 0x3FF70u, 0x30u)
override fun mount(bus: Unibus) {
bus.attach(reg_base, 3, this)
}
override fun service() { }
override fun getw(addr: UInt): UShort = synchronized(this) {
when (addr.toInt() and 7) {
0 -> rcsr
2 -> { rcsr = rcsr bic 7; rbuf }
4 -> xcsr
6 -> 0u
else -> throw MemoryError(MemoryErrorType.OddAddress, addr)
}
}
override fun setw(addr: UInt, value: UShort): Unit = synchronized(this) {
when (addr.toInt() and 7) {
0 -> {
rcsr = rcsr.maskSet(value, 0x009Eu)
if (value bit 0) {rcsr = rcsr bic 7 }
}
2 -> { rcsr = rcsr bic 7 }
4 -> xcsr = xcsr.maskSet(value, 0x0045u)
6 -> {
val b = value.toInt() and 0xFF
if (xcsr bit 1) {
recvByte(b)
}
ostr.write(b)
}
else -> throw MemoryError(MemoryErrorType.OddAddress, addr)
}
}
private fun recvByte(data: Int) {
val overrun = rcsr bit 7
rcsr = rcsr and 0xC3FFu
if (data < 0) {
rcsr = rcsr bis 12
} else {
rcsr = rcsr bis 7
rbuf = (data and 0xFF).bit(14, overrun).bit(15, overrun).toUShort()
}
}
private fun readThread() {
try {
while (true) {
val data = istr.read()
synchronized(this) {
// receive if not in maintenance mode
// TODO: block until receiver ready
if (!(xcsr bit 1)) recvByte(data)
}
if (data < 0) {
break
}
}
} catch (_: InterruptedException) {
// Nothing to do; time to shut down
}
}
override fun start() {
super.start()
reader = thread(block = this::readThread)
reader?.interrupt()
}
}

View File

@@ -0,0 +1,14 @@
package com.thequux.mcpdp.peripheral
interface Peripheral {
fun mount(bus: Unibus)
fun reset() {}
/// Check if the device needs to do something
fun service()
/// Called when the device starts up
fun start() {}
/// Called when the device stops
fun stop() {}
}

View File

@@ -13,7 +13,8 @@ interface Region {
fun map(address: UInt): PAddressSpace?
}
private class MappedDevice(val device: PAddressSpace): Region {
@JvmInline
private value class MappedDevice(val device: PAddressSpace): Region {
override fun attach(address: UInt, suffix: Int, device: PAddressSpace) {
throw ConfigurationError("Attempted to map one device over another at ${address.toString(8)}")
}
@@ -68,4 +69,8 @@ class Unibus: PAddressSpace, Subregion(12, 6) {
override fun getb(addr: UInt): UByte = map(addr).getb(addr)
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")
}
}

View File

@@ -0,0 +1,62 @@
@file:OptIn(ExperimentalUnsignedTypes::class)
package com.thequux.mcpdp.tools
import com.sun.org.apache.xml.internal.security.utils.UnsyncBufferedOutputStream
import picocli.CommandLine
import picocli.CommandLine.Option
import picocli.CommandLine.Parameters
import java.io.File
import java.util.Hashtable
import java.util.concurrent.Callable
import java.util.function.Consumer
@CommandLine.Command(name = "link", description = ["Link together multiple object files into something loadable"])
class Link11: Callable<Int> {
@Parameters()
lateinit var objects: Array<File>
@Option(names = ["-o", "--out"], required = true, description = ["Output file"])
lateinit var outFile: File
@Option(names = ["-l", "--load"], description = ["Load address"])
var loadAddr: Int = 1024
companion object {
val R40Chars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789".toCharArray()
fun decodeRadix50(word: UShort): String {
val buf = CharArray(3)
var value = word.toInt()
for (i in 0..2) {
buf[2-i] = (value % 40).toChar()
value /= 40
}
return String(buf)
}
fun main(args: Array<String>) {
}
}
// symbols are always 4 bytes of radix-50, so we can just treat it as a uint
private val symbols: HashMap<UInt, UShort> = HashMap()
private val pendingReloc: HashMap<UInt, ArrayList<Consumer<UInt>>> = HashMap()
private val mem = UByteArray(65536)
private val prog_start_addr: UShort = 1u
private val prog_start_value: UShort = 1u
private val prog_start_psect: UInt = 0xF94AF01u // ". ABS."
override fun call(): Int {
return 0
}
private fun load_rt11() {
}
}

16
test/echo.mac Normal file
View File

@@ -0,0 +1,16 @@
rcsr = 177560
rbuf = 177562
xcsr = 177564
xbuf = 177566
.psect .txt
.title echo
start:
tstb @#rcsr
bmi start
mov @#rbuf,r1
xor r1,#40
mov r1,@#xbuf
br start
.byte 123
.end start