diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt index 31c51136a..953c7dd35 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt @@ -4,6 +4,7 @@ import prog8.code.core.CompilationOptions import prog8.code.core.IAssemblyProgram import prog8.vm.Instruction import prog8.vm.Opcode +import prog8.vm.VmDataType import java.io.BufferedWriter import kotlin.io.path.bufferedWriter import kotlin.io.path.div @@ -51,7 +52,17 @@ internal class AssemblyProgram(override val name: String, internal sealed class VmCodeLine -internal class VmCodeInstruction(val ins: Instruction): VmCodeLine() +internal class VmCodeInstruction( + opcode: Opcode, + type: VmDataType?=null, + reg1: Int?=null, // 0-$ffff + reg2: Int?=null, // 0-$ffff + reg3: Int?=null, // 0-$ffff + value: Int?=null, // 0-$ffff + symbol: List?=null // alternative to value + ): VmCodeLine() { + val ins = Instruction(opcode, type, reg1, reg2, reg3, value, symbol) + } internal class VmCodeLabel(val name: List): VmCodeLine() internal class VmCodeComment(val comment: String): VmCodeLine() diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index b8854c108..4780f5665 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -3,7 +3,6 @@ package prog8.codegen.virtual import prog8.code.ast.PtBuiltinFunctionCall import prog8.code.ast.PtNumber import prog8.code.ast.PtString -import prog8.vm.Instruction import prog8.vm.Opcode import prog8.vm.Syscall import prog8.vm.VmDataType @@ -15,32 +14,32 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: when(call.name) { "syscall" -> { val vExpr = call.args.single() as PtNumber - code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt())) + code += VmCodeInstruction(Opcode.SYSCALL, value=vExpr.number.toInt()) } "syscall1" -> { - code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0) val callNr = (call.args[0] as PtNumber).number.toInt() code += exprGen.translateExpression(call.args[1], 0) - code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) - code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) + code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0) } "syscall2" -> { - code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) - code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1) while(codeGen.vmRegisters.peekNext()<2) { codeGen.vmRegisters.nextFree() } val callNr = (call.args[0] as PtNumber).number.toInt() code += exprGen.translateExpression(call.args[1], 0) code += exprGen.translateExpression(call.args[2], 1) - code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) - code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1)) - code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) + code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0) } "syscall3" -> { - code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) - code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)) - code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2)) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1) + code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2) while(codeGen.vmRegisters.peekNext()<3) { codeGen.vmRegisters.nextFree() } @@ -48,14 +47,14 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: code += exprGen.translateExpression(call.args[1], 0) code += exprGen.translateExpression(call.args[2], 1) code += exprGen.translateExpression(call.args[3], 2) - code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) - code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 2)) - code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1)) - code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) + code += VmCodeInstruction(Opcode.SYSCALL, value=callNr) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 2) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1) + code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0) } "msb" -> { code += exprGen.translateExpression(call.args.single(), resultRegister) - code += VmCodeInstruction(Instruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)) + code += VmCodeInstruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister) // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. } "lsb" -> { @@ -75,50 +74,50 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: } else existing.first - code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt())) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt()) } "rnd" -> { - code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value= Syscall.RND.ordinal)) + code += VmCodeInstruction(Opcode.SYSCALL, value= Syscall.RND.ordinal) if(resultRegister!=0) - code += VmCodeInstruction(Instruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)) + code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) } "peek" -> { val addressReg = codeGen.vmRegisters.nextFree() code += exprGen.translateExpression(call.args.single(), addressReg) - code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg)) + code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg) } "peekw" -> { val addressReg = codeGen.vmRegisters.nextFree() code += exprGen.translateExpression(call.args.single(), addressReg) - code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg)) + code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg) } "mkword" -> { val msbReg = codeGen.vmRegisters.nextFree() val lsbReg = codeGen.vmRegisters.nextFree() code += exprGen.translateExpression(call.args[0], msbReg) code += exprGen.translateExpression(call.args[1], lsbReg) - code += VmCodeInstruction(Instruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg)) + code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg) } else -> { TODO("builtinfunc ${call.name}") -// code += VmCodeInstruction(Instruction(Opcode.NOP)) +// code += VmCodeInstruction(Opcode.NOP)) // for (arg in call.args) { // code += translateExpression(arg, resultRegister) // code += when(arg.type) { -// in ByteDatatypes -> VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister)) -// in WordDatatypes -> VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister)) +// in ByteDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister)) +// in WordDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister)) // else -> throw AssemblyError("weird arg dt") // } // } -// code += VmCodeInstruction(Instruction(Opcode.CALL), labelArg = listOf("_prog8_builtin", call.name)) +// code += VmCodeInstruction(Opcode.CALL), labelArg = listOf("_prog8_builtin", call.name)) // for (arg in call.args) { // code += when(arg.type) { -// in ByteDatatypes -> VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1=resultRegister)) -// in WordDatatypes -> VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1=resultRegister)) +// in ByteDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=resultRegister)) +// in WordDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=resultRegister)) // else -> throw AssemblyError("weird arg dt") // } // } -// code += VmCodeInstruction(Instruction(Opcode.NOP)) +// code += VmCodeInstruction(Opcode.NOP)) } } return code diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index 8cf082f28..28c2ce50c 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -4,10 +4,8 @@ import prog8.code.StStaticVariable import prog8.code.SymbolTable import prog8.code.ast.* import prog8.code.core.* -import prog8.vm.Instruction import prog8.vm.Opcode import prog8.vm.VmDataType -import java.lang.Math.pow import kotlin.math.pow @@ -82,7 +80,7 @@ class CodeGen(internal val program: PtProgram, is PtPostIncrDecr -> translate(node) is PtRepeatLoop -> translate(node) is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName)) - is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Instruction(Opcode.BREAKPOINT))) + is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Opcode.BREAKPOINT)) is PtAddressOf, is PtContainmentCheck, is PtMemoryByte, @@ -129,14 +127,14 @@ class CodeGen(internal val program: PtProgram, val endLabel = createLabelName() if(iterableVar.dt==DataType.STR) { // iterate over a zero-terminated string - code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0) code += VmCodeLabel(loopLabel) - code += VmCodeInstruction(Instruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress)) - code += VmCodeInstruction(Instruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel)) - code += VmCodeInstruction(Instruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress)) + code += VmCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress) + code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel) + code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress) code += translateNode(forLoop.statements) - code += VmCodeInstruction(Instruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)) - code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel)) + code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg) + code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) code += VmCodeLabel(endLabel) } else { // iterate over array @@ -154,15 +152,15 @@ class CodeGen(internal val program: PtProgram, goto _loop _end: ... */ - code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)) - code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes) code += VmCodeLabel(loopLabel) - code += VmCodeInstruction(Instruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, symbol = endLabel)) - code += VmCodeInstruction(Instruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)) - code += VmCodeInstruction(Instruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)) + code += VmCodeInstruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, symbol = endLabel) + code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress) + code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress) code += translateNode(forLoop.statements) code += addConst(VmDataType.BYTE, indexReg, elementSize) - code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel)) + code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) code += VmCodeLabel(endLabel) } } @@ -176,12 +174,12 @@ class CodeGen(internal val program: PtProgram, when(value) { 0u -> { /* do nothing */ } 1u -> { - code += VmCodeInstruction(Instruction(Opcode.INC, dt, reg1=reg)) + code += VmCodeInstruction(Opcode.INC, dt, reg1=reg) } else -> { val valueReg = vmRegisters.nextFree() - code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt())) - code += VmCodeInstruction(Instruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg)) + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt()) + code += VmCodeInstruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg) } } return code @@ -194,17 +192,17 @@ class CodeGen(internal val program: PtProgram, val pow2 = powersOfTwo.indexOf(factor.toInt()) if(pow2>=1) { // just shift bits - code += VmCodeInstruction(Instruction(Opcode.LSL, dt, reg1=reg, reg2=reg, reg3=pow2)) + code += VmCodeInstruction(Opcode.LSL, dt, reg1=reg, reg2=reg, reg3=pow2) } else { when(factor) { 0u -> { - code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=reg, value=0)) + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0) } 1u -> { /* do nothing */ } else -> { val factorReg = vmRegisters.nextFree() - code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=factorReg, value=factor.toInt())) - code += VmCodeInstruction(Instruction(Opcode.MUL, dt, reg1=reg, reg2=reg, reg3=factorReg)) + code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value=factor.toInt()) + code += VmCodeInstruction(Opcode.MUL, dt, reg1=reg, reg2=reg, reg3=factorReg) } } } @@ -236,16 +234,16 @@ class CodeGen(internal val program: PtProgram, // if and else parts val elseLabel = createLabelName() val afterIfLabel = createLabelName() - code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg, symbol = elseLabel)) + code += VmCodeInstruction(branch, vmDt, reg1=conditionReg, symbol = elseLabel) code += translateNode(ifElse.ifScope) - code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = afterIfLabel)) + code += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel) code += VmCodeLabel(elseLabel) code += translateNode(ifElse.elseScope) code += VmCodeLabel(afterIfLabel) } else { // only if part val afterIfLabel = createLabelName() - code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg, symbol = afterIfLabel)) + code += VmCodeInstruction(branch, vmDt, reg1=conditionReg, symbol = afterIfLabel) code += translateNode(ifElse.ifScope) code += VmCodeLabel(afterIfLabel) } @@ -266,15 +264,15 @@ class CodeGen(internal val program: PtProgram, val resultReg = vmRegisters.nextFree() if(ident!=null) { val address = allocations.get(ident.targetName) - code += VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address)) - code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg)) - code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address)) + code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address) + code += VmCodeInstruction(operation, vmDt, reg1=resultReg) + code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address) } else if(memory!=null) { val addressReg = vmRegisters.nextFree() code += expressionEval.translateExpression(memory.address, addressReg) - code += VmCodeInstruction(Instruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg)) - code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg)) - code += VmCodeInstruction(Instruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg)) + code += VmCodeInstruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg) + code += VmCodeInstruction(operation, vmDt, reg1=resultReg) + code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg) } else if (array!=null) { TODO("postincrdecr array") } else @@ -300,8 +298,8 @@ class CodeGen(internal val program: PtProgram, val repeatLabel = createLabelName() code += VmCodeLabel(repeatLabel) code += translateNode(repeat.statements) - code += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg)) - code += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel)) + code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg) + code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel) return code } @@ -310,9 +308,9 @@ class CodeGen(internal val program: PtProgram, if(jump.address!=null) throw AssemblyError("cannot jump to memory location in the vm target") code += if(jump.generatedLabel!=null) - VmCodeInstruction(Instruction(Opcode.JUMP, symbol = listOf(jump.generatedLabel!!))) + VmCodeInstruction(Opcode.JUMP, symbol = listOf(jump.generatedLabel!!)) else if(jump.identifier!=null) - VmCodeInstruction(Instruction(Opcode.JUMP, symbol = jump.identifier!!.targetName)) + VmCodeInstruction(Opcode.JUMP, symbol = jump.identifier!!.targetName) else throw AssemblyError("weird jump") return code @@ -336,8 +334,7 @@ class CodeGen(internal val program: PtProgram, val vmDt = vmType(assignment.value.type) if(ident!=null) { val address = allocations.get(ident.targetName) - val ins = Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address) - code += VmCodeInstruction(ins) + code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address) } else if(array!=null) { val variable = array.variable.targetName @@ -347,23 +344,21 @@ class CodeGen(internal val program: PtProgram, val vmDtArrayIdx = vmType(array.type) if(fixedIndex!=null) { variableAddr += fixedIndex*itemsize - code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr)) + code += VmCodeInstruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr) } else { val indexReg = vmRegisters.nextFree() code += expressionEval.translateExpression(array.index, indexReg) - code += VmCodeInstruction(Instruction(Opcode.STOREX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr)) + code += VmCodeInstruction(Opcode.STOREX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr) } } else if(memory!=null) { - val ins = - if(memory.address is PtNumber) { - Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) - } else { - val addressRegister = vmRegisters.nextFree() - code += expressionEval.translateExpression(assignment.value, addressRegister) - Instruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister) - } - code += VmCodeInstruction(ins) + if(memory.address is PtNumber) { + code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) + } else { + val addressRegister = vmRegisters.nextFree() + code += expressionEval.translateExpression(assignment.value, addressRegister) + code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister) + } } else throw AssemblyError("weird assigntarget") @@ -377,7 +372,7 @@ class CodeGen(internal val program: PtProgram, // Call Convention: return value is always returned in r0 code += expressionEval.translateExpression(value, 0) } - code += VmCodeInstruction(Instruction(Opcode.RETURN)) + code += VmCodeInstruction(Opcode.RETURN) return code } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt index 859b1e1a7..5610fcb26 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt @@ -7,7 +7,6 @@ import prog8.code.core.AssemblyError import prog8.code.core.DataType import prog8.code.core.PassByValueDatatypes import prog8.code.core.SignedDatatypes -import prog8.vm.Instruction import prog8.vm.Opcode import prog8.vm.VmDataType @@ -21,20 +20,20 @@ internal class ExpressionGen(private val codeGen: CodeGen) { when (expr) { is PtNumber -> { - code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())) + code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt()) } is PtIdentifier -> { val mem = codeGen.allocations.get(expr.targetName) code += if(expr.type in PassByValueDatatypes) { - VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem)) + VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem) } else { // for strings and arrays etc., load the *address* of the value instead - VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)) + VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem) } } is PtAddressOf -> { val mem = codeGen.allocations.get(expr.identifier.targetName) - code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)) + code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem) } is PtMemoryByte -> { val addressRegister = codeGen.vmRegisters.nextFree() @@ -100,11 +99,11 @@ internal class ExpressionGen(private val codeGen: CodeGen) { code += translateExpression(arrayIx.index, idxReg) if(eltSize>1) { val factorReg = codeGen.vmRegisters.nextFree() - code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize)) - code += VmCodeInstruction(Instruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg)) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize) + code += VmCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg) } val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName) - code += VmCodeInstruction(Instruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation)) + code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation) return code } @@ -115,22 +114,22 @@ internal class ExpressionGen(private val codeGen: CodeGen) { when(expr.operator) { "+" -> { } "-" -> { - code += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister)) + code += VmCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister) } "~" -> { val regMask = codeGen.vmRegisters.nextFree() val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff - code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask)) - code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)) + code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask) + code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask) } "not" -> { val label = codeGen.createLabelName() - code += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label)) - code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1)) + code += VmCodeInstruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label) + code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1) code += VmCodeLabel(label) val regMask = codeGen.vmRegisters.nextFree() - code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=1)) - code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)) + code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=1) + code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask) } else -> throw AssemblyError("weird prefix operator") } @@ -165,11 +164,11 @@ internal class ExpressionGen(private val codeGen: CodeGen) { when(cast.value.type) { DataType.BYTE -> { // byte -> uword: sign extend - code += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister)) + code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister) } DataType.UBYTE -> { // ubyte -> uword: sign extend - code += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister)) + code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister) } DataType.WORD -> { } DataType.FLOAT -> { @@ -182,11 +181,11 @@ internal class ExpressionGen(private val codeGen: CodeGen) { when(cast.value.type) { DataType.BYTE -> { // byte -> word: sign extend - code += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister)) + code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister) } DataType.UBYTE -> { // byte -> word: sign extend - code += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister)) + code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister) } DataType.UWORD -> { } DataType.FLOAT -> { @@ -228,57 +227,57 @@ internal class ExpressionGen(private val codeGen: CodeGen) { val signed = binExpr.left.type in SignedDatatypes when(binExpr.operator) { "+" -> { - code += VmCodeInstruction(Instruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "-" -> { - code += VmCodeInstruction(Instruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "*" -> { - code += VmCodeInstruction(Instruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "/" -> { - code += VmCodeInstruction(Instruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "%" -> { - code += VmCodeInstruction(Instruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "|", "or" -> { - code += VmCodeInstruction(Instruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "&", "and" -> { - code += VmCodeInstruction(Instruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "^", "xor" -> { - code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "<<" -> { - code += VmCodeInstruction(Instruction(Opcode.LSL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.LSL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } ">>" -> { val opc = if(signed) Opcode.ASR else Opcode.LSR - code += VmCodeInstruction(Instruction(opc, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(opc, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "==" -> { - code += VmCodeInstruction(Instruction(Opcode.SEQ, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.SEQ, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "!=" -> { - code += VmCodeInstruction(Instruction(Opcode.SNE, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(Opcode.SNE, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "<" -> { val ins = if(signed) Opcode.SLTS else Opcode.SLT - code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } ">" -> { val ins = if(signed) Opcode.SGTS else Opcode.SGT - code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } "<=" -> { val ins = if(signed) Opcode.SLES else Opcode.SLE - code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } ">=" -> { val ins = if(signed) Opcode.SGES else Opcode.SGE - code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)) + code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg) } else -> throw AssemblyError("weird operator ${binExpr.operator}") } @@ -293,12 +292,12 @@ internal class ExpressionGen(private val codeGen: CodeGen) { code += translateExpression(arg, argReg) val vmDt = codeGen.vmType(parameter.type) val mem = codeGen.allocations.get(fcall.functionName + parameter.name) - code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem)) + code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem) } - code += VmCodeInstruction(Instruction(Opcode.CALL, symbol=fcall.functionName)) + code += VmCodeInstruction(Opcode.CALL, symbol=fcall.functionName) if(!fcall.void && resultRegister!=0) { // Call convention: result value is in r0, so put it in the required register instead. TODO does this work correctly? - code += VmCodeInstruction(Instruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0)) + code += VmCodeInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0) } return code } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 71321dcdf..dc0383359 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- vm: memory: reading/writing of words seems buggy. add tests+fix - x16: check additional FP lib changes https://github.com/commanderx16/x16-rom/commit/ae608673f0210953172d6837acfbb231d62ddbd1 and https://github.com/commanderx16/x16-docs/commit/21238aedc641da91df88e04c4ce9bf3324a3c12d - x16: check joystick support (petaxian, own stuff) because of api change in r39 kernal https://github.com/commanderx16/x16-docs/blob/master/Commander%20X16%20Programmer's%20Reference%20Guide.md#function-name-joystick_get diff --git a/examples/test.p8 b/examples/test.p8 index 1a2e8b1bc..d666cb56c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -24,8 +24,9 @@ main { } txt.print_uw(ww) ; 8 txt.nl() + txt.nl() - for wc in [4096,8192,16384] { + for wc in [4097,8193,16385] { txt.print_uw(wc) txt.spc() ww++ @@ -33,19 +34,24 @@ main { txt.print_uw(ww) ; 11 txt.nl() -; for bc in 10 to 20 step 3 { -; ; 10,13,16,19 -> 4 -; ww++ -; } -; txt.print_uw(ww) -; txt.nl() -; for bc in 30 to 10 step -4 { -; ; 30,26,22,18,14,10,6,2 -> +8 -> 12 -; ww++ -; } -; txt.print_uw(ww) -; txt.nl() -; + for bc in 10 to 20 step 3 { + ; 10,13,16,19 + txt.print_ub(bc) + txt.spc() + ww++ + } + txt.print_uw(ww) ; 15 + txt.nl() + + for bc in 30 to 10 step -4 { + ; 30,26,22,18,14,10,6,2 + txt.print_ub(bc) + txt.spc() + ww++ + } + txt.print_uw(ww) ; 23 + txt.nl() + sys.exit(99) diff --git a/virtualmachine/build.gradle b/virtualmachine/build.gradle index febb4f22b..6bc0802d7 100644 --- a/virtualmachine/build.gradle +++ b/virtualmachine/build.gradle @@ -3,6 +3,7 @@ plugins { id 'java' id 'application' id "org.jetbrains.kotlin.jvm" + id "io.kotest" version "0.3.9" } java { @@ -26,6 +27,7 @@ compileTestKotlin { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14" + testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.1.0' } sourceSets { @@ -37,6 +39,23 @@ sourceSets { srcDirs = ["${project.projectDir}/res"] } } + + test { + java { + srcDir "${project.projectDir}/test" + } + } } -// note: there are no unit tests in this module! +test { + // Enable JUnit 5 (Gradle 4.6+). + useJUnitPlatform() + + // Always run tests, even when nothing changed. + dependsOn 'cleanTest' + + // Show test results. + testLogging { + events "skipped", "failed" + } +} diff --git a/virtualmachine/src/prog8/vm/Assembler.kt b/virtualmachine/src/prog8/vm/Assembler.kt index f5c3860e0..ae6ee81d5 100644 --- a/virtualmachine/src/prog8/vm/Assembler.kt +++ b/virtualmachine/src/prog8/vm/Assembler.kt @@ -40,7 +40,7 @@ class Assembler { val array = values.split(',').map { parseValue(it.trim(), 0) } for (value in array) { memory.setUW(address, value.toUShort()) - address++ + address += 2 } } else -> throw IllegalArgumentException("mem instr $what") diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index e0ddaecb3..54367697c 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -33,7 +33,6 @@ loadm reg1, address - load reg1 with value in memory address loadi reg1, reg2 - load reg1 with value in memory indirect, memory pointed to by reg2 loadx reg1, reg2, address - load reg1 with value in memory address, indexed by value in reg2 loadr reg1, reg2 - load reg1 with value in register reg2 -swapreg reg1, reg2 - swap values in reg1 and reg2 storem reg1, address - store reg1 in memory address storei reg1, reg2 - store reg1 in memory indirect, memory pointed to by reg2 @@ -130,7 +129,8 @@ nop - do nothing breakpoint - trigger a breakpoint copy reg1, reg2, length - copy memory from ptrs in reg1 to reg3, length bytes copyz reg1, reg2 - copy memory from ptrs in reg1 to reg3, stop after first 0-byte -swap [b, w] reg1, reg2 - reg1 = swapped lsb and msb from register reg2 (16 bits) or lsw and msw (32 bits) +swap [b, w] reg1 - swap lsb and msb in register reg1 (16 bits) or lsw and msw (32 bits) +swapreg reg1, reg2 - swap values in reg1 and reg2 concat [b, w] reg1, reg2, reg3 - reg1 = concatenated lsb/lsw of reg2 and lsb/lsw of reg3 into new word or int (int not yet implemented, requires 32bits regs) push [b, w] reg1 - push value in reg1 on the stack pop [b, w] reg1 - pop value from stack into reg1 @@ -227,8 +227,17 @@ data class Instruction( ) { override fun toString(): String { val result = mutableListOf(opcode.name.lowercase()) - if(instructionFormats.getValue(opcode).datatypes.isNotEmpty() && type==null) - throw IllegalArgumentException("opcode $opcode requires type") + val format = instructionFormats.getValue(opcode) + if(format.datatypes.isNotEmpty() && type==null) + throw IllegalArgumentException("missing type") + + if(format.reg1 && reg1==null || + format.reg2 && reg2==null || + format.reg3 && reg3==null) + throw IllegalArgumentException("missing a register") + + if(format.value && (value==null && symbol==null)) + throw IllegalArgumentException("missing a value or symbol") when(type) { VmDataType.BYTE -> result.add(".b ") @@ -332,7 +341,7 @@ val instructionFormats = mutableMapOf( Opcode.COPY to InstructionFormat(NN, true, true, false, true ), Opcode.COPYZ to InstructionFormat(NN, true, true, false, false), - Opcode.SWAP to InstructionFormat(BW, true, true, false, false), + Opcode.SWAP to InstructionFormat(BW, true, false, false, false), Opcode.PUSH to InstructionFormat(BW, true, false, false, false), Opcode.POP to InstructionFormat(BW, true, false, false, false), Opcode.CONCAT to InstructionFormat(BW, true, true, true, false), diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 0942d81a9..97dcfcb58 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -36,7 +36,8 @@ enum class Syscall { GFX_PLOT, RND, WAIT, - WAITVSYNC + WAITVSYNC, + TMP_PRINT_UW } object SysCalls { @@ -91,6 +92,9 @@ object SysCalls { Thread.sleep(millis) } Syscall.WAITVSYNC -> vm.waitvsync() + Syscall.TMP_PRINT_UW -> { + println("VM: UW=${vm.registers.getUW(0)}") + } else -> TODO("syscall ${call.name}") } } diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 5448d9500..851c493d2 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -694,9 +694,9 @@ class VirtualMachine(val memory: Memory, program: List) { private fun InsSWAP(i: Instruction) { when(i.type!!) { VmDataType.BYTE -> { - val value = registers.getUW(i.reg2!!) - val newValue = value.toUByte()*256u + (value.toInt() ushr 8).toUInt() - registers.setUW(i.reg1!!, newValue.toUShort()) + val value = registers.getUW(i.reg1!!) + val newValue = 0xea31 // value.toUByte()*256u + (value.toInt() ushr 8).toUInt() + registers.setUW(i.reg1, newValue.toUShort()) } VmDataType.WORD -> TODO("swap.w requires 32-bits registers") } diff --git a/virtualmachine/test/TestInstructions.kt b/virtualmachine/test/TestInstructions.kt new file mode 100644 index 000000000..d81593f1c --- /dev/null +++ b/virtualmachine/test/TestInstructions.kt @@ -0,0 +1,75 @@ +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 + + +class TestInstructions: FunSpec({ + + test("simple") { + val ins = Instruction(Opcode.NOP) + ins.opcode shouldBe Opcode.NOP + ins.type shouldBe null + ins.reg1 shouldBe null + ins.reg2 shouldBe null + ins.reg3 shouldBe null + ins.value shouldBe null + ins.symbol shouldBe null + ins.toString() shouldBe "nop" + } + + test("with value") { + val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42, value = 9999) + ins.opcode shouldBe Opcode.BZ + ins.type shouldBe VmDataType.BYTE + ins.reg1 shouldBe 42 + ins.reg2 shouldBe null + ins.reg3 shouldBe null + ins.value shouldBe 9999 + ins.symbol shouldBe null + ins.toString() shouldBe "bz.b r42,9999" + } + + test("with label") { + val ins = Instruction(Opcode.BZ, VmDataType.WORD, reg1=11, reg2=22, reg3=33, symbol = listOf("a","b","c")) + ins.opcode shouldBe Opcode.BZ + ins.type shouldBe VmDataType.WORD + ins.reg1 shouldBe 11 + ins.reg2 shouldBe 22 + ins.reg3 shouldBe 33 + ins.value shouldBe null + ins.symbol shouldBe listOf("a","b","c") + ins.toString() shouldBe "bz.w r11,r22,r33,_a.b.c" + } + + test("missing type should fail") { + val ins = Instruction(Opcode.BZ, reg1=42, value=9999) + shouldThrow { + ins.toString() + } + } + + test("missing registers should fail") { + val ins = Instruction(Opcode.BZ, VmDataType.BYTE, value=9999) + shouldThrow { + ins.toString() + } + } + + test("missing value should fail") { + val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42) + shouldThrow { + ins.toString() + } + } + + test("all instructionformats") { + Opcode.values().forEach { + instructionFormats[it] shouldNotBe null + } + } +}) diff --git a/virtualmachine/test/TestMemory.kt b/virtualmachine/test/TestMemory.kt new file mode 100644 index 000000000..47edd5cba --- /dev/null +++ b/virtualmachine/test/TestMemory.kt @@ -0,0 +1,67 @@ +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import prog8.vm.Memory + + +class TestMemory: FunSpec({ + + test("reset") { + val mem = Memory() + mem.setUB(1000, 42u) + mem.getUB(1000) shouldBe 42u + mem.reset() + mem.getUB(1000) shouldBe 0u + } + + test("byte access") { + val mem = Memory() + mem.setUB(1000, 123u) + mem.getUB(1000) shouldBe 123u + mem.getSB(1000) shouldBe 123 + mem.setUB(1000, 234u) + mem.getUB(1000) shouldBe 234u + mem.getSB(1000) shouldBe -22 + mem.setSB(1000, -99) + mem.getSB(1000) shouldBe -99 + mem.getUB(1000) shouldBe 157u + } + + test("word access") { + val mem = Memory() + mem.setUW(1000, 12345u) + mem.getUW(1000) shouldBe 12345u + mem.getSW(1000) shouldBe 12345 + mem.setUW(1000, 55444u) + mem.getUW(1000) shouldBe 55444u + mem.getSW(1000) shouldBe -10092 + mem.setSW(1000, -23456) + mem.getSW(1000) shouldBe -23456 + mem.getUW(1000) shouldBe 42080u + + mem.setUW(1000, 0xea31u) + mem.getUB(1000) shouldBe 0x31u + mem.getUB(1001) shouldBe 0xeau + } + + test("setstring and getstring") { + val mem = Memory() + mem.setString(1000, "******************", false) + mem.setString(1000, "Hello world!", true) + mem.getString(1000) shouldBe "Hello world!" + mem.getUB(1012) shouldBe 0u + mem.getUB(1013) shouldBe 42u + mem.setString(1000, "Goodbye", false) + mem.getString(1000) shouldBe "Goodbyeorld!" + } + + test("illegal address") { + val mem = Memory() + shouldThrow { + mem.getUB(9999999) + } + shouldThrow { + mem.setUB(9999999, 0u) + } + } +}) diff --git a/virtualmachine/test/TestRegisters.kt b/virtualmachine/test/TestRegisters.kt new file mode 100644 index 000000000..bffb50ed5 --- /dev/null +++ b/virtualmachine/test/TestRegisters.kt @@ -0,0 +1,41 @@ +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import prog8.vm.Registers + + +class TestRegisters: FunSpec({ + + test("reset") { + val regs = Registers() + regs.setUB(1, 42u) + regs.getUB(1) shouldBe 42u + regs.reset() + regs.getUB(1) shouldBe 0u + } + + test("byte access") { + val regs = Registers() + regs.setUB(1000, 123u) + regs.getUB(1000) shouldBe 123u + regs.getSB(1000) shouldBe 123 + regs.setUB(1000, 234u) + regs.getUB(1000) shouldBe 234u + regs.getSB(1000) shouldBe -22 + regs.setSB(1000, -99) + regs.getSB(1000) shouldBe -99 + regs.getUB(1000) shouldBe 157u + } + + test("word access") { + val regs = Registers() + regs.setUW(1000, 12345u) + regs.getUW(1000) shouldBe 12345u + regs.getSW(1000) shouldBe 12345 + regs.setUW(1000, 55444u) + regs.getUW(1000) shouldBe 55444u + regs.getSW(1000) shouldBe -10092 + regs.setSW(1000, -23456) + regs.getSW(1000) shouldBe -23456 + regs.getUW(1000) shouldBe 42080u + } +}) diff --git a/virtualmachine/virtualmachine.iml b/virtualmachine/virtualmachine.iml index 132600e11..26aa43e4d 100644 --- a/virtualmachine/virtualmachine.iml +++ b/virtualmachine/virtualmachine.iml @@ -4,10 +4,13 @@ + + + \ No newline at end of file