mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
working on vm
This commit is contained in:
parent
3e1a7c6102
commit
f46300016d
@ -8,9 +8,9 @@ import prog8.vm.Opcode
|
||||
import prog8.vm.Syscall
|
||||
import prog8.vm.VmDataType
|
||||
|
||||
internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen) {
|
||||
internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: ExpressionGen) {
|
||||
|
||||
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
when(call.name) {
|
||||
"syscall" -> {
|
||||
@ -20,19 +20,19 @@ internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen)
|
||||
"syscall1" -> {
|
||||
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
|
||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
||||
code += exprGen.translateExpression(call.args[1], 0, regUsage)
|
||||
code += exprGen.translateExpression(call.args[1], 0)
|
||||
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
|
||||
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
|
||||
}
|
||||
"syscall2" -> {
|
||||
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
|
||||
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
|
||||
while(regUsage.firstFree<2) {
|
||||
regUsage.nextFree()
|
||||
while(codeGen.vmRegisters.peekNext()<2) {
|
||||
codeGen.vmRegisters.nextFree()
|
||||
}
|
||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
||||
code += exprGen.translateExpression(call.args[1], 0, regUsage)
|
||||
code += exprGen.translateExpression(call.args[2], 1, regUsage)
|
||||
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))
|
||||
@ -41,25 +41,25 @@ internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen)
|
||||
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))
|
||||
while(regUsage.firstFree<3) {
|
||||
regUsage.nextFree()
|
||||
while(codeGen.vmRegisters.peekNext()<3) {
|
||||
codeGen.vmRegisters.nextFree()
|
||||
}
|
||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
||||
code += exprGen.translateExpression(call.args[1], 0, regUsage)
|
||||
code += exprGen.translateExpression(call.args[2], 1, regUsage)
|
||||
code += exprGen.translateExpression(call.args[3], 2, regUsage)
|
||||
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))
|
||||
}
|
||||
"msb" -> {
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister, regUsage)
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister)
|
||||
code += VmCodeInstruction(Instruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister, reg2=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" -> {
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister, regUsage)
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister)
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
}
|
||||
"memory" -> {
|
||||
@ -83,27 +83,27 @@ internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen)
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0))
|
||||
}
|
||||
"peek" -> {
|
||||
val addressReg = regUsage.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), addressReg, regUsage)
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), addressReg)
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg))
|
||||
}
|
||||
"peekw" -> {
|
||||
val addressReg = regUsage.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), addressReg, regUsage)
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), addressReg)
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg))
|
||||
}
|
||||
"mkword" -> {
|
||||
val msbReg = regUsage.nextFree()
|
||||
val lsbReg = regUsage.nextFree()
|
||||
code += exprGen.translateExpression(call.args[0], msbReg, regUsage)
|
||||
code += exprGen.translateExpression(call.args[1], lsbReg, regUsage)
|
||||
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))
|
||||
}
|
||||
else -> {
|
||||
TODO("builtinfunc ${call.name}")
|
||||
// code += VmCodeInstruction(Instruction(Opcode.NOP))
|
||||
// for (arg in call.args) {
|
||||
// code += translateExpression(arg, resultRegister, regUsage)
|
||||
// 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))
|
||||
|
@ -7,6 +7,23 @@ 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
|
||||
|
||||
|
||||
internal class VmRegisterPool() {
|
||||
private var firstFree: Int=3 // registers 0,1,2 are reserved
|
||||
|
||||
fun peekNext() = firstFree
|
||||
|
||||
fun nextFree(): Int {
|
||||
val result = firstFree
|
||||
firstFree++
|
||||
if(firstFree>65535)
|
||||
throw AssemblyError("out of virtual registers")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CodeGen(internal val program: PtProgram,
|
||||
@ -18,6 +35,7 @@ class CodeGen(internal val program: PtProgram,
|
||||
internal val allocations = VariableAllocator(symbolTable, program, errors)
|
||||
private val expressionEval = ExpressionGen(this)
|
||||
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
|
||||
internal val vmRegisters = VmRegisterPool()
|
||||
|
||||
init {
|
||||
if(options.dontReinitGlobals)
|
||||
@ -30,40 +48,39 @@ class CodeGen(internal val program: PtProgram,
|
||||
// collect global variables initializers
|
||||
program.allBlocks().forEach {
|
||||
val code = VmCodeChunk()
|
||||
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += translate(assign, RegisterUsage(0)) }
|
||||
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += translate(assign) }
|
||||
vmprog.addGlobalInits(code)
|
||||
}
|
||||
|
||||
val regUsage = RegisterUsage(0)
|
||||
for (block in program.allBlocks()) {
|
||||
vmprog.addBlock(translate(block, regUsage))
|
||||
vmprog.addBlock(translate(block))
|
||||
}
|
||||
|
||||
return vmprog
|
||||
}
|
||||
|
||||
|
||||
private fun translateNode(node: PtNode, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translateNode(node: PtNode): VmCodeChunk {
|
||||
val code = when(node) {
|
||||
is PtBlock -> translate(node, regUsage)
|
||||
is PtSub -> translate(node, regUsage)
|
||||
is PtBlock -> translate(node)
|
||||
is PtSub -> translate(node)
|
||||
is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table
|
||||
is PtVariable -> VmCodeChunk() // var should be looked up via symbol table
|
||||
is PtMemMapped -> VmCodeChunk() // memmapped var should be looked up via symbol table
|
||||
is PtConstant -> VmCodeChunk() // constants have all been folded into the code
|
||||
is PtAssignment -> translate(node, regUsage)
|
||||
is PtNodeGroup -> translateGroup(node.children, regUsage)
|
||||
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, regUsage.nextFree(), regUsage)
|
||||
is PtFunctionCall -> expressionEval.translate(node, regUsage.nextFree(), regUsage)
|
||||
is PtAssignment -> translate(node)
|
||||
is PtNodeGroup -> translateGroup(node.children)
|
||||
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
|
||||
is PtFunctionCall -> expressionEval.translate(node, 0)
|
||||
is PtNop -> VmCodeChunk()
|
||||
is PtReturn -> translate(node)
|
||||
is PtJump -> translate(node)
|
||||
is PtWhen -> TODO("when")
|
||||
is PtPipe -> expressionEval.translate(node, regUsage.nextFree(), regUsage)
|
||||
is PtForLoop -> translate(node, regUsage)
|
||||
is PtIfElse -> translate(node, regUsage)
|
||||
is PtPostIncrDecr -> translate(node, regUsage)
|
||||
is PtRepeatLoop -> translate(node, regUsage)
|
||||
is PtPipe -> expressionEval.translate(node, 0)
|
||||
is PtForLoop -> translate(node)
|
||||
is PtIfElse -> translate(node)
|
||||
is PtPostIncrDecr -> translate(node)
|
||||
is PtRepeatLoop -> translate(node)
|
||||
is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName))
|
||||
is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Instruction(Opcode.BREAKPOINT)))
|
||||
is PtAddressOf,
|
||||
@ -88,35 +105,113 @@ class CodeGen(internal val program: PtProgram,
|
||||
is PtConditionalBranch -> throw AssemblyError("conditional branches not supported in vm target due to lack of cpu flags ${node.position}")
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
code.lines.add(0, VmCodeComment(node.position.toString()))
|
||||
if(code.lines.isNotEmpty())
|
||||
code.lines.add(0, VmCodeComment(node.position.toString()))
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(forLoop: PtForLoop, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(forLoop: PtForLoop): VmCodeChunk {
|
||||
val loopvar = symbolTable.lookup(forLoop.variable.targetName) as StStaticVariable
|
||||
val iterable = forLoop.iterable
|
||||
val code = VmCodeChunk()
|
||||
when(iterable) {
|
||||
is PtRange -> {
|
||||
println("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ")
|
||||
TODO("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ")
|
||||
iterable.printIndented(0)
|
||||
TODO()
|
||||
TODO("forloop over range")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val address = allocations.get(iterable.targetName)
|
||||
val variable = symbolTable.lookup(iterable.targetName) as StStaticVariable
|
||||
val length = variable.length!!
|
||||
println("forloop ${loopvar.dt} ${loopvar.scopedName} in $iterable @${address.toHex()} ${length}")
|
||||
val indexReg = regUsage.nextFree()
|
||||
val loopvarReg = regUsage.nextFree()
|
||||
TODO()
|
||||
val arrayAddress = allocations.get(iterable.targetName)
|
||||
val iterableVar = symbolTable.lookup(iterable.targetName) as StStaticVariable
|
||||
val loopvarAddress = allocations.get(loopvar.scopedName)
|
||||
val indexReg = vmRegisters.nextFree()
|
||||
val loopLabel = createLabelName()
|
||||
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 += 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 += translateNode(forLoop.statements)
|
||||
code += VmCodeInstruction(Instruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel))
|
||||
code += VmCodeLabel(endLabel)
|
||||
} else {
|
||||
// iterate over array
|
||||
val elementDt = ArrayToElementTypes.getValue(iterable.type)
|
||||
val elementSize = program.memsizer.memorySize(elementDt).toUInt()
|
||||
val lengthBytes = iterableVar.length!! * elementSize.toInt()
|
||||
val lengthReg = vmRegisters.nextFree()
|
||||
/*
|
||||
index = 0
|
||||
_loop:
|
||||
if index==(length * <element_size>) goto _end
|
||||
loopvar = read_mem(iterable+index)
|
||||
<statements>
|
||||
index += <elementsize>
|
||||
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 += 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 += translateNode(forLoop.statements)
|
||||
code += addConst(VmDataType.BYTE, indexReg, elementSize)
|
||||
code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel))
|
||||
code += VmCodeLabel(endLabel)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird for iterable")
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(ifElse: PtIfElse, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun addConst(dt: VmDataType, reg: Int, value: UInt): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
when(value) {
|
||||
0u -> { /* do nothing */ }
|
||||
1u -> {
|
||||
code += VmCodeInstruction(Instruction(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))
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private val powersOfTwo = (0..16).map { 2.0.pow(it.toDouble()).toInt() }
|
||||
|
||||
private fun multiplyByConst(dt: VmDataType, reg: Int, factor: UInt): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
val pow2 = powersOfTwo.indexOf(factor.toInt())
|
||||
if(pow2>=1) {
|
||||
// just shift bits
|
||||
code += VmCodeInstruction(Instruction(Opcode.LSL, dt, reg1=reg, reg2=reg, reg3=pow2))
|
||||
} else {
|
||||
when(factor) {
|
||||
0u -> {
|
||||
code += VmCodeInstruction(Instruction(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))
|
||||
}
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(ifElse: PtIfElse): VmCodeChunk {
|
||||
var branch = Opcode.BZ
|
||||
var condition = ifElse.condition
|
||||
|
||||
@ -133,31 +228,31 @@ class CodeGen(internal val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
val conditionReg = regUsage.nextFree()
|
||||
val conditionReg = vmRegisters.nextFree()
|
||||
val vmDt = vmType(condition.type)
|
||||
val code = VmCodeChunk()
|
||||
code += expressionEval.translateExpression(condition, conditionReg, regUsage)
|
||||
code += expressionEval.translateExpression(condition, conditionReg)
|
||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg, symbol = elseLabel))
|
||||
code += translateNode(ifElse.ifScope, regUsage)
|
||||
code += translateNode(ifElse.ifScope)
|
||||
code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = afterIfLabel))
|
||||
code += VmCodeLabel(elseLabel)
|
||||
code += translateNode(ifElse.elseScope, regUsage)
|
||||
code += translateNode(ifElse.elseScope)
|
||||
code += VmCodeLabel(afterIfLabel)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg, symbol = afterIfLabel))
|
||||
code += translateNode(ifElse.ifScope, regUsage)
|
||||
code += translateNode(ifElse.ifScope)
|
||||
code += VmCodeLabel(afterIfLabel)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(postIncrDecr: PtPostIncrDecr, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(postIncrDecr: PtPostIncrDecr): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
val operation = when(postIncrDecr.operator) {
|
||||
"++" -> Opcode.INC
|
||||
@ -168,15 +263,15 @@ class CodeGen(internal val program: PtProgram,
|
||||
val memory = postIncrDecr.target.memory
|
||||
val array = postIncrDecr.target.array
|
||||
val vmDt = vmType(postIncrDecr.target.type)
|
||||
val resultReg = regUsage.nextFree()
|
||||
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))
|
||||
} else if(memory!=null) {
|
||||
val addressReg = regUsage.nextFree()
|
||||
code += expressionEval.translateExpression(memory.address, addressReg, regUsage)
|
||||
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))
|
||||
@ -188,23 +283,23 @@ class CodeGen(internal val program: PtProgram,
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(repeat: PtRepeatLoop, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(repeat: PtRepeatLoop): VmCodeChunk {
|
||||
if((repeat.count as? PtNumber)?.number==0.0)
|
||||
return VmCodeChunk()
|
||||
if((repeat.count as? PtNumber)?.number==1.0)
|
||||
return translateGroup(repeat.children, regUsage)
|
||||
return translateGroup(repeat.children)
|
||||
if((repeat.count as? PtNumber)?.number==256.0) {
|
||||
// 256 iterations can still be done with just a byte counter if you set it to zero as starting value.
|
||||
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
|
||||
}
|
||||
|
||||
val code = VmCodeChunk()
|
||||
val counterReg = regUsage.nextFree()
|
||||
val counterReg = vmRegisters.nextFree()
|
||||
val vmDt = vmType(repeat.count.type)
|
||||
code += expressionEval.translateExpression(repeat.count, counterReg, regUsage)
|
||||
code += expressionEval.translateExpression(repeat.count, counterReg)
|
||||
val repeatLabel = createLabelName()
|
||||
code += VmCodeLabel(repeatLabel)
|
||||
code += translateNode(repeat.statements, regUsage)
|
||||
code += translateNode(repeat.statements)
|
||||
code += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel))
|
||||
return code
|
||||
@ -223,18 +318,18 @@ class CodeGen(internal val program: PtProgram,
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translateGroup(group: List<PtNode>, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translateGroup(group: List<PtNode>): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
group.forEach { code += translateNode(it, regUsage) }
|
||||
group.forEach { code += translateNode(it) }
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(assignment: PtAssignment, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(assignment: PtAssignment): VmCodeChunk {
|
||||
// TODO can in-place assignments (assignment.augmentable = true) be optimized more?
|
||||
|
||||
val code = VmCodeChunk()
|
||||
val resultRegister = regUsage.nextFree()
|
||||
code += expressionEval.translateExpression(assignment.value, resultRegister, regUsage)
|
||||
val resultRegister = vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(assignment.value, resultRegister)
|
||||
val ident = assignment.target.identifier
|
||||
val memory = assignment.target.memory
|
||||
val array = assignment.target.array
|
||||
@ -252,11 +347,11 @@ class CodeGen(internal val program: PtProgram,
|
||||
val vmDtArrayIdx = vmType(array.type)
|
||||
if(fixedIndex!=null) {
|
||||
variableAddr += fixedIndex*itemsize
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOADM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr))
|
||||
code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr))
|
||||
} else {
|
||||
val indexReg = regUsage.nextFree()
|
||||
code += expressionEval.translateExpression(array.index, indexReg, regUsage)
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOADX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr))
|
||||
val indexReg = vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(array.index, indexReg)
|
||||
code += VmCodeInstruction(Instruction(Opcode.STOREX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr))
|
||||
}
|
||||
}
|
||||
else if(memory!=null) {
|
||||
@ -264,8 +359,8 @@ class CodeGen(internal val program: PtProgram,
|
||||
if(memory.address is PtNumber) {
|
||||
Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
||||
} else {
|
||||
val addressRegister = regUsage.nextFree()
|
||||
code += expressionEval.translateExpression(assignment.value, addressRegister, regUsage)
|
||||
val addressRegister = vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(assignment.value, addressRegister)
|
||||
Instruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister)
|
||||
}
|
||||
code += VmCodeInstruction(ins)
|
||||
@ -280,30 +375,30 @@ class CodeGen(internal val program: PtProgram,
|
||||
val value = ret.value
|
||||
if(value!=null) {
|
||||
// Call Convention: return value is always returned in r0
|
||||
code += expressionEval.translateExpression(value, 0, RegisterUsage(1))
|
||||
code += expressionEval.translateExpression(value, 0)
|
||||
}
|
||||
code += VmCodeInstruction(Instruction(Opcode.RETURN))
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(sub: PtSub, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(sub: PtSub): VmCodeChunk {
|
||||
// TODO actually inline subroutines marked as inline
|
||||
val code = VmCodeChunk()
|
||||
code += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
|
||||
code += VmCodeLabel(sub.scopedName)
|
||||
for (child in sub.children) {
|
||||
code += translateNode(child, regUsage)
|
||||
code += translateNode(child)
|
||||
}
|
||||
code += VmCodeComment("SUB-END '${sub.name}'")
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(block: PtBlock, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(block: PtBlock): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
code += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
|
||||
for (child in block.children) {
|
||||
if(child !is PtAssignment) // global variable initialization is done elsewhere
|
||||
code += translateNode(child, regUsage)
|
||||
code += translateNode(child)
|
||||
}
|
||||
code += VmCodeComment("BLOCK-END '${block.name}'")
|
||||
return code
|
||||
@ -327,6 +422,6 @@ class CodeGen(internal val program: PtProgram,
|
||||
return listOf("generated$labelSequenceNumber")
|
||||
}
|
||||
|
||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk =
|
||||
builtinFuncGen.translate(call, resultRegister, regUsage)
|
||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk =
|
||||
builtinFuncGen.translate(call, resultRegister)
|
||||
}
|
||||
|
@ -12,18 +12,9 @@ import prog8.vm.Opcode
|
||||
import prog8.vm.VmDataType
|
||||
|
||||
|
||||
internal class RegisterUsage(var firstFree: Int=0) {
|
||||
fun nextFree(): Int {
|
||||
val result = firstFree
|
||||
firstFree++
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
fun translateExpression(expr: PtExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
require(regUsage.firstFree > resultRegister)
|
||||
internal class ExpressionGen(private val codeGen: CodeGen) {
|
||||
fun translateExpression(expr: PtExpression, resultRegister: Int): VmCodeChunk {
|
||||
require(codeGen.vmRegisters.peekNext() > resultRegister)
|
||||
|
||||
val code = VmCodeChunk()
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
@ -46,18 +37,18 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem))
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val addressRegister = regUsage.nextFree()
|
||||
val addressExprCode = translateExpression(expr.address, addressRegister, regUsage)
|
||||
val addressRegister = codeGen.vmRegisters.nextFree()
|
||||
val addressExprCode = translateExpression(expr.address, addressRegister)
|
||||
code += addressExprCode
|
||||
}
|
||||
is PtTypeCast -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtPrefix -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtArrayIndexer -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtBinaryExpression -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister, regUsage)
|
||||
is PtFunctionCall -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtContainmentCheck -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtPipe -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtTypeCast -> code += translate(expr, resultRegister)
|
||||
is PtPrefix -> code += translate(expr, resultRegister)
|
||||
is PtArrayIndexer -> code += translate(expr, resultRegister)
|
||||
is PtBinaryExpression -> code += translate(expr, resultRegister)
|
||||
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
||||
is PtFunctionCall -> code += translate(expr, resultRegister)
|
||||
is PtContainmentCheck -> code += translate(expr, resultRegister)
|
||||
is PtPipe -> code += translate(expr, resultRegister)
|
||||
is PtRange,
|
||||
is PtArray,
|
||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||
@ -66,34 +57,34 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun translate(pipe: PtPipe, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
|
||||
TODO("Not yet implemented: pipe expression")
|
||||
}
|
||||
|
||||
private fun translate(check: PtContainmentCheck, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(check: PtContainmentCheck, resultRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
code += translateExpression(check.element, resultRegister, regUsage) // load the element to check in resultRegister
|
||||
code += translateExpression(check.element, resultRegister) // load the element to check in resultRegister
|
||||
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.targetName) as StStaticVariable
|
||||
when(iterable.dt) {
|
||||
DataType.STR -> {
|
||||
val call = PtFunctionCall(listOf("prog8_lib", "string_contains"), false, DataType.UBYTE, check.position)
|
||||
call.children.add(check.element)
|
||||
call.children.add(check.iterable)
|
||||
code += translate(call, resultRegister, regUsage)
|
||||
code += translate(call, resultRegister)
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
val call = PtFunctionCall(listOf("prog8_lib", "bytearray_contains"), false, DataType.UBYTE, check.position)
|
||||
call.children.add(check.element)
|
||||
call.children.add(check.iterable)
|
||||
call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position))
|
||||
code += translate(call, resultRegister, regUsage)
|
||||
code += translate(call, resultRegister)
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
val call = PtFunctionCall(listOf("prog8_lib", "wordarray_contains"), false, DataType.UBYTE, check.position)
|
||||
call.children.add(check.element)
|
||||
call.children.add(check.iterable)
|
||||
call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position))
|
||||
code += translate(call, resultRegister, regUsage)
|
||||
code += translate(call, resultRegister)
|
||||
}
|
||||
DataType.ARRAY_F -> TODO("containment check in float-array")
|
||||
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.targetName}")
|
||||
@ -101,14 +92,14 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int): VmCodeChunk {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
|
||||
val vmDt = codeGen.vmType(arrayIx.type)
|
||||
val code = VmCodeChunk()
|
||||
val idxReg = regUsage.nextFree()
|
||||
code += translateExpression(arrayIx.index, idxReg, regUsage)
|
||||
val idxReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(arrayIx.index, idxReg)
|
||||
if(eltSize>1) {
|
||||
val factorReg = regUsage.nextFree()
|
||||
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))
|
||||
}
|
||||
@ -117,9 +108,9 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(expr: PtPrefix, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(expr: PtPrefix, resultRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
code += translateExpression(expr.value, resultRegister, regUsage)
|
||||
code += translateExpression(expr.value, resultRegister)
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
when(expr.operator) {
|
||||
"+" -> { }
|
||||
@ -127,7 +118,7 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
code += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister))
|
||||
}
|
||||
"~" -> {
|
||||
val regMask = regUsage.nextFree()
|
||||
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))
|
||||
@ -137,7 +128,7 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
code += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label))
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1))
|
||||
code += VmCodeLabel(label)
|
||||
val regMask = regUsage.nextFree()
|
||||
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))
|
||||
}
|
||||
@ -146,11 +137,11 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(cast: PtTypeCast, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(cast: PtTypeCast, resultRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
if(cast.type==cast.value.type)
|
||||
return code
|
||||
code += translateExpression(cast.value, resultRegister, regUsage)
|
||||
code += translateExpression(cast.value, resultRegister)
|
||||
when(cast.type) {
|
||||
DataType.UBYTE -> {
|
||||
when(cast.value.type) {
|
||||
@ -223,14 +214,14 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
val leftResultReg = regUsage.nextFree()
|
||||
val rightResultReg = regUsage.nextFree()
|
||||
val leftResultReg = codeGen.vmRegisters.nextFree()
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
// TODO: optimized codegen when left or right operand is known 0 or 1 or whatever. But only if this would result in a different opcode such as ADD 1 -> INC, MUL 1 -> NOP
|
||||
// actually optimizing the code should not be done here but in a tailored code optimizer step.
|
||||
val leftCode = translateExpression(binExpr.left, leftResultReg, regUsage)
|
||||
val rightCode = translateExpression(binExpr.right, rightResultReg, regUsage)
|
||||
val leftCode = translateExpression(binExpr.left, leftResultReg)
|
||||
val rightCode = translateExpression(binExpr.right, rightResultReg)
|
||||
code += leftCode
|
||||
code += rightCode
|
||||
val vmDt = codeGen.vmType(binExpr.left.type)
|
||||
@ -294,12 +285,12 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
|
||||
fun translate(fcall: PtFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
fun translate(fcall: PtFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||
val subroutine = codeGen.symbolTable.flat.getValue(fcall.functionName) as StSub
|
||||
val code = VmCodeChunk()
|
||||
for ((arg, parameter) in fcall.args.zip(subroutine.parameters)) {
|
||||
val argReg = regUsage.nextFree()
|
||||
code += translateExpression(arg, argReg, regUsage)
|
||||
val argReg = codeGen.vmRegisters.nextFree()
|
||||
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))
|
||||
|
@ -3,7 +3,7 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- vm codegen: fix primes endless loop stuck on '2'
|
||||
- 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
|
||||
|
@ -4,39 +4,35 @@
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte x1 = $ea
|
||||
ubyte x2 = $31
|
||||
uword uw = mkword(x1, $99)
|
||||
txt.print_uwhex(uw, true)
|
||||
txt.nl()
|
||||
uw = mkword(x1, x2)
|
||||
txt.print_uwhex(uw, true)
|
||||
txt.clear_screen()
|
||||
txt.print("Welcome to a prog8 pixel shader :-)\n")
|
||||
uword ww = 0
|
||||
ubyte bc
|
||||
uword wc
|
||||
|
||||
for bc in "irmen" {
|
||||
txt.chrout(bc)
|
||||
ww++
|
||||
}
|
||||
txt.print_uw(ww) ; 5
|
||||
txt.nl()
|
||||
|
||||
sys.exit(99)
|
||||
for bc in [10,11,12] {
|
||||
txt.print_ub(bc)
|
||||
txt.spc()
|
||||
ww++
|
||||
}
|
||||
txt.print_uw(ww) ; 8
|
||||
txt.nl()
|
||||
|
||||
for wc in [4096,8192,16384] {
|
||||
txt.print_uw(wc)
|
||||
txt.spc()
|
||||
ww++
|
||||
}
|
||||
txt.print_uw(ww) ; 11
|
||||
txt.nl()
|
||||
|
||||
; txt.clear_screen()
|
||||
; txt.print("Welcome to a prog8 pixel shader :-)\n")
|
||||
; uword ww = 0
|
||||
; ubyte bc
|
||||
; uword wc
|
||||
;
|
||||
; for bc in "irmen" {
|
||||
; ; +5 -> 17
|
||||
; txt.chrout(bc)
|
||||
; ww++
|
||||
; }
|
||||
; txt.print_uw(ww)
|
||||
; txt.nl()
|
||||
;
|
||||
; for wc in [1000,1111,1222] {
|
||||
; txt.print_uw(wc)
|
||||
; txt.spc()
|
||||
; ww++ ; +3 -> 20
|
||||
; }
|
||||
; txt.print_uw(ww)
|
||||
; txt.nl()
|
||||
;
|
||||
; for bc in 10 to 20 step 3 {
|
||||
; ; 10,13,16,19 -> 4
|
||||
; ww++
|
||||
@ -50,8 +46,8 @@ main {
|
||||
; txt.print_uw(ww)
|
||||
; txt.nl()
|
||||
;
|
||||
;
|
||||
; sys.exit(99)
|
||||
|
||||
sys.exit(99)
|
||||
|
||||
|
||||
; the "pixelshader":
|
||||
|
@ -27,22 +27,22 @@ class Memory {
|
||||
}
|
||||
|
||||
fun getUW(address: Int): UShort {
|
||||
return (256u*mem[address] + mem[address+1]).toUShort()
|
||||
return (256u*mem[address+1] + mem[address]).toUShort()
|
||||
}
|
||||
|
||||
fun getSW(address: Int): Short {
|
||||
return (mem[address].toInt()*256 + mem[address+1].toInt()).toShort()
|
||||
return (mem[address+1].toInt()*256 + mem[address].toInt()).toShort()
|
||||
}
|
||||
|
||||
fun setUW(address: Int, value: UShort) {
|
||||
mem[address] = (value.toInt() ushr 8).toUByte()
|
||||
mem[address+1] = value.toUByte()
|
||||
mem[address+1] = (value.toInt() ushr 8).toUByte()
|
||||
mem[address] = value.toUByte()
|
||||
}
|
||||
|
||||
fun setSW(address: Int, value: Short) {
|
||||
val uv = value.toUShort()
|
||||
mem[address] = (uv.toInt() ushr 8).toUByte()
|
||||
mem[address+1] = uv.toUByte()
|
||||
mem[address+1] = (uv.toInt() ushr 8).toUByte()
|
||||
mem[address] = uv.toUByte()
|
||||
}
|
||||
|
||||
// for now, no LONG 32-bits and no FLOAT support.
|
||||
|
Loading…
Reference in New Issue
Block a user