VM: support cpu registers

This commit is contained in:
Irmen de Jong 2022-09-19 16:59:44 +02:00
parent 627ed51a1b
commit 11d87e4725
6 changed files with 111 additions and 31 deletions

View File

@ -3,7 +3,7 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- IR/VM: actually support the physical cpu registers and status flags in the STORECPU and LOADCPU opcodes.
- Join codeAst and codeCore modules?
- IR: option to save IR in file
- Replace existing vm codegen by expericodegen, expericodegen just stops at saving IR in file.
- vm: implement remaining sin/cos functions in virtual/math.p8 and merge tables

View File

@ -3,19 +3,31 @@ main {
sub start() {
uword @shared slab1 = memory("slab 1", 2000, 0)
uword @shared slab2 = memory("slab 1", 2000, 0)
uword @shared slab3 = memory("other # slab", 2000, 64)
%asm {{
loadcpu.b r1,_a
loadcpu.b r1,_x
loadcpu.b r1,_y
loadcpu.w r1,_ax
loadcpu.w r1,_ay
loadcpu.w r1,_xy
loadcpu.b r1,_pc
loadcpu.b r1,_pn
loadcpu.b r1,_pz
loadcpu.w r1,_r0
loadcpu.w r1,_r15
uword total = slab1+slab2+slab3
txt.print_uw(slab1)
txt.nl()
txt.print_uw(slab2)
txt.nl()
txt.print_uw(slab3)
txt.nl()
txt.print_uw(total)
txt.nl()
storezcpu.b _a
storezcpu.b _x
storezcpu.b _y
storezcpu.w _ax
storezcpu.w _ay
storezcpu.w _xy
storezcpu.b _pc
storezcpu.b _pn
storezcpu.b _pz
storezcpu.b _r0
storezcpu.w _r15
}}
}
}

View File

@ -7,6 +7,7 @@ import prog8.intermediate.*
class Assembler {
private val symbolAddresses = mutableMapOf<String, Int>()
private val placeholders = mutableMapOf<Int, String>()
var cx16virtualregBaseAdress = 0xff02
init {
require(instructionFormats.size== Opcode.values().size) {
@ -25,6 +26,9 @@ class Assembler {
throw IllegalArgumentException("invalid line $line")
else {
val (name, addrStr, datatype, arrayspec, values) = match.destructured
if(name=="cx16.r0") {
cx16virtualregBaseAdress = addrStr.toInt()
}
val numArrayElts = if(arrayspec.isBlank()) 1 else arrayspec.substring(1, arrayspec.length-1).toInt()
var address = parseValue(Opcode.LOADCPU, addrStr, 0).toInt()
symbolAddresses[name] = address
@ -250,17 +254,18 @@ class Assembler {
floatValue = value!!
if(opcode in OpcodesForCpuRegisters) {
val reg=rest.split(',').last().lowercase()
val regStr = rest.split(',').last().lowercase().trim()
val reg = if(regStr.startsWith('_')) regStr.substring(1) else regStr
if(reg !in setOf(
"_a", "_x", "_y",
"_ax", "_ay", "_xy",
"_r0", "_r1", "_r2", "_r3",
"_r4", "_r5", "_r6", "_r7",
"_r8", "_r9", "_r10","_r11",
"_r12", "_r13", "_r14", "_r15",
"_pc", "_pz", "_pv","_pn"))
"a", "x", "y",
"ax", "ay", "xy",
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10","r11",
"r12", "r13", "r14", "r15",
"pc", "pz", "pv","pn"))
throw IllegalArgumentException("invalid cpu reg: $reg")
program.add(Instruction(opcode, type, reg1, labelSymbol = listOf(reg.substring(1))))
program.add(Instruction(opcode, type, reg1, labelSymbol = listOf(reg)))
} else {
program.add(Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue))
}

View File

@ -38,6 +38,6 @@ return"""
assembler.initializeMemory(memsrc, memory)
val program = assembler.assembleProgram(src)
val vm = VirtualMachine(memory, program)
val vm = VirtualMachine(memory, program, assembler.cx16virtualregBaseAdress)
vm.run()
}

View File

@ -3,14 +3,21 @@ package prog8.vm
/**
* 65536 virtual integer registers of 16 bits wide.
* 65536 virtual float registers of 32 bits wide.
* A,X and Y "physical" 6502 registers.
*/
class Registers {
private val registers = Array<UShort>(65536) { 0u }
private val floatRegisters = Array(65535) { 0f }
var cpuA: UByte = 0u
var cpuX: UByte = 0u
var cpuY: UByte = 0u
fun reset() {
registers.fill(0u)
floatRegisters.fill(0f)
cpuA = 0u
cpuX = 0u
cpuY = 0u
}
fun setUB(reg: Int, value: UByte) {

View File

@ -31,7 +31,7 @@ class BreakpointException(val pc: Int): Exception()
@Suppress("FunctionName")
class VirtualMachine(val memory: Memory, program: List<Instruction>) {
class VirtualMachine(val memory: Memory, program: List<Instruction>, val cx16virtualregsBaseAddress: Int) {
val registers = Registers()
val program: Array<Instruction> = program.toTypedArray()
val callStack = Stack<Int>()
@ -305,14 +305,39 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
private fun InsLOADCPU(i: Instruction) {
println("VM:TODO: load reg ${i.reg1} from cpu register ${i.labelSymbol}") // TODO
val reg = i.labelSymbol!!.single()
val value: UInt
if(reg.startsWith('r')) {
val regnum = reg.substring(1).toInt()
val regAddr = cx16virtualregsBaseAddress + regnum*2
value = memory.getUW(regAddr).toUInt()
} else {
value = when(reg) {
"a" -> registers.cpuA.toUInt()
"x" -> registers.cpuX.toUInt()
"y" -> registers.cpuY.toUInt()
"ax" -> (registers.cpuA.toUInt() shl 8) or registers.cpuX.toUInt()
"ay" -> (registers.cpuA.toUInt() shl 8) or registers.cpuY.toUInt()
"xy" -> (registers.cpuX.toUInt() shl 8) or registers.cpuY.toUInt()
"pc" -> if(statusCarry) 1u else 0u
"pz" -> if(statusZero) 1u else 0u
"pn" -> if(statusNegative) 1u else 0u
"pv" -> throw IllegalArgumentException("overflow status register not supported in VM")
else -> throw IllegalArgumentException("invalid cpu reg")
}
}
when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, value.toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, value.toUShort())
else -> throw java.lang.IllegalArgumentException("invalid cpu reg type")
}
pc++
}
private fun InsSTORECPU(i: Instruction) {
val value: UShort = when(i.type!!) {
VmDataType.BYTE -> registers.getUB(i.reg1!!).toUShort()
VmDataType.WORD -> registers.getUW(i.reg1!!)
val value: UInt = when(i.type!!) {
VmDataType.BYTE -> registers.getUB(i.reg1!!).toUInt()
VmDataType.WORD -> registers.getUW(i.reg1!!).toUInt()
VmDataType.FLOAT -> throw IllegalArgumentException("there are no float cpu registers")
}
StoreCPU(value, i.type!!, i.labelSymbol!!.single())
@ -324,8 +349,39 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun StoreCPU(value: UShort, dt: VmDataType, regStr: String) {
println("VM:TODO: store a value into cpu register $regStr") // TODO
private fun StoreCPU(value: UInt, dt: VmDataType, regStr: String) {
if(regStr.startsWith('r')) {
val regnum = regStr.substring(1).toInt()
val regAddr = cx16virtualregsBaseAddress + regnum*2
when(dt) {
VmDataType.BYTE -> memory.setUB(regAddr, value.toUByte())
VmDataType.WORD -> memory.setUW(regAddr, value.toUShort())
else -> throw IllegalArgumentException("invalid reg dt")
}
} else {
when (regStr) {
"a" -> registers.cpuA = value.toUByte()
"x" -> registers.cpuX = value.toUByte()
"y" -> registers.cpuY = value.toUByte()
"ax" -> {
registers.cpuA = (value and 255u).toUByte()
registers.cpuX = (value shr 8).toUByte()
}
"ay" -> {
registers.cpuA = (value and 255u).toUByte()
registers.cpuY = (value shr 8).toUByte()
}
"xy" -> {
registers.cpuX = (value and 255u).toUByte()
registers.cpuY = (value shr 8).toUByte()
}
"pc" -> statusCarry = value == 1u
"pz" -> statusZero = value == 1u
"pn" -> statusNegative = value == 1u
"pv" -> throw IllegalArgumentException("overflow status register not supported in VM")
else -> throw IllegalArgumentException("invalid cpu reg")
}
}
}
private fun InsLOAD(i: Instruction) {
@ -2055,7 +2111,7 @@ class VmRunner: IVirtualMachineRunner {
val assembler = Assembler()
assembler.initializeMemory(memsrc, memory)
val program = assembler.assembleProgram(programsrc)
val vm = VirtualMachine(memory, program)
val vm = VirtualMachine(memory, program, assembler.cx16virtualregBaseAdress)
vm.run(throttle = true)
}
}