From 11d87e4725d290681a9dcc33eb5237eadbc6ee2f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 19 Sep 2022 16:59:44 +0200 Subject: [PATCH] VM: support cpu registers --- docs/source/todo.rst | 2 +- examples/test.p8 | 36 ++++++---- virtualmachine/src/prog8/vm/Assembler.kt | 23 +++--- virtualmachine/src/prog8/vm/Main.kt | 2 +- virtualmachine/src/prog8/vm/Registers.kt | 7 ++ virtualmachine/src/prog8/vm/VirtualMachine.kt | 72 ++++++++++++++++--- 6 files changed, 111 insertions(+), 31 deletions(-) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4aef57979..c6fc90eb9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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 diff --git a/examples/test.p8 b/examples/test.p8 index a2aeeb8db..73a08f559 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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 + }} } } diff --git a/virtualmachine/src/prog8/vm/Assembler.kt b/virtualmachine/src/prog8/vm/Assembler.kt index 45e880fa5..ffa86dc38 100644 --- a/virtualmachine/src/prog8/vm/Assembler.kt +++ b/virtualmachine/src/prog8/vm/Assembler.kt @@ -7,6 +7,7 @@ import prog8.intermediate.* class Assembler { private val symbolAddresses = mutableMapOf() private val placeholders = mutableMapOf() + 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)) } diff --git a/virtualmachine/src/prog8/vm/Main.kt b/virtualmachine/src/prog8/vm/Main.kt index 2082f0a78..352828e37 100644 --- a/virtualmachine/src/prog8/vm/Main.kt +++ b/virtualmachine/src/prog8/vm/Main.kt @@ -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() } diff --git a/virtualmachine/src/prog8/vm/Registers.kt b/virtualmachine/src/prog8/vm/Registers.kt index 8ce82e0f2..3d6ab7cfe 100644 --- a/virtualmachine/src/prog8/vm/Registers.kt +++ b/virtualmachine/src/prog8/vm/Registers.kt @@ -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(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) { diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 78b015d41..e4ff53515 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -31,7 +31,7 @@ class BreakpointException(val pc: Int): Exception() @Suppress("FunctionName") -class VirtualMachine(val memory: Memory, program: List) { +class VirtualMachine(val memory: Memory, program: List, val cx16virtualregsBaseAddress: Int) { val registers = Registers() val program: Array = program.toTypedArray() val callStack = Stack() @@ -305,14 +305,39 @@ class VirtualMachine(val memory: Memory, program: List) { } 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) { 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) } } \ No newline at end of file