working on vm

This commit is contained in:
Irmen de Jong 2022-03-29 01:16:26 +02:00
parent 3e1a7c6102
commit f46300016d
6 changed files with 249 additions and 167 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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":

View File

@ -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.