mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
working on vm
This commit is contained in:
parent
f46300016d
commit
0dc592b819
@ -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<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 VmCodeComment(val comment: String): VmCodeLine()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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),
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
@ -694,9 +694,9 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
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")
|
||||
}
|
||||
|
75
virtualmachine/test/TestInstructions.kt
Normal file
75
virtualmachine/test/TestInstructions.kt
Normal 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
|
||||
}
|
||||
}
|
||||
})
|
67
virtualmachine/test/TestMemory.kt
Normal file
67
virtualmachine/test/TestMemory.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
})
|
41
virtualmachine/test/TestRegisters.kt
Normal file
41
virtualmachine/test/TestRegisters.kt
Normal 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
|
||||
}
|
||||
})
|
@ -4,10 +4,13 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<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>
|
||||
</module>
|
Loading…
x
Reference in New Issue
Block a user