mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
working on vm
This commit is contained in:
parent
3e44620966
commit
0307f6b42c
@ -27,9 +27,9 @@ class CodeGen(internal val program: PtProgram,
|
||||
|
||||
// collect global variables initializers
|
||||
program.allBlocks().forEach {
|
||||
val chunk = VmCodeChunk()
|
||||
it.children.filterIsInstance<PtAssignment>().forEach { assign -> chunk += translate(assign, RegisterUsage(0)) }
|
||||
vmprog.addGlobalInits(chunk)
|
||||
val code = VmCodeChunk()
|
||||
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += translate(assign, RegisterUsage(0)) }
|
||||
vmprog.addGlobalInits(code)
|
||||
}
|
||||
|
||||
val regUsage = RegisterUsage(0)
|
||||
@ -42,7 +42,7 @@ class CodeGen(internal val program: PtProgram,
|
||||
|
||||
|
||||
private fun translateNode(node: PtNode, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val chunk = when(node) {
|
||||
val code = when(node) {
|
||||
is PtBlock -> translate(node, regUsage)
|
||||
is PtSub -> translate(node, regUsage)
|
||||
is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table
|
||||
@ -56,13 +56,12 @@ class CodeGen(internal val program: PtProgram,
|
||||
is PtNop -> VmCodeChunk()
|
||||
is PtReturn -> translate(node)
|
||||
is PtJump -> translate(node)
|
||||
is PtConditionalBranch -> TODO()
|
||||
is PtWhen -> TODO()
|
||||
is PtPipe -> TODO()
|
||||
is PtForLoop -> TODO()
|
||||
is PtIfElse -> TODO()
|
||||
is PtIfElse -> translate(node, regUsage)
|
||||
is PtPostIncrDecr -> translate(node, regUsage)
|
||||
is PtRepeatLoop -> translate(node, regUsage)
|
||||
is PtWhen -> TODO()
|
||||
is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName))
|
||||
is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Instruction(Opcode.BREAKPOINT)))
|
||||
is PtAddressOf,
|
||||
@ -80,18 +79,60 @@ class CodeGen(internal val program: PtProgram,
|
||||
is PtSubroutineParameter,
|
||||
is PtNumber,
|
||||
is PtArrayLiteral,
|
||||
is PtString -> throw AssemblyError("$node should not occur as separate statement node")
|
||||
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target")
|
||||
is PtInlineAssembly -> throw AssemblyError("inline assembly not supported on virtual machine target")
|
||||
is PtIncludeBinary -> throw AssemblyError("inline binary data not supported on virtual machine target")
|
||||
is PtString -> throw AssemblyError("strings should not occur as separate statement node ${node.position}")
|
||||
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target ${node.position}")
|
||||
is PtInlineAssembly -> throw AssemblyError("inline assembly not supported on virtual machine target ${node.position}")
|
||||
is PtIncludeBinary -> throw AssemblyError("inline binary data not supported on virtual machine target ${node.position}")
|
||||
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")
|
||||
}
|
||||
chunk.lines.add(0, VmCodeComment(node.position.toString()))
|
||||
return chunk
|
||||
code.lines.add(0, VmCodeComment(node.position.toString()))
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(ifElse: PtIfElse, regUsage: RegisterUsage): VmCodeChunk {
|
||||
var branch = Opcode.BZ
|
||||
var condition = ifElse.condition
|
||||
|
||||
val cond = ifElse.condition as? PtBinaryExpression
|
||||
if((cond?.right as? PtNumber)?.number==0.0) {
|
||||
if(cond.operator == "==") {
|
||||
// if X==0 ... so we branch on Not-zero instead.
|
||||
branch = Opcode.BNZ
|
||||
condition = cond.left
|
||||
}
|
||||
else if(cond.operator == "!=") {
|
||||
// if X!=0 ... so we keep branching on Zero.
|
||||
condition = cond.left
|
||||
}
|
||||
}
|
||||
|
||||
val conditionReg = regUsage.nextFree()
|
||||
val vmDt = vmType(condition.type)
|
||||
val code = VmCodeChunk()
|
||||
code += expressionEval.translateExpression(condition, conditionReg, regUsage)
|
||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg), labelArg = elseLabel)
|
||||
code += translateNode(ifElse.ifScope, regUsage)
|
||||
code += VmCodeInstruction(Instruction(Opcode.JUMP), labelArg = afterIfLabel)
|
||||
code += VmCodeLabel(elseLabel)
|
||||
code += translateNode(ifElse.elseScope, regUsage)
|
||||
code += VmCodeLabel(afterIfLabel)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg), labelArg = afterIfLabel)
|
||||
code += translateNode(ifElse.ifScope, regUsage)
|
||||
code += VmCodeLabel(afterIfLabel)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(postIncrDecr: PtPostIncrDecr, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
val operation = when(postIncrDecr.operator) {
|
||||
"++" -> Opcode.INC
|
||||
"--" -> Opcode.DEC
|
||||
@ -104,21 +145,21 @@ class CodeGen(internal val program: PtProgram,
|
||||
val resultReg = regUsage.nextFree()
|
||||
if(ident!=null) {
|
||||
val address = allocations.get(ident.targetName)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address))
|
||||
chunk += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address))
|
||||
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()
|
||||
chunk += expressionEval.translateExpression(memory.address, addressReg, regUsage)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg))
|
||||
chunk += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg))
|
||||
code += expressionEval.translateExpression(memory.address, addressReg, regUsage)
|
||||
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))
|
||||
} else if (array!=null) {
|
||||
TODO("postincrdecr array")
|
||||
} else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(repeat: PtRepeatLoop, regUsage: RegisterUsage): VmCodeChunk {
|
||||
@ -131,43 +172,43 @@ class CodeGen(internal val program: PtProgram,
|
||||
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
|
||||
}
|
||||
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
val counterReg = regUsage.nextFree()
|
||||
val vmDt = vmType(repeat.count.type)
|
||||
chunk += expressionEval.translateExpression(repeat.count, counterReg, regUsage)
|
||||
code += expressionEval.translateExpression(repeat.count, counterReg, regUsage)
|
||||
val repeatLabel = createLabelName()
|
||||
chunk += VmCodeLabel(repeatLabel)
|
||||
chunk += translateNode(repeat.statements, regUsage)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg), labelArg = repeatLabel)
|
||||
return chunk
|
||||
code += VmCodeLabel(repeatLabel)
|
||||
code += translateNode(repeat.statements, regUsage)
|
||||
code += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg), labelArg = repeatLabel)
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(jump: PtJump): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
if(jump.address!=null)
|
||||
throw AssemblyError("cannot jump to memory location in the vm target")
|
||||
chunk += if(jump.generatedLabel!=null)
|
||||
code += if(jump.generatedLabel!=null)
|
||||
VmCodeInstruction(Instruction(Opcode.JUMP), labelArg = listOf(jump.generatedLabel!!))
|
||||
else if(jump.identifier!=null)
|
||||
VmCodeInstruction(Instruction(Opcode.JUMP), labelArg = jump.identifier!!.targetName)
|
||||
else
|
||||
throw AssemblyError("weird jump")
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translateGroup(group: List<PtNode>, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
group.forEach { chunk += translateNode(it, regUsage) }
|
||||
return chunk
|
||||
val code = VmCodeChunk()
|
||||
group.forEach { code += translateNode(it, regUsage) }
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(assignment: PtAssignment, regUsage: RegisterUsage): VmCodeChunk {
|
||||
// TODO optimize in-place assignments (assignment.augmentable = true)
|
||||
// TODO can in-place assignments (assignment.augmentable = true) be optimized more?
|
||||
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
val resultRegister = regUsage.nextFree()
|
||||
chunk += expressionEval.translateExpression(assignment.value, resultRegister, regUsage)
|
||||
code += expressionEval.translateExpression(assignment.value, resultRegister, regUsage)
|
||||
val ident = assignment.target.identifier
|
||||
val memory = assignment.target.memory
|
||||
val array = assignment.target.array
|
||||
@ -175,7 +216,7 @@ class CodeGen(internal val program: PtProgram,
|
||||
if(ident!=null) {
|
||||
val address = allocations.get(ident.targetName)
|
||||
val ins = Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address)
|
||||
chunk += VmCodeInstruction(ins)
|
||||
code += VmCodeInstruction(ins)
|
||||
}
|
||||
else if(array!=null) {
|
||||
TODO("assign to array")
|
||||
@ -186,48 +227,48 @@ class CodeGen(internal val program: PtProgram,
|
||||
Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
||||
} else {
|
||||
val addressRegister = regUsage.nextFree()
|
||||
chunk += expressionEval.translateExpression(assignment.value, addressRegister, regUsage)
|
||||
code += expressionEval.translateExpression(assignment.value, addressRegister, regUsage)
|
||||
Instruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister)
|
||||
}
|
||||
chunk += VmCodeInstruction(ins)
|
||||
code += VmCodeInstruction(ins)
|
||||
}
|
||||
else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(ret: PtReturn): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
val value = ret.value
|
||||
if(value!=null) {
|
||||
// Call Convention: return value is always returned in r0
|
||||
chunk += expressionEval.translateExpression(value, 0, RegisterUsage(1))
|
||||
code += expressionEval.translateExpression(value, 0, RegisterUsage(1))
|
||||
}
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.RETURN))
|
||||
return chunk
|
||||
code += VmCodeInstruction(Instruction(Opcode.RETURN))
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(sub: PtSub, regUsage: RegisterUsage): VmCodeChunk {
|
||||
// TODO actually inline subroutines marked as inline
|
||||
val chunk = VmCodeChunk()
|
||||
chunk += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
|
||||
chunk += VmCodeLabel(sub.scopedName)
|
||||
val code = VmCodeChunk()
|
||||
code += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
|
||||
code += VmCodeLabel(sub.scopedName)
|
||||
for (child in sub.children) {
|
||||
chunk += translateNode(child, regUsage)
|
||||
code += translateNode(child, regUsage)
|
||||
}
|
||||
chunk += VmCodeComment("SUB-END '${sub.name}'")
|
||||
return chunk
|
||||
code += VmCodeComment("SUB-END '${sub.name}'")
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(block: PtBlock, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
chunk += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
|
||||
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
|
||||
chunk += translateNode(child, regUsage)
|
||||
code += translateNode(child, regUsage)
|
||||
}
|
||||
chunk += VmCodeComment("BLOCK-END '${block.name}'")
|
||||
return chunk
|
||||
code += VmCodeComment("BLOCK-END '${block.name}'")
|
||||
return code
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,16 +23,16 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
fun translateExpression(expr: PtExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
require(regUsage.firstFree > resultRegister)
|
||||
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
|
||||
when (expr) {
|
||||
is PtNumber -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt()))
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt()))
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val mem = codeGen.allocations.get(expr.targetName)
|
||||
chunk += if(expr.type in PassByValueDatatypes) {
|
||||
code += if(expr.type in PassByValueDatatypes) {
|
||||
VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem))
|
||||
} else {
|
||||
// for strings and arrays etc., load the *address* of the value instead
|
||||
@ -41,19 +41,19 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
val mem = codeGen.allocations.get(expr.identifier.targetName)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem))
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem))
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val addressRegister = regUsage.nextFree()
|
||||
val addressExprCode = translateExpression(expr.address, addressRegister, regUsage)
|
||||
chunk += addressExprCode
|
||||
code += addressExprCode
|
||||
}
|
||||
is PtTypeCast -> chunk += translate(expr, resultRegister, regUsage)
|
||||
is PtPrefix -> chunk += translate(expr, resultRegister, regUsage)
|
||||
is PtArrayIndexer -> chunk += translate(expr, resultRegister, regUsage)
|
||||
is PtBinaryExpression -> chunk += translate(expr, resultRegister, regUsage)
|
||||
is PtBuiltinFunctionCall -> chunk += translate(expr, resultRegister, regUsage)
|
||||
is PtFunctionCall -> chunk += translate(expr, resultRegister, regUsage)
|
||||
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 += translate(expr, resultRegister, regUsage)
|
||||
is PtFunctionCall -> code += translate(expr, resultRegister, regUsage)
|
||||
is PtContainmentCheck -> TODO()
|
||||
is PtPipe -> TODO()
|
||||
is PtRange,
|
||||
@ -61,60 +61,59 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||
else -> throw AssemblyError("weird expression")
|
||||
}
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
|
||||
val vmDt = codeGen.vmType(arrayIx.type)
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
val idxReg = regUsage.nextFree()
|
||||
chunk += translateExpression(arrayIx.index, idxReg, regUsage)
|
||||
code += translateExpression(arrayIx.index, idxReg, regUsage)
|
||||
if(eltSize>1) {
|
||||
val factorReg = regUsage.nextFree()
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize))
|
||||
code += VmCodeInstruction(Instruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg))
|
||||
}
|
||||
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation))
|
||||
return chunk
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation))
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(expr: PtPrefix, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
// operator can be: +, -, ~, not
|
||||
val chunk = VmCodeChunk()
|
||||
chunk += translateExpression(expr.value, resultRegister, regUsage)
|
||||
val code = VmCodeChunk()
|
||||
code += translateExpression(expr.value, resultRegister, regUsage)
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
when(expr.operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister))
|
||||
code += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister))
|
||||
}
|
||||
"~" -> {
|
||||
val regMask = regUsage.nextFree()
|
||||
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask))
|
||||
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
|
||||
}
|
||||
"not" -> {
|
||||
val label = codeGen.createLabelName()
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister), labelArg = label)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1))
|
||||
chunk += VmCodeLabel(label)
|
||||
code += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister), labelArg = label)
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1))
|
||||
code += VmCodeLabel(label)
|
||||
val regMask = regUsage.nextFree()
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=1))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=1))
|
||||
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(cast: PtTypeCast, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
if(cast.type==cast.value.type)
|
||||
return chunk
|
||||
chunk += translateExpression(cast.value, resultRegister, regUsage)
|
||||
return code
|
||||
code += translateExpression(cast.value, resultRegister, regUsage)
|
||||
when(cast.type) {
|
||||
DataType.UBYTE -> {
|
||||
when(cast.value.type) {
|
||||
@ -138,11 +137,11 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
when(cast.value.type) {
|
||||
DataType.BYTE -> {
|
||||
// byte -> uword: sign extend
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
code += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
// ubyte -> uword: sign extend
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
code += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
}
|
||||
DataType.WORD -> { }
|
||||
DataType.FLOAT -> {
|
||||
@ -155,11 +154,11 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
when(cast.value.type) {
|
||||
DataType.BYTE -> {
|
||||
// byte -> word: sign extend
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
code += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
// byte -> word: sign extend
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
code += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
|
||||
}
|
||||
DataType.UWORD -> { }
|
||||
DataType.FLOAT -> {
|
||||
@ -188,105 +187,126 @@ internal class ExpressionGen(val codeGen: CodeGen) {
|
||||
}
|
||||
else -> throw AssemblyError("weird cast type")
|
||||
}
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
val leftResultReg = regUsage.nextFree()
|
||||
val rightResultReg = regUsage.nextFree()
|
||||
val leftCode = translateExpression(binExpr.left, leftResultReg, regUsage)
|
||||
val rightCode = translateExpression(binExpr.right, rightResultReg, regUsage)
|
||||
chunk += leftCode
|
||||
chunk += rightCode
|
||||
// TODO: optimized codegen when left or right operand is known 0 or 1 or whatever.
|
||||
code += leftCode
|
||||
code += rightCode
|
||||
val vmDt = codeGen.vmType(binExpr.type)
|
||||
when(binExpr.operator) {
|
||||
"+" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"-" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"*" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"/" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"%" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
code += VmCodeInstruction(Instruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"|", "or" -> {
|
||||
code += VmCodeInstruction(Instruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"&", "and" -> {
|
||||
code += VmCodeInstruction(Instruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"^", "xor" -> {
|
||||
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"<<" -> {
|
||||
// TODO check if shift amount works
|
||||
code += VmCodeInstruction(Instruction(Opcode.LSL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
">>" -> {
|
||||
// TODO check if shift amount works
|
||||
code += VmCodeInstruction(Instruction(Opcode.LSR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||
}
|
||||
"**" -> throw AssemblyError("** operator requires floating point ${binExpr.position}")
|
||||
// TODO the other operators: "<<", ">>", "==", "!=", "<", ">", "<=", ">="
|
||||
else -> TODO("operator ${binExpr.operator}")
|
||||
}
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
fun translate(fcall: PtFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val subroutine = codeGen.symbolTable.flat.getValue(fcall.functionName) as StSub
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
for ((arg, parameter) in fcall.args.zip(subroutine.parameters)) {
|
||||
val argReg = regUsage.nextFree()
|
||||
chunk += translateExpression(arg, argReg, regUsage)
|
||||
code += translateExpression(arg, argReg, regUsage)
|
||||
val vmDt = codeGen.vmType(parameter.type)
|
||||
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem))
|
||||
code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem))
|
||||
}
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.CALL), labelArg=fcall.functionName)
|
||||
code += VmCodeInstruction(Instruction(Opcode.CALL), labelArg=fcall.functionName)
|
||||
if(!fcall.void && resultRegister!=0) {
|
||||
// Call convention: result value is in r0, so put it in the required register instead.
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0))
|
||||
code += VmCodeInstruction(Instruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0))
|
||||
}
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
val code = VmCodeChunk()
|
||||
when(call.name) {
|
||||
"syscall" -> {
|
||||
val vExpr = call.args.single() as PtNumber
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
|
||||
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
|
||||
}
|
||||
"syscall1" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
|
||||
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
|
||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
||||
chunk += translateExpression(call.args[1], 0, regUsage)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
|
||||
code += translateExpression(call.args[1], 0, regUsage)
|
||||
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
|
||||
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
|
||||
}
|
||||
"syscall2" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
|
||||
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
|
||||
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
|
||||
while(regUsage.firstFree<2) {
|
||||
regUsage.nextFree()
|
||||
}
|
||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
||||
chunk += translateExpression(call.args[1], 0, regUsage)
|
||||
chunk += translateExpression(call.args[2], 1, regUsage)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
|
||||
code += translateExpression(call.args[1], 0, regUsage)
|
||||
code += translateExpression(call.args[2], 1, regUsage)
|
||||
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))
|
||||
}
|
||||
"syscall3" -> {
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2))
|
||||
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()
|
||||
}
|
||||
val callNr = (call.args[0] as PtNumber).number.toInt()
|
||||
chunk += translateExpression(call.args[1], 0, regUsage)
|
||||
chunk += translateExpression(call.args[2], 1, regUsage)
|
||||
chunk += translateExpression(call.args[3], 2, regUsage)
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 2))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1))
|
||||
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
|
||||
code += translateExpression(call.args[1], 0, regUsage)
|
||||
code += translateExpression(call.args[2], 1, regUsage)
|
||||
code += translateExpression(call.args[3], 2, regUsage)
|
||||
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))
|
||||
}
|
||||
else -> {
|
||||
// TODO builtin functions...
|
||||
TODO("builtinfunc ${call.name}")
|
||||
}
|
||||
}
|
||||
return chunk
|
||||
return code
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- vm code gen: don't reuse registers, don't pre allocate variables (except strings + arrays) but instead put them into registers too
|
||||
then we have Static Single Assignment form in the VM code
|
||||
- vm code gen: don't reuse registers and don't pre allocate variables? (except strings + arrays) but instead put them into registers too
|
||||
then we ALMOST have Static Single Assignment form in the VM code. But what can we use that for?
|
||||
- pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls.
|
||||
- writeAssembly(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
|
||||
- make everything an expression? (get rid of Statements. Statements are expressions with void return types?).
|
||||
|
@ -10,6 +10,8 @@ main {
|
||||
syscall1(8, 0) ; enable lo res creen
|
||||
ubyte shifter
|
||||
|
||||
shifter >>= 1
|
||||
|
||||
repeat {
|
||||
uword xx
|
||||
uword yy = 0
|
||||
@ -22,6 +24,9 @@ main {
|
||||
yy++
|
||||
}
|
||||
shifter+=4
|
||||
|
||||
txt.print_ub(shifter)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,11 +104,12 @@ All have type b or w.
|
||||
and reg1, reg2, reg3 - reg1 = reg2 bitwise and reg3
|
||||
or reg1, reg2, reg3 - reg1 = reg2 bitwise or reg3
|
||||
xor reg1, reg2, reg3 - reg1 = reg2 bitwise xor reg3
|
||||
lsr reg1 - reg1 = shift reg1 right by 1 bit
|
||||
lsl reg1 - reg1 = shift reg1 left by 1 bit
|
||||
ror reg1 - reg1 = rotate reg1 right by 1 bit, not using carry
|
||||
rol reg1 - reg1 = rotate reg1 left by 1 bit, not using carry
|
||||
lsr reg1, reg2, reg3 - reg1 = shift reg2 right by reg3 bits
|
||||
lsl reg1, reg2, reg3 - reg1 = shift reg2 left by reg3 bits
|
||||
ror reg1, reg2, reg3 - reg1 = rotate reg2 right by reg3 bits, not using carry
|
||||
rol reg1, reg2, reg3 - reg1 = rotate reg2 left by reg3 bits, not using carry
|
||||
|
||||
TODO add asr? (arithmetical shift right, so with sign bit)
|
||||
TODO also add ror/rol variants using the carry bit? These do map directly on 6502 and 68k instructions.
|
||||
|
||||
|
||||
@ -285,10 +286,10 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.AND to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.OR to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.XOR to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.LSR to InstructionFormat(BW, true, false, false, false),
|
||||
Opcode.LSL to InstructionFormat(BW, true, false, false, false),
|
||||
Opcode.ROR to InstructionFormat(BW, true, false, false, false),
|
||||
Opcode.ROL to InstructionFormat(BW, true, false, false, false),
|
||||
Opcode.LSR to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.LSL to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.ROR to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.ROL to InstructionFormat(BW, true, true, true, false),
|
||||
|
||||
Opcode.COPY to InstructionFormat(NN, true, true, false, true ),
|
||||
Opcode.COPYZ to InstructionFormat(NN, true, true, false, false),
|
||||
|
@ -590,33 +590,37 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
}
|
||||
|
||||
private fun InsLSR(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
||||
when(i.type!!) {
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt() ushr 1).toUByte())
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt() ushr 1).toUShort())
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (left shr right.toInt()).toUByte())
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (left shr right.toInt()).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsLSL(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
||||
when(i.type!!) {
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt() shl 1).toUByte())
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt() shl 1).toUShort())
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (left shl right.toInt()).toUByte())
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (left shl right.toInt()).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsROR(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
||||
when(i.type!!) {
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt().rotateRight(1).toUByte()))
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt().rotateRight(1).toUShort()))
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (left.rotateRight(right.toInt()).toUByte()))
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (left.rotateRight(right.toInt()).toUShort()))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsROL(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
||||
when(i.type!!) {
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt().rotateLeft(1).toUByte()))
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt().rotateLeft(1).toUShort()))
|
||||
VmDataType.BYTE -> registers.setB(i.reg1!!, (left.rotateLeft(right.toInt()).toUByte()))
|
||||
VmDataType.WORD -> registers.setW(i.reg1!!, (left.rotateLeft(right.toInt()).toUShort()))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user