UEmu/MicroEmu.kt
2019-04-08 12:38:17 +02:00

341 lines
12 KiB
Kotlin

class MachineState {
val programMemory = ShortArray(256) // PM
val microMemory = IntArray(128) // MyM
val k1 = ByteArray(16) // K1
val k2 = ByteArray(4) // K2
var registers = Registers()
var flag_Z: Boolean = false
var flag_N: Boolean = false
var flag_O: Boolean = false
var flag_C: Boolean = false
var flag_L: Boolean = false
var cycleCount: Long = 0
var halted: Boolean = false
fun step(){
if(halted) return
++cycleCount
val instr = microMemory[registers.uPC.toIndexValue()]
val instr_ALU = instr ushr 21 onlyBits 4
val instr_TB = instr ushr 18 onlyBits 3
val instr_FB = instr ushr 15 onlyBits 3
val instr_S = instr.getBitAt(14)
val instr_P = instr.getBitAt(13)
val instr_LC = instr ushr 11 onlyBits 2
val instr_SEQ = instr ushr 7 onlyBits 4
val instr_ADR = instr onlyBits 7
val pInstr = Instruction.parse(registers.ir)
val mux_select = if(instr_S) pInstr.M else pInstr.GRx
val gr = when(mux_select.toInt()){
0 -> registers.gr0
1 -> registers.gr1
2 -> registers.gr2
else -> registers.gr3
}
val newRegs = registers.copy()
val bus_to = when(instr_TB){
0 -> 0xFFFF.toShort()
1 -> registers.ir
2 -> programMemory[registers.asr.toIndexValue()]
3 -> registers.pc.toMaskedShort()
4 -> registers.ar
5 -> registers.hr
6 -> gr
else -> instr.onlyBits(16).toShort() // Special TB-mode where uADR is copied to bus
}
// Do non-ALU things here
if(instr_TB != 7){
when(instr_FB){
1 -> newRegs.ir = bus_to
2 -> programMemory[registers.asr.toIndexValue()] = bus_to
3 -> newRegs.pc = bus_to.toProperType(newRegs.pc)
5 -> newRegs.hr = bus_to
6 -> when(mux_select.toInt()){
0 -> newRegs.gr0 = bus_to
1 -> newRegs.gr1 = bus_to
2 -> newRegs.gr2 = bus_to
else -> newRegs.gr3 = bus_to
}
7 -> newRegs.asr = bus_to.toProperType(newRegs.asr)
}
if(instr_P) newRegs.pc = (registers.pc + 1).toByte()
when(instr_LC){
1 -> newRegs.lc = (registers.lc - 1).toByte()
2 -> newRegs.lc = bus_to.onlyBits(8).toByte()
3 -> newRegs.lc = instr.onlyBits(8).toByte()
}
fun jumpIf(cond: Boolean) =
if(cond) instr_ADR.toByte()
else (registers.uPC + 1).toByte()
newRegs.uPC = when(instr_SEQ){
0 -> (registers.uPC + 1).toByte()
1 -> k1[pInstr.OP.toIndexValue()]
2 -> k2[pInstr.M.toIndexValue()]
3 -> 0.toByte()
4 -> jumpIf(!flag_Z)
5 -> instr_ADR.toByte()
6 -> {
newRegs.uSP = registers.uPC
instr_ADR.toByte()
}
7 -> registers.uSP
8 -> jumpIf(flag_Z)
9 -> jumpIf(flag_N)
10 -> jumpIf(flag_C)
11 -> jumpIf(flag_O)
12 -> jumpIf(flag_L)
13 -> jumpIf(!flag_C)
14 -> jumpIf(!flag_O)
else -> {
halted = true
0.toByte()
}
}
}
newRegs.ar = when(instr_ALU){
0 -> registers.ar
1 -> bus_to
2 -> bus_to.inv()
3 ->{
flag_Z = true
flag_N = false
0
}
4 ->{
val result = (registers.ar + bus_to).toShort()
flag_Z = result == 0.toShort()
flag_N = result < 0
flag_C = result <= registers.ar
flag_O = (result.getBitAt(7) == bus_to.getBitAt(7)) && (registers.ar.getBitAt(7) != result.getBitAt(7))
result
}
5 ->{
val result = (registers.ar + bus_to).toShort()
flag_Z = result == 0.toShort()
flag_N = result < 0
flag_C = result <= registers.ar
result
}
6 ->{
val result = registers.ar.toInt().and(bus_to.toInt()).toShort()
flag_Z = result == 0.toShort()
flag_N = result < 0
result
}
7 ->{
val result = registers.ar.toInt().or(bus_to.toInt()).toShort()
flag_Z = result == 0.toShort()
flag_N = result < 0
result
}
8 -> (registers.ar + bus_to).toShort()
9 ->{
val result = registers.ar.toInt().shl(1).toShort()
flag_C = registers.ar.getBitAt(7)
flag_Z = result == 0.toShort()
flag_N = result < 0
result
}
10 ->{
val result = registers.ar.toInt().shl(16).or(registers.hr.toInt().onlyBits(16)).shl(1)
newRegs.hr = result.onlyBits(16).toShort()
flag_C = registers.ar.getBitAt(7)
flag_Z = result == 0
flag_N = result < 0
result.ushr(16).toShort()
}
11 ->{
val result = registers.ar.toInt().shr(1).toShort()
flag_C = registers.ar.getBitAt(0)
flag_Z = result == 0.toShort()
flag_N = result < 0
result
}
12 ->{
val result = registers.ar.toInt().shl(16).or(registers.hr.toInt().onlyBits(16)).shr(1)
newRegs.hr = result.onlyBits(16).toShort()
flag_C = registers.hr.getBitAt(0)
flag_Z = result == 0
flag_N = result < 0
result.ushr(16).toShort()
}
13 ->{
val result = registers.ar.toInt().ushr(1).toShort()
flag_C = registers.ar.getBitAt(0)
flag_Z = result == 0.toShort()
flag_N = result < 0
result
}
14 ->{
val result = registers.ar.toInt().shl(1).or(registers.ar.toInt().ushr(15)).toShort()
flag_C = registers.ar.getBitAt(7)
flag_Z = result == 0.toShort()
flag_N = result < 0
result
}
else ->{
val arhr = registers.ar.toInt().shl(16).or(registers.hr.toInt().onlyBits(16))
val result = arhr.shl(1).or(arhr.ushr(31)).toShort()
flag_C = registers.hr.getBitAt(0)
flag_Z = result == 0.toShort()
flag_N = result < 0
result
}
}
flag_L = newRegs.lc == 0.toByte()
registers = newRegs
}
data class Instruction(
var OP: Byte = 0,
var GRx: Byte = 0,
var M: Byte = 0,
var adr: Byte = 0
){
companion object {
fun parse(irVal: Short) = Instruction(
(irVal.toInt() ushr 12 and 15).toByte(),
(irVal.toInt() ushr 10 and 3).toByte(),
(irVal.toInt() ushr 8 and 3).toByte(),
(irVal.toInt() and 255).toByte()
)
}
}
data class Registers(
var pc: Byte = 0, // PC
var asr: Byte = 0, // ASR
var ar: Short = 0, // AR
var hr: Short = 0, // HR
var gr0: Short = 0, // GR0
var gr1: Short = 0, // GR1
var gr2: Short = 0, // GR2
var gr3: Short = 0, // GR3
var ir: Short = 0, // IR
var uPC: Byte = 0, // MyPC
var uSP: Byte = 0, // SMyPC
var lc: Byte = 0 // LC
){
fun copy() = Registers(pc, asr, ar, hr, gr0, gr1, gr2, gr3, ir, uPC, uSP, lc)
}
// Stolen from my (as of yet unpublished) (u)code state weaver
companion object {
fun parseState(rawState: String): MachineState {
val state = MachineState()
val lines = rawState.replace("\r", "").split("\n").toTypedArray()
// Read PM
for(index in 0 until 256)
state.programMemory[index] = Integer.parseInt(lines[index + 1].substring(4), 16).toShort()
// Read MyM
for(index in 0 until 128)
state.microMemory[index] = Integer.parseInt(lines[index + 3 + 256].substring(4), 16)
// Read K1
for(index in 0 until 16)
state.k1[index] = Integer.parseInt(lines[index + 5 + 256 + 128].substring(4), 16).toByte()
// Read K2
for(index in 0 until 4)
state.k2[index] = Integer.parseInt(lines[index + 7 + 256 + 128 + 16].substring(4), 16).toByte()
state.registers.pc = Integer.parseInt(lines[413], 16).toByte()
state.registers.asr = Integer.parseInt(lines[416], 16).toByte()
state.registers.ar = Integer.parseInt(lines[419], 16).toShort()
state.registers.hr = Integer.parseInt(lines[422], 16).toShort()
state.registers.gr0 = Integer.parseInt(lines[425], 16).toShort()
state.registers.gr1 = Integer.parseInt(lines[428], 16).toShort()
state.registers.gr2 = Integer.parseInt(lines[431], 16).toShort()
state.registers.gr3 = Integer.parseInt(lines[434], 16).toShort()
state.registers.ir = Integer.parseInt(lines[437].substring(1), 2).toShort()
state.registers.uPC = Integer.parseInt(lines[440], 16).toByte()
state.registers.uSP = Integer.parseInt(lines[443], 16).toByte()
state.registers.lc = Integer.parseInt(lines[446], 16).toByte()
return state
}
}
}
fun Short.inv() = toInt().inv().onlyBits(16).toShort()
fun Short.onlyBits(bits: Int) = toInt().onlyBits(bits).toShort()
fun Byte.toMaskedShort() = toProperType(0.toShort())
fun Short.toProperType(other: Short) = this
fun Short.toProperType(other: Byte) = toInt().and(0xFF).toByte()
fun Byte.toProperType(other: Short) = this.toShort()
fun Byte.toProperType(other: Byte) = this
fun Byte.toIndexValue() = toInt() and 0xFF
fun Int.getBitAt(index: Int) = ushr(index).and(1) == 1
fun Short.getBitAt(index: Int) = toInt().ushr(index).and(1) == 1
infix fun Int.onlyBits(bits: Int) = and(-1 ushr (32 - bits))
fun main(args: Array<String>){
val state = if(args.size > 0){
val file = java.io.File(args[0])
if(!file.isFile){
System.err.println("Not a file :(")
System.exit(1)
}
try{
MachineState.parseState(file.readText())
}catch(e: NumberFormatException){
System.err.println("Bad file format :(")
System.exit(2)
null
}
}else MachineState()
state!!
val scanner = java.util.Scanner(System.`in`)
val escape: Char = 0x1B.toChar()
print("$escape[2J")
System.out.flush()
while(!state.halted){
print("$escape[1;1H")
System.out.flush()
println("REGISTERS:")
for(field in state.registers::class.java.declaredFields){
field.isAccessible = true
if(field.isSynthetic) continue
println("${field.name.toUpperCase()}:\t0x${field.get(state.registers).asShortHex()}")
}
state.step()
println("\nPress [RETURN] to step once...")
scanner.nextLine()
}
}
fun Any?.asShortHex(): String {
if(this == null) return "null"
val num = if(this is Int) this
else if(this is Short) this.toInt().and(0xFFFF)
else if(this is Byte) this.toInt().and(0xFF)
else null
if(num == null) return this.toString()
return num.toLong().or(1 shl 62).toString(16).substring(6, 8)
}