Initial commit
This commit is contained in:
7
src/main/kotlin/Main.kt
Normal file
7
src/main/kotlin/Main.kt
Normal 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()}")
|
||||
}
|
8
src/main/kotlin/com/thequux/mcpdp/core/AddressSpace.kt
Normal file
8
src/main/kotlin/com/thequux/mcpdp/core/AddressSpace.kt
Normal 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)
|
||||
}
|
349
src/main/kotlin/com/thequux/mcpdp/core/CPU.kt
Normal file
349
src/main/kotlin/com/thequux/mcpdp/core/CPU.kt
Normal 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
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
5
src/main/kotlin/com/thequux/mcpdp/core/Trap.kt
Normal file
5
src/main/kotlin/com/thequux/mcpdp/core/Trap.kt
Normal file
@@ -0,0 +1,5 @@
|
||||
package com.thequux.mcpdp.core
|
||||
|
||||
class Trap(val vector: Int): Exception("TRAP#$vector") {
|
||||
|
||||
}
|
83
src/main/kotlin/com/thequux/mcpdp/ext/bit/bitext.kt
Normal file
83
src/main/kotlin/com/thequux/mcpdp/ext/bit/bitext.kt
Normal 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))
|
||||
}
|
||||
|
Reference in New Issue
Block a user