diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index 4eb392159..93bcad8e4 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -68,7 +68,7 @@ class CodeGen(internal val program: PtProgram, } if(options.optimize) { - val optimizer = VmPeepholeOptimizer(vmprog, allocations) + val optimizer = VmPeepholeOptimizer(vmprog) optimizer.optimize() } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/VmPeepholeOptimizer.kt b/codeGenVirtual/src/prog8/codegen/virtual/VmPeepholeOptimizer.kt index 3609f8ef3..40cdb64b2 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/VmPeepholeOptimizer.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/VmPeepholeOptimizer.kt @@ -4,10 +4,8 @@ import prog8.vm.Instruction import prog8.vm.Opcode import prog8.vm.VmDataType -internal class VmOptimizerException(msg: String): Exception(msg) - -class VmPeepholeOptimizer(private val vmprog: AssemblyProgram, private val allocations: VariableAllocator) { +class VmPeepholeOptimizer(private val vmprog: AssemblyProgram) { fun optimize() { vmprog.getBlocks().forEach { block -> do { diff --git a/codeGenVirtual/test/TestVmPeepholeOpt.kt b/codeGenVirtual/test/TestVmPeepholeOpt.kt index 262af3396..511f5c8bd 100644 --- a/codeGenVirtual/test/TestVmPeepholeOpt.kt +++ b/codeGenVirtual/test/TestVmPeepholeOpt.kt @@ -26,19 +26,19 @@ class TestVmPeepholeOpt: FunSpec({ fun AssemblyProgram.lines(): List = this.getBlocks().flatMap { it.lines } test("remove nops") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf("dummy")), VmCodeInstruction(Opcode.NOP), VmCodeInstruction(Opcode.NOP) )) asm.lines().size shouldBe 3 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() asm.lines().size shouldBe 1 } test("remove jmp to label below") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf("label")), // removed VmCodeLabel(listOf("label")), VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf("label2")), // removed @@ -49,7 +49,7 @@ class TestVmPeepholeOpt: FunSpec({ VmCodeLabel(listOf("label3")) )) asm.lines().size shouldBe 8 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() val lines = asm.lines() lines.size shouldBe 5 @@ -61,7 +61,7 @@ class TestVmPeepholeOpt: FunSpec({ } test("remove double sec/clc") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.SEC), VmCodeInstruction(Opcode.SEC), VmCodeInstruction(Opcode.SEC), @@ -70,7 +70,7 @@ class TestVmPeepholeOpt: FunSpec({ VmCodeInstruction(Opcode.CLC) )) asm.lines().size shouldBe 6 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() val lines = asm.lines() lines.size shouldBe 1 @@ -78,14 +78,14 @@ class TestVmPeepholeOpt: FunSpec({ } test("push followed by pop") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42), VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=42), VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99), VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=222) )) asm.lines().size shouldBe 4 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() val lines = asm.lines() lines.size shouldBe 1 @@ -95,7 +95,7 @@ class TestVmPeepholeOpt: FunSpec({ } test("remove useless div/mul, add/sub") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1), VmCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1), VmCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1), @@ -108,19 +108,19 @@ class TestVmPeepholeOpt: FunSpec({ VmCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0) )) asm.lines().size shouldBe 10 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() val lines = asm.lines() lines.size shouldBe 4 } test("replace add/sub 1 by inc/dec") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1), VmCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1) )) asm.lines().size shouldBe 2 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() val lines = asm.lines() lines.size shouldBe 2 @@ -129,7 +129,7 @@ class TestVmPeepholeOpt: FunSpec({ } test("remove useless and/or/xor") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255), VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535), VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0), @@ -140,21 +140,21 @@ class TestVmPeepholeOpt: FunSpec({ VmCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1) )) asm.lines().size shouldBe 8 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() val lines = asm.lines() lines.size shouldBe 4 } test("replace and/or/xor by constant number") { - val(asm, allocations) = makeVmProgram(listOf( + val(asm, _) = makeVmProgram(listOf( VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0), VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0), VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255), VmCodeInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535) )) asm.lines().size shouldBe 4 - val opt = VmPeepholeOptimizer(asm, allocations) + val opt = VmPeepholeOptimizer(asm) opt.optimize() val lines = asm.lines() lines.size shouldBe 4 diff --git a/compiler/res/prog8lib/virtual/floats.p8 b/compiler/res/prog8lib/virtual/floats.p8 index a47aac23b..c2975511a 100644 --- a/compiler/res/prog8lib/virtual/floats.p8 +++ b/compiler/res/prog8lib/virtual/floats.p8 @@ -84,7 +84,7 @@ sub log2(float value) -> float { sub sqrt(float value) -> float { %asm {{ loadm.f fr0,{floats.sqrt.value} - fsqrt.f fr0,fr0 + sqrt.f fr0,fr0 return }} } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 6cd9810ee..33c581043 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -16,7 +16,6 @@ Need help with Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ Compiler: -- vm Instructions needs to know what the read-registers/memory are, and what the write-register/memory is. This info is needed for more advanced optimizations and later code generation steps. - vm: implement remaining sin/cos functions in math.p8 - vm: find a solution for the cx16.r0..r15 that "overlap" (r0, r0L, r0H etc) but in the vm each get their own separate variable location now - vm: encode romsub & romsub call in VM IR (but just crash in virtualmachine itself.) ExpressionGen.kt @@ -29,7 +28,7 @@ Compiler: How is it for the vm target? -> just 2 special cases in CodeGen. - when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed. It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed. - So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step. + So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step instead. - generate WASM from the new ast (or from vm code?) to run prog8 on a browser canvas? - createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there. but probably better to rewrite the 6502 codegen on top of the new Ast. diff --git a/virtualmachine/src/prog8/vm/Assembler.kt b/virtualmachine/src/prog8/vm/Assembler.kt index c47e39346..6e643644d 100644 --- a/virtualmachine/src/prog8/vm/Assembler.kt +++ b/virtualmachine/src/prog8/vm/Assembler.kt @@ -208,7 +208,7 @@ class Assembler { if(format.fpValue) floatValue = value!! - program.add(Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value = intValue, fpValue = floatValue)) + program.add(Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue)) } } diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index 476734642..c90d96e99 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -128,31 +128,31 @@ All have type b or w. andr reg1, reg2 - reg1 = reg1 bitwise and reg2 and reg1, value - reg1 = reg1 bitwise and value +andm reg1 address - memory = memory bitwise and reg1 orr reg1, reg2 - reg1 = reg1 bitwise or reg2 or reg1, value - reg1 = reg1 bitwise or value +orm reg1, address - memory = memory bitwise or reg1 xorr reg1, reg2 - reg1 = reg1 bitwise xor reg2 xor reg1, value - reg1 = reg1 bitwise xor value +xorm reg1, address - memory = memory bitwise xor reg1 inv reg1 - reg1 = bitwise invert of reg1 (all bits flipped) -lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit +invm address - memory = bitwise invert of that memory (all bits flipped) asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit +lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit -lsr reg1 - shift reg1 right by 1 bits + set Carry to shifted bit +asrnm reg1, address - multi-shift memory right by reg1 bits (signed) + set Carry to shifted bit +lsrnm reg1, address - multi-shift memoryright by reg1 bits + set Carry to shifted bit +lslnm reg1, address - multi-shift memory left by reg1 bits + set Carry to shifted bit asr reg1 - shift reg1 right by 1 bits (signed) + set Carry to shifted bit +lsr reg1 - shift reg1 right by 1 bits + set Carry to shifted bit lsl reg1 - shift reg1 left by 1 bits + set Carry to shifted bit +lsrm address - shift memory right by 1 bits + set Carry to shifted bit +asrm address - shift memory right by 1 bits (signed) + set Carry to shifted bit +lslm address - shift memory left by 1 bits + set Carry to shifted bit ror reg1 - rotate reg1 right by 1 bits, not using carry + set Carry to shifted bit roxr reg1 - rotate reg1 right by 1 bits, using carry + set Carry to shifted bit rol reg1 - rotate reg1 left by 1 bits, not using carry + set Carry to shifted bit roxl reg1 - rotate reg1 left by 1 bits, using carry, + set Carry to shifted bit -andm reg1 address - memory = memory bitwise and reg1 -orm reg1, address - memory = memory bitwise or reg1 -xorm reg1, address - memory = memory bitwise xor reg1 -invm address - memory = bitwise invert of that memory (all bits flipped) -lsrnm reg1, address - multi-shift memoryright by reg1 bits + set Carry to shifted bit -asrnm reg1, address - multi-shift memory right by reg1 bits (signed) + set Carry to shifted bit -lslnm reg1, address - multi-shift memory left by reg1 bits + set Carry to shifted bit -lsrm address - shift memory right by 1 bits + set Carry to shifted bit -asrm address - shift memory right by 1 bits (signed) + set Carry to shifted bit -lslm address - shift memory left by 1 bits + set Carry to shifted bit rorm address - rotate memory right by 1 bits, not using carry + set Carry to shifted bit roxrm address - rotate memory right by 1 bits, using carry + set Carry to shifted bit rolm address - rotate memory left by 1 bits, not using carry + set Carry to shifted bit @@ -172,6 +172,15 @@ ftosw reg1, fpreg1 - reg1 = fpreg1 as signed word fpow fpreg1, fpreg2 - fpreg1 = fpreg1 to the power of fpreg2 fabs fpreg1, fpreg2 - fpreg1 = abs(fpreg2) fcomp reg1, fpreg1, fpreg2 - reg1 = result of comparison of fpreg1 and fpreg2: 0=equal, 1=fpreg1 is greater, -1=fpreg1 is smaller +fsin fpreg1, fpreg2 - fpreg1 = sin(fpreg2) +fcos fpreg1, fpreg2 - fpreg1 = cos(fpreg2) +ftan fpreg1, fpreg2 - fpreg1 = tan(fpreg2) +fatan fpreg1, fpreg2 - fpreg1 = atan(fpreg2) +fln fpreg1, fpreg2 - fpreg1 = ln(fpreg2) ; natural logarithm +flog fpreg1, fpreg2 - fpreg1 = log(fpreg2) ; base 2 logarithm +fround fpreg1, fpreg2 - fpreg1 = round(fpreg2) +ffloor fpreg1, fpreg2 - fpreg1 = floor(fpreg2) +fceil fpreg1, fpreg2 - fpreg1 = ceil(fpreg2) MISC @@ -316,7 +325,6 @@ enum class Opcode { FATAN, FLN, FLOG, - FSQRT, FROUND, FFLOOR, FCEIL, @@ -382,6 +390,11 @@ data class Instruction( val fpValue: Float?=null, val labelSymbol: List?=null // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!) ) { + // reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT) + // This knowledge is useful in IL assembly optimizers to see how registers are used. + val reg1direction: OperandDirection + val fpReg1direction: OperandDirection + init { val formats = instructionFormats.getValue(opcode) if(type==null && !formats.containsKey(null)) @@ -410,6 +423,9 @@ data class Instruction( throw IllegalArgumentException("$opcode: integer point instruction can't use floating point registers") } + reg1direction = format.reg1direction + fpReg1direction = format.fpReg1direction + if(opcode in setOf(Opcode.BEQ, Opcode.BNE, Opcode.BLT, Opcode.BLTS, Opcode.BGT, Opcode.BGTS, Opcode.BLE, Opcode.BLES, Opcode.BGE, Opcode.BGES, @@ -464,184 +480,203 @@ data class Instruction( } } +enum class OperandDirection { + INPUT, + OUTPUT, + INOUT +} + data class InstructionFormat(val datatype: VmDataType?, - val reg1: Boolean, val reg2: Boolean, - val fpReg1: Boolean, val fpReg2: Boolean, - val value: Boolean, - val fpValue: Boolean) { + val reg1: Boolean, val reg1direction: OperandDirection, // reg1 can be IN/OUT/INOUT + val reg2: Boolean, // always only IN + val fpReg1: Boolean, val fpReg1direction: OperandDirection, // fpreg1 can be IN/OUT/INOUT + val fpReg2: Boolean, // always only IN + val value: Boolean, // always only IN + val fpValue: Boolean // always only IN + ) { companion object { fun from(spec: String): Map { val result = mutableMapOf() for(part in spec.split('|').map{ it.trim() }) { - var reg1 = false - var reg2 = false - var fpreg1 = false - var fpreg2 = false - var value = false - var fpvalue = false + var reg1 = false // read/write/modify possible + var reg1Direction = OperandDirection.INPUT + var reg2 = false // strictly read-only + var fpreg1 = false // read/write/modify possible + var fpreg1Direction = OperandDirection.INPUT + var fpreg2 = false // strictly read-only + var value = false // strictly read-only + var fpvalue = false // strictly read-only val splits = part.splitToSequence(',').iterator() val typespec = splits.next() while(splits.hasNext()) { when(splits.next()) { - "r1" -> reg1=true - "r2" -> reg2=true - "fr1" -> fpreg1=true - "fr2" -> fpreg2=true - "v" -> value = true - "fv" -> fpvalue = true + " { reg1=true; reg1Direction=OperandDirection.INPUT } + ">r1" -> { reg1=true; reg1Direction=OperandDirection.OUTPUT } + "<>r1" -> { reg1=true; reg1Direction=OperandDirection.INOUT } + " reg2 = true + " { fpreg1=true; fpreg1Direction=OperandDirection.INPUT } + ">fr1" -> { fpreg1=true; fpreg1Direction=OperandDirection.OUTPUT } + "<>fr1" -> { fpreg1=true; fpreg1Direction=OperandDirection.INOUT } + " fpreg2=true + " value = true + " fpvalue = true else -> throw IllegalArgumentException(spec) } } if(typespec=="N") - result[null] = InstructionFormat(null, reg1=reg1, reg2=reg2, fpReg1=fpreg1, fpReg2=fpreg2, value=value, fpValue=fpvalue) + result[null] = InstructionFormat(null, reg1, reg1Direction, reg2, fpreg1, fpreg1Direction, fpreg2, value, fpvalue) if('B' in typespec) - result[VmDataType.BYTE] = InstructionFormat(VmDataType.BYTE, reg1=reg1, reg2=reg2, fpReg1=fpreg1, fpReg2=fpreg2, value=value, fpValue=fpvalue) + result[VmDataType.BYTE] = InstructionFormat(VmDataType.BYTE, reg1, reg1Direction, reg2, fpreg1, fpreg1Direction, fpreg2, value, fpvalue) if('W' in typespec) - result[VmDataType.WORD] = InstructionFormat(VmDataType.WORD, reg1=reg1, reg2=reg2, fpReg1=fpreg1, fpReg2=fpreg2, value=value, fpValue=fpvalue) + result[VmDataType.WORD] = InstructionFormat(VmDataType.WORD, reg1, reg1Direction, reg2, fpreg1, fpreg1Direction, fpreg2, value, fpvalue) if('F' in typespec) - result[VmDataType.FLOAT] = InstructionFormat(VmDataType.FLOAT, reg1=reg1, reg2=reg2, fpReg1=fpreg1, fpReg2=fpreg2, value=value, fpValue=fpvalue) + result[VmDataType.FLOAT] = InstructionFormat(VmDataType.FLOAT, reg1, reg1Direction, reg2, fpreg1, fpreg1Direction, fpreg2, value, fpvalue) } return result } } } - +/* + X = X is overwritten with output value (output value) + <>X = X is modified (input + output) + TODO: also encode if memory is read/written/modified? + */ @Suppress("BooleanLiteralArgument") val instructionFormats = mutableMapOf( Opcode.NOP to InstructionFormat.from("N"), - Opcode.LOAD to InstructionFormat.from("BW,r1,v | F,fr1,fv"), - Opcode.LOADM to InstructionFormat.from("BW,r1,v | F,fr1,v"), - Opcode.LOADI to InstructionFormat.from("BW,r1,r2 | F,fr1,r1"), - Opcode.LOADX to InstructionFormat.from("BW,r1,r2,v | F,fr1,r1,v"), - Opcode.LOADIX to InstructionFormat.from("BW,r1,r2,v | F,fr1,r1,v"), - Opcode.LOADR to InstructionFormat.from("BW,r1,r2 | F,fr1,fr2"), - Opcode.STOREM to InstructionFormat.from("BW,r1,v | F,fr1,v"), - Opcode.STOREI to InstructionFormat.from("BW,r1,r2 | F,fr1,r1"), - Opcode.STOREX to InstructionFormat.from("BW,r1,r2,v | F,fr1,r1,v"), - Opcode.STOREIX to InstructionFormat.from("BW,r1,r2,v | F,fr1,r1,v"), - Opcode.STOREZM to InstructionFormat.from("BW,v | F,v"), - Opcode.STOREZI to InstructionFormat.from("BW,r1 | F,r1"), - Opcode.STOREZX to InstructionFormat.from("BW,r1,v | F,r1,v"), - Opcode.JUMP to InstructionFormat.from("N,v"), - Opcode.CALL to InstructionFormat.from("N,v"), - Opcode.SYSCALL to InstructionFormat.from("N,v"), + Opcode.LOAD to InstructionFormat.from("BW,>r1,fr1,r1,fr1,r1,fr1,r1,fr1,r1,fr1,r1,fr1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1"), + Opcode.INCM to InstructionFormat.from("BW,r1"), + Opcode.DECM to InstructionFormat.from("BW,r1 | F,<>fr1"), + Opcode.NEGM to InstructionFormat.from("BW,r1,fr1,r1,fr1,r1,fr1,r1,fr1,r1,fr1,r1,fr1,r1,r1,r1,fr1,r1,fr1,r1,fr1,r1,fr1,r1 | F,>fr1"), + Opcode.MODR to InstructionFormat.from("BW,<>r1,r1,r1"), + Opcode.EXTS to InstructionFormat.from("BW,<>r1"), + Opcode.ANDR to InstructionFormat.from("BW,<>r1,r1,r1,r1,r1,r1,r1"), + Opcode.INVM to InstructionFormat.from("BW,r1,r1,r1,r1"), + Opcode.ASRM to InstructionFormat.from("BW,r1"), + Opcode.LSRM to InstructionFormat.from("BW,r1"), + Opcode.LSLM to InstructionFormat.from("BW,r1"), + Opcode.RORM to InstructionFormat.from("BW,r1"), + Opcode.ROXRM to InstructionFormat.from("BW,r1"), + Opcode.ROLM to InstructionFormat.from("BW,r1"), + Opcode.ROXLM to InstructionFormat.from("BW,fr1,fr1,fr1,fr1,r1,r1,r1,r1,fr1,fr1,r1,fr1,fr1,fr1,fr1,fr1,fr1,fr1,fr1,fr1,r1,r1"), + Opcode.CONCAT to InstructionFormat.from("BW,<>r1,) { Opcode.FATAN -> InsFATAN(ins) Opcode.FLN -> InsFLN(ins) Opcode.FLOG -> InsFLOG(ins) - Opcode.FSQRT -> InsFSQRT(ins) Opcode.FROUND -> InsFROUND(ins) Opcode.FFLOOR -> InsFFLOOR(ins) Opcode.FCEIL -> InsFCEIL(ins) @@ -1887,12 +1886,6 @@ class VirtualMachine(val memory: Memory, program: List) { pc++ } - private fun InsFSQRT(i: Instruction) { - val value = registers.getFloat(i.fpReg2!!) - registers.setFloat(i.fpReg1!!, sqrt(value)) - pc++ - } - private fun InsFCOMP(i: Instruction) { val left = registers.getFloat(i.fpReg1!!) val right = registers.getFloat(i.fpReg2!!) diff --git a/virtualmachine/test/TestInstructions.kt b/virtualmachine/test/TestInstructions.kt index 40f8bca25..5d3abac32 100644 --- a/virtualmachine/test/TestInstructions.kt +++ b/virtualmachine/test/TestInstructions.kt @@ -2,10 +2,7 @@ import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import prog8.vm.Instruction -import prog8.vm.Opcode -import prog8.vm.VmDataType -import prog8.vm.instructionFormats +import prog8.vm.* class TestInstructions: FunSpec({ @@ -18,6 +15,8 @@ class TestInstructions: FunSpec({ ins.reg2 shouldBe null ins.value shouldBe null ins.labelSymbol shouldBe null + ins.reg1direction shouldBe OperandDirection.INPUT + ins.fpReg1direction shouldBe OperandDirection.INPUT ins.toString() shouldBe "nop" } @@ -29,6 +28,8 @@ class TestInstructions: FunSpec({ ins.reg2 shouldBe null ins.value shouldBe 9999 ins.labelSymbol shouldBe null + ins.reg1direction shouldBe OperandDirection.INPUT + ins.fpReg1direction shouldBe OperandDirection.INPUT ins.toString() shouldBe "bz.b r42,9999" } @@ -40,9 +41,36 @@ class TestInstructions: FunSpec({ ins.reg2 shouldBe null ins.value shouldBe null ins.labelSymbol shouldBe listOf("a","b","c") + ins.reg1direction shouldBe OperandDirection.INPUT + ins.fpReg1direction shouldBe OperandDirection.INPUT ins.toString() shouldBe "bz.w r11,_a.b.c" } + test("with output registers") { + val ins = Instruction(Opcode.ADDR, VmDataType.WORD, reg1=11, reg2=22) + ins.opcode shouldBe Opcode.ADDR + ins.type shouldBe VmDataType.WORD + ins.reg1 shouldBe 11 + ins.reg2 shouldBe 22 + ins.value shouldBe null + ins.labelSymbol shouldBe null + ins.reg1direction shouldBe OperandDirection.INOUT + ins.fpReg1direction shouldBe OperandDirection.INPUT + ins.toString() shouldBe "addr.w r11,r22" + + val ins2 = Instruction(Opcode.SQRT, VmDataType.BYTE, reg1=11, reg2=22) + ins2.opcode shouldBe Opcode.SQRT + ins2.type shouldBe VmDataType.BYTE + ins2.reg1 shouldBe 11 + ins2.reg2 shouldBe 22 + ins2.value shouldBe null + ins2.labelSymbol shouldBe null + ins2.reg1direction shouldBe OperandDirection.OUTPUT + ins2.fpReg1direction shouldBe OperandDirection.INPUT + ins2.toString() shouldBe "sqrt.b r11,r22" + } + + test("missing type should fail") { shouldThrow { Instruction(Opcode.BZ, reg1=42, value=9999)