Initial commit

This commit is contained in:
2023-08-28 14:00:07 +02:00
commit 35736a5fd2
23 changed files with 1035 additions and 0 deletions

7
src/main/kotlin/Main.kt Normal file
View File

@@ -0,0 +1,7 @@
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,8 @@
package com.thequux.mcpdp.core
interface AddressSpace {
fun getw(addr: UShort): UShort
fun getb(addr: UShort): UByte
fun setw(addr: UShort, value: UShort)
fun setb(addr: UShort, value: UByte)
}

View File

@@ -0,0 +1,349 @@
package com.thequux.mcpdp.core
import com.thequux.mcpdp.ext.bit.*
/// The main CPU
///
/// By default, the memory map is arranged as follows:
/// Fxxx: Channel I/O
/// Exxx: ROM
/// xxxx: rest
@OptIn(ExperimentalUnsignedTypes::class)
class CPU(private val core: AddressSpace) {
private val registers = UShortArray(8)
public var psw: UShort = 0U
private var pc: UShort
get() = registers[7]
set(value) {
registers[7] = value
}
private var cc: UShort
get() = psw and 0xFu
set(value) {
psw = (psw and 0xFFFu) or (value and 0xFu)
}
private var N: Boolean = false
private var C: Boolean = false
private var Z: Boolean = false
private var V: Boolean = false
/// Load an operand. This performs any indicated pre-inc/post-dec, so this or op_adjust must be called exactly once per operand
private fun op_resolve(operand: Int, byte_mode: Boolean = false): UInt {
val mode = operand shr 3
val reg = operand and 0x7
val increment = if (byte_mode && reg != 7) 1U else 2U
return when (mode) {
0 -> reg.toUInt() + 0x1_0000U
1 -> registers[reg].toUInt()
2 -> {
val addr = registers[reg]
registers[reg] = (addr + increment).toUShort()
addr.toUInt()
}
3 -> {
val addr = registers[reg]
registers[reg] = (addr + 2U).toUShort()
core.getw(addr).toUInt()
}
4 -> {
registers[reg] = (registers[reg] - increment).toUShort()
return registers[reg].toUInt()
}
5 -> {
registers[reg] = (registers[reg] - 2U).toUShort()
core.getw(registers[reg]).toUInt()
}
6 -> {
val idx = core.getw(pc)
pc = (pc + 2u).toUShort()
idx + registers[reg]
}
7 -> {
val idx = core.getw(pc)
pc = (pc+2u).toUShort()
core.getw((idx+registers[reg]).toUShort()).toUInt()
}
else -> throw InvalidOpcodeException()
}
}
/// Store an operand. This assumes that the spec has already been `load`ed or `adjust`ed
private fun op_storb(spec: UInt, value: UByte) {
val addr = (spec and 0xFFFFu).toUShort()
if (spec >= 0x1_0000u) {
// register
registers[addr.toInt()] = registers[addr.toInt()] and 0xFF00u or value.toUShort()
} else {
core.setb(addr, value)
}
}
private fun op_loadb(spec: UInt): UByte {
val addr = (spec and 0xFFFFu).toUShort()
return if (spec > 0x1_0000u) {
(registers[addr.toInt()] and 0xFFu).toUByte()
} else {
core.getb(addr)
}
}
private fun op_storw(spec: UInt, value: UShort) {
val addr = (spec and 0xFFFFu).toUShort()
if (spec >= 0x1_0000u) {
// register
registers[addr.toInt()] = value
} else {
core.setw(addr, value)
}
}
private fun op_loadw(spec: UInt): UShort {
val addr = (spec and 0xFFFFu).toUShort()
return if (spec > 0x1_0000u) {
(registers[addr.toInt()] and 0xFFu)
} else {
core.getw(addr)
}
}
companion object {
val CC_C: UShort = 1U
val CC_V: UShort = 2U
val CC_Z: UShort = 4U
val CC_N: UShort = 8U
}
private fun opc_dst(opcode: Int): UInt = op_resolve(opcode and 0x3F)
private fun opc_src(opcode: Int): UInt = op_resolve(opcode shr 6 and 0xFC0)
private fun br_rel(cond: Boolean, opcode: Int) {
if (cond) {
pc = (pc + 2u * (opcode and 0xFF).toByte().toShort().toUShort()).toUShort()
}
}
fun step() {
val opcode = core.getw(pc).toInt()
pc = (pc + 2u).toUShort()
when (opcode and 0xF000) {
0x0, 0x8000 -> when (opcode and 0xFFC0) {
0x0000 -> when (opcode) {
0x0003 -> throw Trap(14) // BPT
}
0x0080 ->
0x0100, 0x0140, 0x0180, 0x01C0 -> br_rel(true, opcode) // BR
0x0200, 0x0240, 0x0280, 0x02C0 -> br_rel(!Z, opcode) // BNE
0x0300, 0x0340, 0x0380, 0x03C0 -> br_rel(Z, opcode) // BEQ
0x0400, 0x0440, 0x0480, 0x04C0 -> br_rel(N == V, opcode) // BGE
0x0600, 0x0640, 0x0680, 0x06C0 -> br_rel(!Z and (N == V), opcode) // BGT
0x0500, 0x0540, 0x0580, 0x05C0 -> br_rel(N xor V, opcode) // BLT
0x0700, 0x0740, 0x0780, 0x07C0 -> br_rel(Z or (N xor V), opcode) // BLE
0x0A00 -> { // CLR
op_storw(opc_dst(opcode), 0U)
N = false
V = false
C = false
Z = true
} // CLR
0x0B40 -> { // ADC
val dst = opc_dst(opcode)
val c: UShort = if (C) 1u else 0u
val res = (op_loadw(dst) + c).toUShort()
op_storw(dst, res)
N = res bit 15
Z = res == 0u.toUShort()
V = (res == 0x8000u.toUShort()) and C
C = Z and C
} // ADC
0x0C80 -> { // ASR
val dst = opc_dst(opcode)
val src = op_loadw(dst).toShort()
val res = (src shr 1).toUShort()
op_storw(dst, res)
N = res bit 15
Z = res == 0.toUShort()
C = src bit 0
V = N xor C
} // ASR
0x0CC0 -> { // ASL
val dst = opc_dst(opcode)
val src = op_loadw(dst)
val res = src shl 1
op_storw(dst, res.toUShort())
N = res bit 15
Z = res == 0.toUShort()
C = src and 0x8000u != 0u.toUShort()
V = N xor C
} // ASL
0x8000, 0x8040, 0x8080, 0x80C0 -> br_rel(!N, opcode) // BPL
0x8100, 0x8140, 0x8180, 0x81C0 -> br_rel(N, opcode) // BMI
0x8200, 0x8240, 0x8280, 0x82C0 -> br_rel(!C and !Z, opcode) // BHI
0x8300, 0x8340, 0x8380, 0x83C0 -> br_rel(C or Z, opcode) // BLOS
0x8400, 0x8440, 0x8480, 0x84C0 -> br_rel(!V, opcode) // BVC
0x8500, 0x8540, 0x8580, 0x85C0 -> br_rel(V, opcode) // BVS
0x8600, 0x8640, 0x8680, 0x86C0 -> br_rel(!C, opcode) // BCC/BHIS
0x8700, 0x8740, 0x8780, 0x87C0 -> br_rel(C, opcode) // BCS/BLO
0x8A00 -> { // CLRB
op_storb(opc_dst(opcode), 0U)
N = false
V = false
C = false
Z = true
} // CLRB
0x8B40 -> { // ADCB
val dst = opc_dst(opcode)
val c: UShort = if (C) 1u else 0u
val res = (op_loadb(dst) + c).toUByte()
op_storb(dst, res)
N = res bit 7
Z = res == 0u.toUByte()
V = (res == 0x80u.toUByte()) and C
C = Z and C
} // ADCB
0x8C80 -> { // ASRB
val dst = opc_dst(opcode)
val src = op_loadb(dst).toByte()
val res = (src shr 1).toUByte()
op_storb(dst, res)
N = res bit 7
Z = res == 0.toUByte()
C = src bit 0
V = N xor C
} // ASRB
0x8CC0 -> { // ASLB
val dst = opc_dst(opcode)
val src = op_loadb(dst)
val res = (src shl 1).toUByte()
op_storw(dst, res.toUShort())
N = res bit 7
Z = res == 0.toUByte()
C = src bit 7
V = N xor C
} // ASLB
}
0x3000 -> { // BIT
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadw(dst) and op_loadw(src).inv()
N = res bit 15
Z = res != 0u.toUShort()
V = false
} // BIT
0x4000 -> { // BIC
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadw(dst) and op_loadw(src).inv()
op_storw(dst, res)
N = res bit 15
Z = res != 0u.toUShort()
V = false
} // BIC
0x5000 -> { // BIS
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadw(dst) or op_loadw(src)
op_storw(dst, res)
N = res and 0x8000u != 0u.toUShort()
Z = res != 0u.toUShort()
V = false
} // BIS
0x6000 -> { // ADD
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val srcv = op_loadw(src)
val dstv = op_loadw(dst)
val res = (srcv + dstv)
val resw = res.toUShort()
op_storw(dst, res.toUShort())
N = resw > 0x7FFFu
Z = resw == 0.toUShort()
val src_sign = srcv and 0x8000u
V = (src_sign == dstv and 0x8000u) && (src_sign != resw and 0x8000u)
C = (resw >= 0x10000u)
} // ADD
7000 -> when (opcode shr 9 and 0x7) {
2 -> { // ASH
val r = opcode shr 6 and 0x7
var count = (op_loadb(opc_src(opcode)) and 0x3Fu).toInt()
val src = registers[r].toShort().toInt() // two casts to sign extend
count = count sex 6
V = false
val res = if (count > 0) {
C = (src shl (count-1)) bit 15
val shifted = if (count < 16) {
src shr 16-count
} else {
src
}
V = (shifted != 0) and (shifted != -1)
src shl count
} else if (count < 0) {
count = -count
C = (src shr (count-1)) bit 0
src shr count
} else {
C = false
src
}
registers[r] = res.toUShort()
N = res < 0
Z = res == 0
} // ASH
3 -> { // ASHC
val r = opcode shr 6 and 0x7
var count = (op_loadb(opc_src(opcode)) and 0x3Fu).toInt() sex 6
val src = (registers[r].toUInt() shl 16 or registers[r or 1].toUInt()).toInt() // two casts to sign extend
V = false
val res = if (count > 0) {
C = (src shl (count-1)) bit 31
val shifted = src shr 32-count
V = (shifted != 0) and (shifted != -1)
src shl count
} else if (count < 0) {
count = -count
C = (src shr (count-1)) bit 0
src shr count
} else {
C = false
src
}
val resS = res.toUShort()
registers[r] = (res and 0xFFFF).toUShort()
registers[r or 1] = (res shr 16 and 0xFFFF).toUShort()
N = res < 0
Z = res == 0
} // ASHC
}
0xB000 -> { // BITB
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadb(dst) and op_loadb(src).inv()
N = res bit 7
Z = res != 0u.toUByte()
V = false
} // BITB
0xC000 -> { // BICB
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadb(dst) and op_loadb(src).inv()
op_storb(dst, res)
N = res bit 7
Z = res != 0u.toUByte()
V = false
} // BICB
0xD000 -> { // BISB
val src = opc_src(opcode)
val dst = opc_dst(opcode)
val res = op_loadb(dst) or op_loadb(src)
op_storb(dst, res)
N = res bit 7
Z = res != 0u.toUByte()
V = false
} // BISB
}
}
}

View File

@@ -0,0 +1,9 @@
package com.thequux.mcpdp.core
class InvalidOpcodeException : Exception {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}

View File

@@ -0,0 +1,5 @@
package com.thequux.mcpdp.core
class Trap(val vector: Int): Exception("TRAP#$vector") {
}

View File

@@ -0,0 +1,83 @@
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
val sign = 1UL shl n+1
return ((this and (sign.dec())) - (this and sign))
}