working on vm

This commit is contained in:
Irmen de Jong
2022-03-30 21:44:48 +02:00
parent f46300016d
commit 0dc592b819
15 changed files with 372 additions and 145 deletions

View File

@@ -4,6 +4,7 @@ import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram import prog8.code.core.IAssemblyProgram
import prog8.vm.Instruction import prog8.vm.Instruction
import prog8.vm.Opcode import prog8.vm.Opcode
import prog8.vm.VmDataType
import java.io.BufferedWriter import java.io.BufferedWriter
import kotlin.io.path.bufferedWriter import kotlin.io.path.bufferedWriter
import kotlin.io.path.div import kotlin.io.path.div
@@ -51,7 +52,17 @@ internal class AssemblyProgram(override val name: String,
internal sealed class VmCodeLine 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<String>?=null // alternative to value
): VmCodeLine() {
val ins = Instruction(opcode, type, reg1, reg2, reg3, value, symbol)
}
internal class VmCodeLabel(val name: List<String>): VmCodeLine() internal class VmCodeLabel(val name: List<String>): VmCodeLine()
internal class VmCodeComment(val comment: String): VmCodeLine() internal class VmCodeComment(val comment: String): VmCodeLine()

View File

@@ -3,7 +3,6 @@ package prog8.codegen.virtual
import prog8.code.ast.PtBuiltinFunctionCall import prog8.code.ast.PtBuiltinFunctionCall
import prog8.code.ast.PtNumber import prog8.code.ast.PtNumber
import prog8.code.ast.PtString import prog8.code.ast.PtString
import prog8.vm.Instruction
import prog8.vm.Opcode import prog8.vm.Opcode
import prog8.vm.Syscall import prog8.vm.Syscall
import prog8.vm.VmDataType import prog8.vm.VmDataType
@@ -15,32 +14,32 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
when(call.name) { when(call.name) {
"syscall" -> { "syscall" -> {
val vExpr = call.args.single() as PtNumber 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" -> { "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() val callNr = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], 0) code += exprGen.translateExpression(call.args[1], 0)
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
} }
"syscall2" -> { "syscall2" -> {
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)) code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)
while(codeGen.vmRegisters.peekNext()<2) { while(codeGen.vmRegisters.peekNext()<2) {
codeGen.vmRegisters.nextFree() codeGen.vmRegisters.nextFree()
} }
val callNr = (call.args[0] as PtNumber).number.toInt() val callNr = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], 0) code += exprGen.translateExpression(call.args[1], 0)
code += exprGen.translateExpression(call.args[2], 1) code += exprGen.translateExpression(call.args[2], 1)
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1)) code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1)
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
} }
"syscall3" -> { "syscall3" -> {
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)) code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2)) code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2)
while(codeGen.vmRegisters.peekNext()<3) { while(codeGen.vmRegisters.peekNext()<3) {
codeGen.vmRegisters.nextFree() 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[1], 0)
code += exprGen.translateExpression(call.args[2], 1) code += exprGen.translateExpression(call.args[2], 1)
code += exprGen.translateExpression(call.args[3], 2) code += exprGen.translateExpression(call.args[3], 2)
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 2)) code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 2)
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1)) code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1)
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
} }
"msb" -> { "msb" -> {
code += exprGen.translateExpression(call.args.single(), resultRegister) 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. // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
} }
"lsb" -> { "lsb" -> {
@@ -75,50 +74,50 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
} }
else else
existing.first 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" -> { "rnd" -> {
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value= Syscall.RND.ordinal)) code += VmCodeInstruction(Opcode.SYSCALL, value= Syscall.RND.ordinal)
if(resultRegister!=0) 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" -> { "peek" -> {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg) 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" -> { "peekw" -> {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg) 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" -> { "mkword" -> {
val msbReg = codeGen.vmRegisters.nextFree() val msbReg = codeGen.vmRegisters.nextFree()
val lsbReg = codeGen.vmRegisters.nextFree() val lsbReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], msbReg) code += exprGen.translateExpression(call.args[0], msbReg)
code += exprGen.translateExpression(call.args[1], lsbReg) 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 -> { else -> {
TODO("builtinfunc ${call.name}") TODO("builtinfunc ${call.name}")
// code += VmCodeInstruction(Instruction(Opcode.NOP)) // code += VmCodeInstruction(Opcode.NOP))
// for (arg in call.args) { // for (arg in call.args) {
// code += translateExpression(arg, resultRegister) // code += translateExpression(arg, resultRegister)
// code += when(arg.type) { // code += when(arg.type) {
// in ByteDatatypes -> VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister)) // in ByteDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister))
// in WordDatatypes -> VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister)) // in WordDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister))
// else -> throw AssemblyError("weird arg dt") // 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) { // for (arg in call.args) {
// code += when(arg.type) { // code += when(arg.type) {
// in ByteDatatypes -> VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1=resultRegister)) // in ByteDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=resultRegister))
// in WordDatatypes -> VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1=resultRegister)) // in WordDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=resultRegister))
// else -> throw AssemblyError("weird arg dt") // else -> throw AssemblyError("weird arg dt")
// } // }
// } // }
// code += VmCodeInstruction(Instruction(Opcode.NOP)) // code += VmCodeInstruction(Opcode.NOP))
} }
} }
return code return code

View File

@@ -4,10 +4,8 @@ import prog8.code.StStaticVariable
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.vm.Instruction
import prog8.vm.Opcode import prog8.vm.Opcode
import prog8.vm.VmDataType import prog8.vm.VmDataType
import java.lang.Math.pow
import kotlin.math.pow import kotlin.math.pow
@@ -82,7 +80,7 @@ class CodeGen(internal val program: PtProgram,
is PtPostIncrDecr -> translate(node) is PtPostIncrDecr -> translate(node)
is PtRepeatLoop -> translate(node) is PtRepeatLoop -> translate(node)
is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName)) is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName))
is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Instruction(Opcode.BREAKPOINT))) is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Opcode.BREAKPOINT))
is PtAddressOf, is PtAddressOf,
is PtContainmentCheck, is PtContainmentCheck,
is PtMemoryByte, is PtMemoryByte,
@@ -129,14 +127,14 @@ class CodeGen(internal val program: PtProgram,
val endLabel = createLabelName() val endLabel = createLabelName()
if(iterableVar.dt==DataType.STR) { if(iterableVar.dt==DataType.STR) {
// iterate over a zero-terminated string // 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 += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Instruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress)) code += VmCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress)
code += VmCodeInstruction(Instruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel)) code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel)
code += VmCodeInstruction(Instruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress)) code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress)
code += translateNode(forLoop.statements) code += translateNode(forLoop.statements)
code += VmCodeInstruction(Instruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)) code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel)) code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
code += VmCodeLabel(endLabel) code += VmCodeLabel(endLabel)
} else { } else {
// iterate over array // iterate over array
@@ -154,15 +152,15 @@ class CodeGen(internal val program: PtProgram,
goto _loop goto _loop
_end: ... _end: ...
*/ */
code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)) code += VmCodeInstruction(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=lengthReg, value=lengthBytes)
code += VmCodeLabel(loopLabel) code += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Instruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, symbol = endLabel)) code += VmCodeInstruction(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(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)
code += VmCodeInstruction(Instruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)) code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)
code += translateNode(forLoop.statements) code += translateNode(forLoop.statements)
code += addConst(VmDataType.BYTE, indexReg, elementSize) code += addConst(VmDataType.BYTE, indexReg, elementSize)
code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel)) code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
code += VmCodeLabel(endLabel) code += VmCodeLabel(endLabel)
} }
} }
@@ -176,12 +174,12 @@ class CodeGen(internal val program: PtProgram,
when(value) { when(value) {
0u -> { /* do nothing */ } 0u -> { /* do nothing */ }
1u -> { 1u -> {
code += VmCodeInstruction(Instruction(Opcode.INC, dt, reg1=reg)) code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
} }
else -> { else -> {
val valueReg = vmRegisters.nextFree() val valueReg = vmRegisters.nextFree()
code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt())) code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt())
code += VmCodeInstruction(Instruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg)) code += VmCodeInstruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg)
} }
} }
return code return code
@@ -194,17 +192,17 @@ class CodeGen(internal val program: PtProgram,
val pow2 = powersOfTwo.indexOf(factor.toInt()) val pow2 = powersOfTwo.indexOf(factor.toInt())
if(pow2>=1) { if(pow2>=1) {
// just shift bits // 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 { } else {
when(factor) { when(factor) {
0u -> { 0u -> {
code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=reg, value=0)) code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
} }
1u -> { /* do nothing */ } 1u -> { /* do nothing */ }
else -> { else -> {
val factorReg = vmRegisters.nextFree() val factorReg = vmRegisters.nextFree()
code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=factorReg, value=factor.toInt())) code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value=factor.toInt())
code += VmCodeInstruction(Instruction(Opcode.MUL, dt, reg1=reg, reg2=reg, reg3=factorReg)) 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 // if and else parts
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = 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 += translateNode(ifElse.ifScope)
code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = afterIfLabel)) code += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel)
code += VmCodeLabel(elseLabel) code += VmCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope) code += translateNode(ifElse.elseScope)
code += VmCodeLabel(afterIfLabel) code += VmCodeLabel(afterIfLabel)
} else { } else {
// only if part // only if part
val afterIfLabel = createLabelName() 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 += translateNode(ifElse.ifScope)
code += VmCodeLabel(afterIfLabel) code += VmCodeLabel(afterIfLabel)
} }
@@ -266,15 +264,15 @@ class CodeGen(internal val program: PtProgram,
val resultReg = vmRegisters.nextFree() val resultReg = vmRegisters.nextFree()
if(ident!=null) { if(ident!=null) {
val address = allocations.get(ident.targetName) val address = allocations.get(ident.targetName)
code += VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address)) code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address)
code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg)) code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address)) code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address)
} else if(memory!=null) { } else if(memory!=null) {
val addressReg = vmRegisters.nextFree() val addressReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg) code += expressionEval.translateExpression(memory.address, addressReg)
code += VmCodeInstruction(Instruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg)) code += VmCodeInstruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg)
code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg)) code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
code += VmCodeInstruction(Instruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg)) code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg)
} else if (array!=null) { } else if (array!=null) {
TODO("postincrdecr array") TODO("postincrdecr array")
} else } else
@@ -300,8 +298,8 @@ class CodeGen(internal val program: PtProgram,
val repeatLabel = createLabelName() val repeatLabel = createLabelName()
code += VmCodeLabel(repeatLabel) code += VmCodeLabel(repeatLabel)
code += translateNode(repeat.statements) code += translateNode(repeat.statements)
code += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg)) code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg)
code += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel)) code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel)
return code return code
} }
@@ -310,9 +308,9 @@ class CodeGen(internal val program: PtProgram,
if(jump.address!=null) if(jump.address!=null)
throw AssemblyError("cannot jump to memory location in the vm target") throw AssemblyError("cannot jump to memory location in the vm target")
code += if(jump.generatedLabel!=null) 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) else if(jump.identifier!=null)
VmCodeInstruction(Instruction(Opcode.JUMP, symbol = jump.identifier!!.targetName)) VmCodeInstruction(Opcode.JUMP, symbol = jump.identifier!!.targetName)
else else
throw AssemblyError("weird jump") throw AssemblyError("weird jump")
return code return code
@@ -336,8 +334,7 @@ class CodeGen(internal val program: PtProgram,
val vmDt = vmType(assignment.value.type) val vmDt = vmType(assignment.value.type)
if(ident!=null) { if(ident!=null) {
val address = allocations.get(ident.targetName) val address = allocations.get(ident.targetName)
val ins = Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address) code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address)
code += VmCodeInstruction(ins)
} }
else if(array!=null) { else if(array!=null) {
val variable = array.variable.targetName val variable = array.variable.targetName
@@ -347,23 +344,21 @@ class CodeGen(internal val program: PtProgram,
val vmDtArrayIdx = vmType(array.type) val vmDtArrayIdx = vmType(array.type)
if(fixedIndex!=null) { if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize variableAddr += fixedIndex*itemsize
code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr)) code += VmCodeInstruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr)
} else { } else {
val indexReg = vmRegisters.nextFree() val indexReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(array.index, indexReg) 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) { else if(memory!=null) {
val ins = if(memory.address is PtNumber) {
if(memory.address is PtNumber) { code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) } else {
} else { val addressRegister = vmRegisters.nextFree()
val addressRegister = vmRegisters.nextFree() code += expressionEval.translateExpression(assignment.value, addressRegister)
code += expressionEval.translateExpression(assignment.value, addressRegister) code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister)
Instruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister) }
}
code += VmCodeInstruction(ins)
} }
else else
throw AssemblyError("weird assigntarget") throw AssemblyError("weird assigntarget")
@@ -377,7 +372,7 @@ class CodeGen(internal val program: PtProgram,
// Call Convention: return value is always returned in r0 // Call Convention: return value is always returned in r0
code += expressionEval.translateExpression(value, 0) code += expressionEval.translateExpression(value, 0)
} }
code += VmCodeInstruction(Instruction(Opcode.RETURN)) code += VmCodeInstruction(Opcode.RETURN)
return code return code
} }

View File

@@ -7,7 +7,6 @@ import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.PassByValueDatatypes import prog8.code.core.PassByValueDatatypes
import prog8.code.core.SignedDatatypes import prog8.code.core.SignedDatatypes
import prog8.vm.Instruction
import prog8.vm.Opcode import prog8.vm.Opcode
import prog8.vm.VmDataType import prog8.vm.VmDataType
@@ -21,20 +20,20 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
when (expr) { when (expr) {
is PtNumber -> { 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 -> { is PtIdentifier -> {
val mem = codeGen.allocations.get(expr.targetName) val mem = codeGen.allocations.get(expr.targetName)
code += if(expr.type in PassByValueDatatypes) { code += if(expr.type in PassByValueDatatypes) {
VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem)) VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem)
} else { } else {
// for strings and arrays etc., load the *address* of the value instead // 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 -> { is PtAddressOf -> {
val mem = codeGen.allocations.get(expr.identifier.targetName) 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 -> { is PtMemoryByte -> {
val addressRegister = codeGen.vmRegisters.nextFree() val addressRegister = codeGen.vmRegisters.nextFree()
@@ -100,11 +99,11 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += translateExpression(arrayIx.index, idxReg) code += translateExpression(arrayIx.index, idxReg)
if(eltSize>1) { if(eltSize>1) {
val factorReg = codeGen.vmRegisters.nextFree() val factorReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize)) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize)
code += VmCodeInstruction(Instruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg)) code += VmCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg)
} }
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName) 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 return code
} }
@@ -115,22 +114,22 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
when(expr.operator) { when(expr.operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
code += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister)) code += VmCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
} }
"~" -> { "~" -> {
val regMask = codeGen.vmRegisters.nextFree() val regMask = codeGen.vmRegisters.nextFree()
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask)) code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask)
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)) code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)
} }
"not" -> { "not" -> {
val label = codeGen.createLabelName() val label = codeGen.createLabelName()
code += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label)) code += VmCodeInstruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label)
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1)) code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1)
code += VmCodeLabel(label) code += VmCodeLabel(label)
val regMask = codeGen.vmRegisters.nextFree() val regMask = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=1)) code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=1)
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)) code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)
} }
else -> throw AssemblyError("weird prefix operator") else -> throw AssemblyError("weird prefix operator")
} }
@@ -165,11 +164,11 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
when(cast.value.type) { when(cast.value.type) {
DataType.BYTE -> { DataType.BYTE -> {
// byte -> uword: sign extend // 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 -> { DataType.UBYTE -> {
// ubyte -> uword: sign extend // 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.WORD -> { }
DataType.FLOAT -> { DataType.FLOAT -> {
@@ -182,11 +181,11 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
when(cast.value.type) { when(cast.value.type) {
DataType.BYTE -> { DataType.BYTE -> {
// byte -> word: sign extend // 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 -> { DataType.UBYTE -> {
// byte -> word: sign extend // 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.UWORD -> { }
DataType.FLOAT -> { DataType.FLOAT -> {
@@ -228,57 +227,57 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
val signed = binExpr.left.type in SignedDatatypes val signed = binExpr.left.type in SignedDatatypes
when(binExpr.operator) { 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" -> { "|", "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" -> { "&", "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" -> { "^", "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 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 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 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 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 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}") else -> throw AssemblyError("weird operator ${binExpr.operator}")
} }
@@ -293,12 +292,12 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += translateExpression(arg, argReg) code += translateExpression(arg, argReg)
val vmDt = codeGen.vmType(parameter.type) val vmDt = codeGen.vmType(parameter.type)
val mem = codeGen.allocations.get(fcall.functionName + parameter.name) 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) { 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? // 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 return code
} }

View File

@@ -3,7 +3,6 @@ TODO
For next release 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 - x16: check additional FP lib changes https://github.com/commanderx16/x16-rom/commit/ae608673f0210953172d6837acfbb231d62ddbd1
and https://github.com/commanderx16/x16-docs/commit/21238aedc641da91df88e04c4ce9bf3324a3c12d 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 - 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

View File

@@ -24,8 +24,9 @@ main {
} }
txt.print_uw(ww) ; 8 txt.print_uw(ww) ; 8
txt.nl() txt.nl()
txt.nl()
for wc in [4096,8192,16384] { for wc in [4097,8193,16385] {
txt.print_uw(wc) txt.print_uw(wc)
txt.spc() txt.spc()
ww++ ww++
@@ -33,19 +34,24 @@ main {
txt.print_uw(ww) ; 11 txt.print_uw(ww) ; 11
txt.nl() txt.nl()
; for bc in 10 to 20 step 3 { for bc in 10 to 20 step 3 {
; ; 10,13,16,19 -> 4 ; 10,13,16,19
; ww++ txt.print_ub(bc)
; } txt.spc()
; txt.print_uw(ww) ww++
; txt.nl() }
; for bc in 30 to 10 step -4 { txt.print_uw(ww) ; 15
; ; 30,26,22,18,14,10,6,2 -> +8 -> 12 txt.nl()
; ww++
; } for bc in 30 to 10 step -4 {
; txt.print_uw(ww) ; 30,26,22,18,14,10,6,2
; txt.nl() txt.print_ub(bc)
; txt.spc()
ww++
}
txt.print_uw(ww) ; 23
txt.nl()
sys.exit(99) sys.exit(99)

View File

@@ -3,6 +3,7 @@ plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
} }
java { java {
@@ -26,6 +27,7 @@ compileTestKotlin {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.1.0'
} }
sourceSets { sourceSets {
@@ -37,6 +39,23 @@ sourceSets {
srcDirs = ["${project.projectDir}/res"] 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"
}
}

View File

@@ -40,7 +40,7 @@ class Assembler {
val array = values.split(',').map { parseValue(it.trim(), 0) } val array = values.split(',').map { parseValue(it.trim(), 0) }
for (value in array) { for (value in array) {
memory.setUW(address, value.toUShort()) memory.setUW(address, value.toUShort())
address++ address += 2
} }
} }
else -> throw IllegalArgumentException("mem instr $what") else -> throw IllegalArgumentException("mem instr $what")

View File

@@ -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 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 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 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 storem reg1, address - store reg1 in memory address
storei reg1, reg2 - store reg1 in memory indirect, memory pointed to by reg2 storei reg1, reg2 - store reg1 in memory indirect, memory pointed to by reg2
@@ -130,7 +129,8 @@ nop - do nothing
breakpoint - trigger a breakpoint breakpoint - trigger a breakpoint
copy reg1, reg2, length - copy memory from ptrs in reg1 to reg3, length bytes 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 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) 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 push [b, w] reg1 - push value in reg1 on the stack
pop [b, w] reg1 - pop value from stack into reg1 pop [b, w] reg1 - pop value from stack into reg1
@@ -227,8 +227,17 @@ data class Instruction(
) { ) {
override fun toString(): String { override fun toString(): String {
val result = mutableListOf(opcode.name.lowercase()) val result = mutableListOf(opcode.name.lowercase())
if(instructionFormats.getValue(opcode).datatypes.isNotEmpty() && type==null) val format = instructionFormats.getValue(opcode)
throw IllegalArgumentException("opcode $opcode requires type") 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) { when(type) {
VmDataType.BYTE -> result.add(".b ") VmDataType.BYTE -> result.add(".b ")
@@ -332,7 +341,7 @@ val instructionFormats = mutableMapOf(
Opcode.COPY to InstructionFormat(NN, true, true, false, true ), Opcode.COPY to InstructionFormat(NN, true, true, false, true ),
Opcode.COPYZ to InstructionFormat(NN, true, true, false, false), 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.PUSH to InstructionFormat(BW, true, false, false, false),
Opcode.POP 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), Opcode.CONCAT to InstructionFormat(BW, true, true, true, false),

View File

@@ -36,7 +36,8 @@ enum class Syscall {
GFX_PLOT, GFX_PLOT,
RND, RND,
WAIT, WAIT,
WAITVSYNC WAITVSYNC,
TMP_PRINT_UW
} }
object SysCalls { object SysCalls {
@@ -91,6 +92,9 @@ object SysCalls {
Thread.sleep(millis) Thread.sleep(millis)
} }
Syscall.WAITVSYNC -> vm.waitvsync() Syscall.WAITVSYNC -> vm.waitvsync()
Syscall.TMP_PRINT_UW -> {
println("VM: UW=${vm.registers.getUW(0)}")
}
else -> TODO("syscall ${call.name}") else -> TODO("syscall ${call.name}")
} }
} }

View File

@@ -694,9 +694,9 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsSWAP(i: Instruction) { private fun InsSWAP(i: Instruction) {
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> { VmDataType.BYTE -> {
val value = registers.getUW(i.reg2!!) val value = registers.getUW(i.reg1!!)
val newValue = value.toUByte()*256u + (value.toInt() ushr 8).toUInt() val newValue = 0xea31 // value.toUByte()*256u + (value.toInt() ushr 8).toUInt()
registers.setUW(i.reg1!!, newValue.toUShort()) registers.setUW(i.reg1, newValue.toUShort())
} }
VmDataType.WORD -> TODO("swap.w requires 32-bits registers") VmDataType.WORD -> TODO("swap.w requires 32-bits registers")
} }

View File

@@ -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<IllegalArgumentException> {
ins.toString()
}
}
test("missing registers should fail") {
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, value=9999)
shouldThrow<IllegalArgumentException> {
ins.toString()
}
}
test("missing value should fail") {
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
shouldThrow<IllegalArgumentException> {
ins.toString()
}
}
test("all instructionformats") {
Opcode.values().forEach {
instructionFormats[it] shouldNotBe null
}
}
})

View File

@@ -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<ArrayIndexOutOfBoundsException> {
mem.getUB(9999999)
}
shouldThrow<ArrayIndexOutOfBoundsException> {
mem.setUB(9999999, 0u)
}
}
})

View File

@@ -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
}
})

View File

@@ -4,10 +4,13 @@
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
</component> </component>
</module> </module>