mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 19:54:26 +00:00
working on vm translator
This commit is contained in:
parent
fd581ffc37
commit
06b38506d1
@ -2,22 +2,67 @@ package prog8.codegen.virtual
|
|||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.IAssemblyProgram
|
import prog8.code.core.IAssemblyProgram
|
||||||
|
import prog8.vm.Instruction
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
import java.io.BufferedWriter
|
||||||
import kotlin.io.path.bufferedWriter
|
import kotlin.io.path.bufferedWriter
|
||||||
import kotlin.io.path.div
|
import kotlin.io.path.div
|
||||||
|
|
||||||
|
|
||||||
internal class AssemblyProgram(override val name: String,
|
internal class AssemblyProgram(override val name: String,
|
||||||
private val allocations: VariableAllocator,
|
private val allocations: VariableAllocator
|
||||||
private val instructions: MutableList<String>
|
|
||||||
) : IAssemblyProgram {
|
) : IAssemblyProgram {
|
||||||
|
|
||||||
|
private val globalInits = mutableListOf<VmCodeLine>()
|
||||||
|
private val blocks = mutableListOf<VmCodeChunk>()
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
override fun assemble(options: CompilationOptions): Boolean {
|
||||||
val outfile = options.outputDir / ("$name.p8virt")
|
val outfile = options.outputDir / ("$name.p8virt")
|
||||||
println("write code to ${outfile}")
|
println("write code to ${outfile}")
|
||||||
outfile.bufferedWriter().use {
|
outfile.bufferedWriter().use { out ->
|
||||||
allocations.asVmMemory().forEach { alloc -> it.write(alloc + "\n") }
|
allocations.asVmMemory().forEach { (name, alloc) ->
|
||||||
it.write("------PROGRAM------\n")
|
out.write("; ${name.joinToString(".")}\n")
|
||||||
instructions.forEach { ins -> it.write(ins + "\n") }
|
out.write(alloc + "\n")
|
||||||
|
}
|
||||||
|
out.write("------PROGRAM------\n")
|
||||||
|
|
||||||
|
out.write("; global var inits\n")
|
||||||
|
globalInits.forEach { out.writeLine(it) }
|
||||||
|
|
||||||
|
out.write("; actual program code\n")
|
||||||
|
blocks.asSequence().flatMap { it.lines }.forEach { line->out.writeLine(line) }
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun BufferedWriter.writeLine(line: VmCodeLine) {
|
||||||
|
when(line) {
|
||||||
|
is VmCodeComment -> write("; ${line.comment}\n")
|
||||||
|
is VmCodeInstruction -> write(line.ins.toString() + "\n")
|
||||||
|
is VmCodeLabel -> write("_" + line.name.joinToString(".") + ":\n")
|
||||||
|
is VmCodeOpcodeWithStringArg -> write("${line.opcode.name.lowercase()} ${line.arg}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addGlobalInits(chunk: VmCodeChunk) = globalInits.addAll(chunk.lines)
|
||||||
|
fun addBlock(block: VmCodeChunk) = blocks.add(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal sealed class VmCodeLine
|
||||||
|
|
||||||
|
internal class VmCodeInstruction(val ins: Instruction): VmCodeLine()
|
||||||
|
internal class VmCodeLabel(val name: List<String>): VmCodeLine()
|
||||||
|
internal class VmCodeComment(val comment: String): VmCodeLine()
|
||||||
|
internal class VmCodeOpcodeWithStringArg(val opcode: Opcode, val arg: String): VmCodeLine()
|
||||||
|
|
||||||
|
internal class VmCodeChunk {
|
||||||
|
val lines = mutableListOf<VmCodeLine>()
|
||||||
|
|
||||||
|
operator fun plusAssign(line: VmCodeLine) {
|
||||||
|
lines.add(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun plusAssign(chunk: VmCodeChunk) {
|
||||||
|
lines.addAll(chunk.lines)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.code.ast.PtBuiltinFunctionCall
|
||||||
|
import prog8.code.ast.PtNumber
|
||||||
|
import prog8.vm.Instruction
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
|
||||||
|
internal class BuiltinFunctionsGen(val codegen: CodeGen) {
|
||||||
|
fun translate(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||||
|
val chunk = VmCodeChunk()
|
||||||
|
when(call.name) {
|
||||||
|
"syscall" -> {
|
||||||
|
val vExpr = call.args.single() as PtNumber
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
|
||||||
|
}
|
||||||
|
"syscall1" -> {
|
||||||
|
val vExpr = call.args[0] as PtNumber
|
||||||
|
val vExpr1 = call.args[1] as PtNumber
|
||||||
|
// TODO assign regs
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
|
||||||
|
}
|
||||||
|
"syscall2" -> {
|
||||||
|
val vExpr = call.args[0] as PtNumber
|
||||||
|
val vExpr1 = call.args[1] as PtNumber
|
||||||
|
val vExpr2 = call.args[2] as PtNumber
|
||||||
|
// TODO assign regs
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
|
||||||
|
}
|
||||||
|
"syscall3" -> {
|
||||||
|
val vExpr = call.args[0] as PtNumber
|
||||||
|
val vExpr1 = call.args[1] as PtNumber
|
||||||
|
val vExpr2 = call.args[2] as PtNumber
|
||||||
|
val vExpr3 = call.args[3] as PtNumber
|
||||||
|
// TODO assign regs
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
TODO("builtinfunc ${call.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ import prog8.code.ast.*
|
|||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.vm.Instruction
|
import prog8.vm.Instruction
|
||||||
import prog8.vm.Opcode
|
import prog8.vm.Opcode
|
||||||
|
import prog8.vm.DataType
|
||||||
|
|
||||||
|
|
||||||
class CodeGen(internal val program: PtProgram,
|
class CodeGen(internal val program: PtProgram,
|
||||||
internal val symbolTable: SymbolTable,
|
internal val symbolTable: SymbolTable,
|
||||||
@ -12,6 +14,9 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
internal val errors: IErrorReporter
|
internal val errors: IErrorReporter
|
||||||
): IAssemblyGenerator {
|
): IAssemblyGenerator {
|
||||||
|
|
||||||
|
internal val allocations = VariableAllocator(symbolTable, program, errors)
|
||||||
|
private val builtinFunctions = BuiltinFunctionsGen(this)
|
||||||
|
private val expressionEval = ExpressionGen(this, builtinFunctions)
|
||||||
private val instructions = mutableListOf<String>()
|
private val instructions = mutableListOf<String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -21,54 +26,76 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
|
|
||||||
override fun compileToAssembly(): IAssemblyProgram? {
|
override fun compileToAssembly(): IAssemblyProgram? {
|
||||||
instructions.clear()
|
instructions.clear()
|
||||||
val allocations = VariableAllocator(symbolTable, program, errors)
|
val vmprog = AssemblyProgram(program.name, allocations)
|
||||||
|
|
||||||
outComment("GLOBAL VARS INITS")
|
|
||||||
program.allBlocks().forEach {
|
program.allBlocks().forEach {
|
||||||
it.children
|
it.children
|
||||||
.singleOrNull { node->node is PtScopeVarsInit }
|
.singleOrNull { node->node is PtScopeVarsInit }
|
||||||
?.let { inits->
|
?.let { inits ->
|
||||||
translateNode(inits)
|
vmprog.addGlobalInits(translate(inits as PtScopeVarsInit))
|
||||||
it.children.remove(inits)
|
it.children.remove(inits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
outComment("PROGRAM CODE")
|
|
||||||
for (block in program.allBlocks()) {
|
for (block in program.allBlocks()) {
|
||||||
translateNode(block)
|
vmprog.addBlock(translate(block))
|
||||||
}
|
}
|
||||||
return AssemblyProgram(program.name, allocations, instructions)
|
|
||||||
|
return vmprog
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun out(ins: Instruction) {
|
private fun translate(assignment: PtAssignment): VmCodeChunk {
|
||||||
instructions.add(ins.toString())
|
val chunk = VmCodeChunk()
|
||||||
|
val (expressionChunk, resultRegister) = expressionEval.translateExpression(assignment.value)
|
||||||
|
chunk += expressionChunk
|
||||||
|
val ident = assignment.target.identifier
|
||||||
|
val memory = assignment.target.memory
|
||||||
|
val array = assignment.target.array
|
||||||
|
val vmDt = vmType(assignment.value.type)
|
||||||
|
if(ident!=null) {
|
||||||
|
val address = allocations.get(ident.targetName)
|
||||||
|
val ins = Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address)
|
||||||
|
chunk += VmCodeInstruction(ins)
|
||||||
|
}
|
||||||
|
else if(array!=null) {
|
||||||
|
TODO("assign to array")
|
||||||
|
}
|
||||||
|
else if(memory!=null) {
|
||||||
|
val ins =
|
||||||
|
if(memory.address is PtNumber) {
|
||||||
|
Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
||||||
|
} else {
|
||||||
|
// TODO make sure the registers used in this eval don't overlap with the one above
|
||||||
|
val (addrExpressionChunk, addressRegister) = expressionEval.translateExpression(assignment.value)
|
||||||
|
chunk += addrExpressionChunk
|
||||||
|
Instruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister)
|
||||||
|
}
|
||||||
|
chunk += VmCodeInstruction(ins)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw AssemblyError("weird assigntarget")
|
||||||
|
return chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outComment(ins: String) {
|
private fun translateNode(node: PtNode): VmCodeChunk {
|
||||||
instructions.add("; $ins")
|
return when(node) {
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateNode(node: PtNode) {
|
|
||||||
when(node) {
|
|
||||||
is PtBlock -> translate(node)
|
is PtBlock -> translate(node)
|
||||||
is PtSub -> translate(node)
|
is PtSub -> translate(node)
|
||||||
is PtScopeVarsDecls -> { /* vars should be looked up via symbol table */ }
|
is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table
|
||||||
is PtVariable -> { /* var should be looked up via symbol table */ }
|
is PtVariable -> VmCodeChunk() // var should be looked up via symbol table
|
||||||
is PtMemMapped -> { /* memmapped var should be looked up via symbol table */ }
|
is PtMemMapped -> VmCodeChunk() // memmapped var should be looked up via symbol table
|
||||||
is PtConstant -> { /* constants have all been folded into the code */ }
|
is PtConstant -> VmCodeChunk() // constants have all been folded into the code
|
||||||
is PtAssignTarget -> TODO()
|
is PtAssignTarget -> TODO()
|
||||||
is PtAssignment -> translate(node)
|
is PtAssignment -> translate(node)
|
||||||
is PtScopeVarsInit -> translate(node)
|
is PtScopeVarsInit -> translate(node)
|
||||||
is PtBreakpoint -> translate(node)
|
|
||||||
is PtConditionalBranch -> TODO()
|
is PtConditionalBranch -> TODO()
|
||||||
is PtAddressOf -> TODO()
|
is PtAddressOf -> TODO()
|
||||||
is PtArrayIndexer -> TODO()
|
is PtArrayIndexer -> TODO()
|
||||||
is PtArrayLiteral -> TODO()
|
is PtArrayLiteral -> TODO()
|
||||||
is PtBinaryExpression -> TODO()
|
is PtBinaryExpression -> TODO()
|
||||||
is PtBuiltinFunctionCall -> TODO()
|
is PtBuiltinFunctionCall -> builtinFunctions.translate(node)
|
||||||
is PtContainmentCheck -> TODO()
|
is PtContainmentCheck -> TODO()
|
||||||
is PtFunctionCall -> TODO()
|
is PtFunctionCall -> translate(node)
|
||||||
is PtIdentifier -> TODO()
|
is PtIdentifier -> TODO()
|
||||||
is PtMemoryByte -> TODO()
|
is PtMemoryByte -> TODO()
|
||||||
is PtNumber -> TODO()
|
is PtNumber -> TODO()
|
||||||
@ -78,55 +105,103 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
is PtString -> TODO()
|
is PtString -> TODO()
|
||||||
is PtTypeCast -> TODO()
|
is PtTypeCast -> TODO()
|
||||||
is PtForLoop -> TODO()
|
is PtForLoop -> TODO()
|
||||||
is PtGosub -> TODO()
|
is PtGosub -> translate(node)
|
||||||
is PtIfElse -> TODO()
|
is PtIfElse -> TODO()
|
||||||
is PtIncludeBinary -> TODO()
|
|
||||||
is PtJump -> TODO()
|
is PtJump -> TODO()
|
||||||
is PtNodeGroup -> TODO()
|
is PtNodeGroup -> TODO()
|
||||||
is PtNop -> { }
|
is PtNop -> VmCodeChunk()
|
||||||
is PtPostIncrDecr -> TODO()
|
is PtPostIncrDecr -> TODO()
|
||||||
is PtProgram -> TODO()
|
is PtProgram -> TODO()
|
||||||
is PtRepeatLoop -> TODO()
|
is PtRepeatLoop -> TODO()
|
||||||
is PtReturn -> TODO()
|
is PtReturn -> translate(node)
|
||||||
is PtSubroutineParameter -> TODO()
|
is PtSubroutineParameter -> TODO()
|
||||||
is PtWhen -> TODO()
|
is PtWhen -> TODO()
|
||||||
is PtWhenChoice -> TODO()
|
is PtWhenChoice -> TODO()
|
||||||
|
is PtLabel -> TODO()
|
||||||
|
is PtBreakpoint -> TODO()
|
||||||
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target")
|
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target")
|
||||||
is PtInlineAssembly -> throw AssemblyError("inline assembly 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")
|
||||||
else -> TODO("missing codegen for $node")
|
else -> TODO("missing codegen for $node")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(breakpoint: PtBreakpoint) {
|
private fun translate(fcall: PtFunctionCall): VmCodeChunk {
|
||||||
out(Instruction(Opcode.BREAKPOINT))
|
val chunk = VmCodeChunk()
|
||||||
|
// TODO evaluate function call arguments
|
||||||
|
chunk += VmCodeOpcodeWithStringArg(Opcode.GOSUB, gosubArg(fcall.functionName))
|
||||||
|
return chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(init: PtScopeVarsInit) {
|
private fun translate(gosub: PtGosub): VmCodeChunk {
|
||||||
init.children.forEach { translateNode(it) }
|
val chunk = VmCodeChunk()
|
||||||
|
if(gosub.address!=null)
|
||||||
|
throw AssemblyError("cannot gosub to a memory address in the vm target")
|
||||||
|
else if(gosub.identifier!=null) {
|
||||||
|
chunk += VmCodeOpcodeWithStringArg(Opcode.GOSUB, gosubArg(gosub.identifier!!.targetName))
|
||||||
|
} else if(gosub.generatedLabel!=null) {
|
||||||
|
chunk += VmCodeOpcodeWithStringArg(Opcode.GOSUB, gosubArg(listOf(gosub.generatedLabel!!)))
|
||||||
|
}
|
||||||
|
return chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(sub: PtSub) {
|
private fun translate(ret: PtReturn): VmCodeChunk {
|
||||||
outComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
|
val chunk = VmCodeChunk()
|
||||||
|
val value = ret.value
|
||||||
|
if(value!=null) {
|
||||||
|
val (expressionChunk, resultRegister) = expressionEval.translateExpression(value)
|
||||||
|
chunk += expressionChunk
|
||||||
|
if(resultRegister!=0)
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.LOADR, vmType(value.type), reg1=0, reg2=resultRegister))
|
||||||
|
}
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.RETURN))
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun vmType(type: prog8.code.core.DataType): DataType {
|
||||||
|
return when(type) {
|
||||||
|
prog8.code.core.DataType.UBYTE,
|
||||||
|
prog8.code.core.DataType.BYTE -> DataType.BYTE
|
||||||
|
prog8.code.core.DataType.UWORD,
|
||||||
|
prog8.code.core.DataType.WORD -> DataType.WORD
|
||||||
|
in PassByReferenceDatatypes -> DataType.WORD
|
||||||
|
else -> throw AssemblyError("no vm datatype for $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(init: PtScopeVarsInit): VmCodeChunk {
|
||||||
|
val chunk = VmCodeChunk()
|
||||||
|
init.children.forEach { chunk += translateNode(it) }
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(sub: PtSub): VmCodeChunk {
|
||||||
|
val chunk = VmCodeChunk()
|
||||||
|
chunk += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
|
||||||
|
chunk += VmCodeLabel(sub.scopedName)
|
||||||
sub.children
|
sub.children
|
||||||
.singleOrNull { it is PtScopeVarsInit }
|
.singleOrNull { it is PtScopeVarsInit }
|
||||||
?.let { inits ->
|
?.let { inits ->
|
||||||
sub.children.remove(inits)
|
sub.children.remove(inits)
|
||||||
translateNode(inits)
|
chunk += translateNode(inits)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO rest
|
for (child in sub.children) {
|
||||||
outComment("SUB-END ${sub.scopedName}\n")
|
chunk += translateNode(child)
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(assign: PtAssignment) {
|
|
||||||
outComment("ASSIGN: ${assign.target.identifier?.targetName} = ${assign.value}")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(block: PtBlock) {
|
|
||||||
outComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
|
|
||||||
for (child in block.children) {
|
|
||||||
translateNode(child)
|
|
||||||
}
|
}
|
||||||
outComment("BLOCK-END '${block.name}'\n")
|
chunk += VmCodeComment("SUB-END '${sub.name}'")
|
||||||
|
return chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translate(block: PtBlock): VmCodeChunk {
|
||||||
|
val chunk = VmCodeChunk()
|
||||||
|
chunk += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
|
||||||
|
for (child in block.children)
|
||||||
|
chunk += translateNode(child)
|
||||||
|
chunk += VmCodeComment("BLOCK-END '${block.name}'")
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun gosubArg(targetName: List<String>) = "_${targetName.joinToString(".")}"
|
||||||
}
|
}
|
||||||
|
83
codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt
Normal file
83
codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package prog8.codegen.virtual
|
||||||
|
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.AssemblyError
|
||||||
|
import prog8.vm.Instruction
|
||||||
|
import prog8.vm.Opcode
|
||||||
|
|
||||||
|
internal class ExpressionGen(val codeGen: CodeGen, val builtinFunctions: BuiltinFunctionsGen) {
|
||||||
|
fun translateExpression(expr: PtExpression): Pair<VmCodeChunk, Int> {
|
||||||
|
// TODO("Not yet implemented")
|
||||||
|
val chunk = VmCodeChunk()
|
||||||
|
val vmDt = codeGen.vmType(expr.type)
|
||||||
|
val resultRegister = 0 // TODO need a way to make this dynamic to avoid clobbering existing registers
|
||||||
|
|
||||||
|
fun process(code: VmCodeChunk, actualResultReg: Int) {
|
||||||
|
chunk += code
|
||||||
|
if(actualResultReg!=resultRegister)
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=actualResultReg))
|
||||||
|
}
|
||||||
|
|
||||||
|
when (expr) {
|
||||||
|
is PtNumber -> {
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt()))
|
||||||
|
}
|
||||||
|
is PtIdentifier -> {
|
||||||
|
val mem = codeGen.allocations.get(expr.targetName)
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem))
|
||||||
|
}
|
||||||
|
is PtAddressOf -> {
|
||||||
|
val mem = codeGen.allocations.get(expr.identifier.targetName)
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem))
|
||||||
|
}
|
||||||
|
is PtMemoryByte -> {
|
||||||
|
val (addressExprCode, addressRegister) = translateExpression(expr.address)
|
||||||
|
process(addressExprCode, addressRegister)
|
||||||
|
}
|
||||||
|
is PtTypeCast -> TODO()
|
||||||
|
is PtPrefix -> TODO()
|
||||||
|
is PtArrayIndexer -> TODO()
|
||||||
|
is PtBinaryExpression -> {
|
||||||
|
val (exprCode, functionResultReg) = translate(expr)
|
||||||
|
process(exprCode, functionResultReg)
|
||||||
|
}
|
||||||
|
is PtBuiltinFunctionCall -> TODO()
|
||||||
|
is PtContainmentCheck -> TODO()
|
||||||
|
is PtFunctionCall -> {
|
||||||
|
val (callCode, functionResultReg) = translate(expr)
|
||||||
|
process(callCode, functionResultReg)
|
||||||
|
}
|
||||||
|
is PtPipe -> TODO()
|
||||||
|
is PtRange -> TODO()
|
||||||
|
is PtArrayLiteral -> TODO()
|
||||||
|
is PtString -> TODO()
|
||||||
|
else -> throw AssemblyError("weird expression")
|
||||||
|
}
|
||||||
|
return Pair(chunk, resultRegister)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(binExpr: PtBinaryExpression): Pair<VmCodeChunk, Int> {
|
||||||
|
val chunk = VmCodeChunk()
|
||||||
|
val (leftCode, leftResultReg) = translateExpression(binExpr.left)
|
||||||
|
val (rightCode, rightResultReg) = translateExpression(binExpr.right)
|
||||||
|
chunk += leftCode
|
||||||
|
chunk += rightCode
|
||||||
|
val resultRegister = 0 // TODO binexpr result can't always be in r0...
|
||||||
|
when(binExpr.operator) {
|
||||||
|
"+" -> {
|
||||||
|
chunk += VmCodeInstruction(Instruction(Opcode.ADD, codeGen.vmType(binExpr.type), reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
|
||||||
|
}
|
||||||
|
else -> TODO("operator ${binExpr.operator}")
|
||||||
|
}
|
||||||
|
return Pair(chunk, resultRegister)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(fcall: PtFunctionCall): Pair<VmCodeChunk, Int> {
|
||||||
|
require(!fcall.void)
|
||||||
|
val chunk = VmCodeChunk()
|
||||||
|
// TODO evaluate arguments
|
||||||
|
chunk += VmCodeOpcodeWithStringArg(Opcode.GOSUB, codeGen.gosubArg(fcall.functionName))
|
||||||
|
return Pair(chunk, 0) // TODO function result always in r0?
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,13 +27,10 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg
|
|||||||
freeStart = nextLocation
|
freeStart = nextLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asVmMemory(): List<String> {
|
fun get(name: List<String>) = allocations.getValue(name)
|
||||||
/*
|
|
||||||
$4000 strz "Hello from program! "derp" bye.\n"
|
fun asVmMemory(): List<Pair<List<String>, String>> {
|
||||||
$2000 ubyte 65,66,67,68,0
|
val mm = mutableListOf<Pair<List<String>, String>>()
|
||||||
$2100 uword $1111,$2222,$3333,$4444
|
|
||||||
*/
|
|
||||||
val mm = mutableListOf<String>()
|
|
||||||
for (variable in st.allVariables) {
|
for (variable in st.allVariables) {
|
||||||
val location = allocations.getValue(variable.scopedName)
|
val location = allocations.getValue(variable.scopedName)
|
||||||
val typeStr = when(variable.dt) {
|
val typeStr = when(variable.dt) {
|
||||||
@ -67,7 +64,7 @@ $2100 uword $1111,$2222,$3333,$4444
|
|||||||
}
|
}
|
||||||
else -> throw InternalCompilerException("weird dt")
|
else -> throw InternalCompilerException("weird dt")
|
||||||
}
|
}
|
||||||
mm.add("${location.toHex()} $typeStr $value")
|
mm.add(Pair(variable.scopedName, "${location.toHex()} $typeStr $value"))
|
||||||
}
|
}
|
||||||
return mm
|
return mm
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,10 @@ TODO
|
|||||||
|
|
||||||
For next release
|
For next release
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
- first for virtual target: do not create tempvars for return statements (StatementOptimizer)
|
||||||
|
maybe other places as well?
|
||||||
|
- in new AST: combine param assignments + GoSub, back into PtFunctionCall node. PtGosub node should not exist.
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,65 +1,16 @@
|
|||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
main {
|
main {
|
||||||
ubyte[256] sieve
|
ubyte variable = 42
|
||||||
ubyte candidate_prime = 2 ; is increased in the loop
|
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
sys.memset(sieve, 256, false) ; clear the sieve, to reset starting situation on subsequent runs
|
txt.print("value=")
|
||||||
|
txt.print_ub(variable)
|
||||||
|
|
||||||
syscall(1)
|
variable = foo()
|
||||||
syscall1(1, 1111)
|
|
||||||
syscall2(1, 1111, 2222)
|
|
||||||
syscall3(1, 1111, 2222, 3333)
|
|
||||||
|
|
||||||
%breakpoint
|
|
||||||
%asmbinary "LICENSE", 10 ,1
|
|
||||||
|
|
||||||
%asm {{
|
|
||||||
nop
|
|
||||||
}}
|
|
||||||
|
|
||||||
|
|
||||||
; calculate primes
|
|
||||||
txt.print("prime numbers up to 255:\n\n")
|
|
||||||
ubyte amount=0
|
|
||||||
repeat {
|
|
||||||
ubyte prime = find_next_prime()
|
|
||||||
if prime==0
|
|
||||||
break
|
|
||||||
txt.print_ub(prime)
|
|
||||||
txt.print(", ")
|
|
||||||
amount++
|
|
||||||
}
|
|
||||||
txt.nl()
|
|
||||||
txt.print("number of primes (expected 54): ")
|
|
||||||
txt.print_ub(amount)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
uword[] @shared array = [111,222,333,444,555]
|
|
||||||
|
|
||||||
amount = amount |> sin8u() |> cos8u() |> sin8u()
|
|
||||||
|
|
||||||
; test_stack.test()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub find_next_prime() -> ubyte {
|
sub foo() -> ubyte {
|
||||||
|
return variable+1
|
||||||
while sieve[candidate_prime] {
|
|
||||||
candidate_prime++
|
|
||||||
if candidate_prime==0
|
|
||||||
return 0 ; we wrapped; no more primes available in the sieve
|
|
||||||
}
|
|
||||||
|
|
||||||
; found next one, mark the multiples and return it.
|
|
||||||
sieve[candidate_prime] = true
|
|
||||||
uword multiple = candidate_prime
|
|
||||||
|
|
||||||
|
|
||||||
while multiple < len(sieve) {
|
|
||||||
sieve[lsb(multiple)] = true
|
|
||||||
multiple += candidate_prime
|
|
||||||
}
|
|
||||||
return candidate_prime
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ class Assembler {
|
|||||||
fun initializeMemory(memsrc: String, memory: Memory) {
|
fun initializeMemory(memsrc: String, memory: Memory) {
|
||||||
val instrPattern = Regex("""(.+?)\s+([a-z]+)\s+(.+)""", RegexOption.IGNORE_CASE)
|
val instrPattern = Regex("""(.+?)\s+([a-z]+)\s+(.+)""", RegexOption.IGNORE_CASE)
|
||||||
for(line in memsrc.lines()) {
|
for(line in memsrc.lines()) {
|
||||||
if(line.isBlank())
|
if(line.isBlank() || line.startsWith(';'))
|
||||||
continue
|
continue
|
||||||
val match = instrPattern.matchEntire(line.trim())
|
val match = instrPattern.matchEntire(line.trim())
|
||||||
if(match==null)
|
if(match==null)
|
||||||
@ -54,7 +54,7 @@ class Assembler {
|
|||||||
placeholders.clear()
|
placeholders.clear()
|
||||||
val program = mutableListOf<Instruction>()
|
val program = mutableListOf<Instruction>()
|
||||||
val instructionPattern = Regex("""([a-z]+)(\.b|\.w)?(.*)""", RegexOption.IGNORE_CASE)
|
val instructionPattern = Regex("""([a-z]+)(\.b|\.w)?(.*)""", RegexOption.IGNORE_CASE)
|
||||||
val labelPattern = Regex("""_([a-z0-9]+):""")
|
val labelPattern = Regex("""_([a-z0-9\._]+):""")
|
||||||
for (line in source.lines()) {
|
for (line in source.lines()) {
|
||||||
if(line.isBlank() || line.startsWith(';'))
|
if(line.isBlank() || line.startsWith(';'))
|
||||||
continue
|
continue
|
||||||
|
@ -24,7 +24,7 @@ Currently NO support for 24 or 32 bits, and FLOATING POINT is not implemented ye
|
|||||||
*only* LOAD AND STORE instructions have a possible memory operand, all other instructions use only registers or immediate value.
|
*only* LOAD AND STORE instructions have a possible memory operand, all other instructions use only registers or immediate value.
|
||||||
|
|
||||||
|
|
||||||
LOAD/STORE -- all have type b/w/f. (note: float not yet implemented)
|
LOAD/STORE -- all have type b/w
|
||||||
|
|
||||||
|
|
||||||
load reg1, value - load immediate value into register
|
load reg1, value - load immediate value into register
|
||||||
@ -49,9 +49,9 @@ Subroutine parameters set in Reg 0, 1, 2... before gosub.
|
|||||||
Return value in Reg 0 before return.
|
Return value in Reg 0 before return.
|
||||||
|
|
||||||
jump location - continue running at instruction number given by location
|
jump location - continue running at instruction number given by location
|
||||||
jumpi reg1 - continue running at instruction number given by reg1
|
jumpi reg1 - continue running at instruction number in reg1
|
||||||
gosub location - save current instruction location+1, continue execution at location
|
gosub location - save current instruction location+1, continue execution at location
|
||||||
gosubi reg1 - gosub to subroutine at instruction number given by reg1
|
gosubi reg1 - gosub to subroutine at instruction number in reg1
|
||||||
syscall value - do a systemcall identified by call number
|
syscall value - do a systemcall identified by call number
|
||||||
return - restore last saved instruction location and continue at that instruction
|
return - restore last saved instruction location and continue at that instruction
|
||||||
|
|
||||||
@ -70,14 +70,12 @@ bge reg1, reg2, value - jump to location in program given by val
|
|||||||
bges reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (signed)
|
bges reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (signed)
|
||||||
|
|
||||||
|
|
||||||
ARITHMETIC - all have a type of b/w/f. (note: float not yet implemented)
|
INTEGER ARITHMETIC - all have a type of b/w.
|
||||||
(note: for calculations, all types -result, and both operands- are identical)
|
(note: the types of the result and both operands, are all identical UNLESS OTHERWISE NOTED).
|
||||||
|
|
||||||
neg reg1, reg2 - reg1 = sign negation of reg2
|
neg reg1, reg2 - reg1 = sign negation of reg2
|
||||||
add reg1, reg2, reg3 - reg1 = reg2+reg3 (unsigned + signed)
|
add reg1, reg2, reg3 - reg1 = reg2+reg3 (unsigned + signed)
|
||||||
addi reg1, reg2, value - reg1 = reg2+value (unsigned + signed)
|
|
||||||
sub reg1, reg2, reg3 - reg1 = reg2-reg3 (unsigned + signed)
|
sub reg1, reg2, reg3 - reg1 = reg2-reg3 (unsigned + signed)
|
||||||
subi reg1, reg2, value - reg1 = reg2-value (unsigned + signed)
|
|
||||||
ext reg1, reg2 - reg1 = unsigned extension of reg2 (which in practice just means clearing the MSB / MSW) (latter not yet implemented as we don't have longs yet)
|
ext reg1, reg2 - reg1 = unsigned extension of reg2 (which in practice just means clearing the MSB / MSW) (latter not yet implemented as we don't have longs yet)
|
||||||
exts reg1, reg2 - reg1 = signed extension of reg2 (byte to word, or word to long) (note: latter ext.w, not yet implemented as we don't have longs yet)
|
exts reg1, reg2 - reg1 = signed extension of reg2 (byte to word, or word to long) (note: latter ext.w, not yet implemented as we don't have longs yet)
|
||||||
mul reg1, reg2, reg3 - unsigned multiply reg1=reg2*reg3 note: byte*byte->byte, no type extension to word!
|
mul reg1, reg2, reg3 - unsigned multiply reg1=reg2*reg3 note: byte*byte->byte, no type extension to word!
|
||||||
@ -145,9 +143,7 @@ enum class Opcode {
|
|||||||
|
|
||||||
NEG,
|
NEG,
|
||||||
ADD,
|
ADD,
|
||||||
ADDI,
|
|
||||||
SUB,
|
SUB,
|
||||||
SUBI,
|
|
||||||
MUL,
|
MUL,
|
||||||
DIV,
|
DIV,
|
||||||
EXT,
|
EXT,
|
||||||
@ -171,7 +167,7 @@ enum class Opcode {
|
|||||||
enum class DataType {
|
enum class DataType {
|
||||||
BYTE,
|
BYTE,
|
||||||
WORD
|
WORD
|
||||||
// TODO add LONG? FLOAT?
|
// TODO add INT (32-bit)?
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Instruction(
|
data class Instruction(
|
||||||
@ -190,22 +186,22 @@ data class Instruction(
|
|||||||
else -> result.add(" ")
|
else -> result.add(" ")
|
||||||
}
|
}
|
||||||
reg1?.let {
|
reg1?.let {
|
||||||
result.add(it.toString())
|
result.add("r$it")
|
||||||
result.add(",")
|
result.add(",")
|
||||||
}
|
}
|
||||||
reg2?.let {
|
reg2?.let {
|
||||||
result.add(it.toString())
|
result.add("r$it")
|
||||||
result.add(",")
|
result.add(",")
|
||||||
}
|
}
|
||||||
reg3?.let {
|
reg3?.let {
|
||||||
result.add(it.toString())
|
result.add("r$it")
|
||||||
result.add(",")
|
result.add(",")
|
||||||
}
|
}
|
||||||
value?.let {
|
value?.let {
|
||||||
result.add(it.toString())
|
result.add(it.toString())
|
||||||
}
|
}
|
||||||
if(result.last() == ",")
|
if(result.last() == ",")
|
||||||
result.dropLast(1)
|
result.removeLast()
|
||||||
return result.joinToString("").trimEnd()
|
return result.joinToString("").trimEnd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,9 +249,7 @@ val instructionFormats = mutableMapOf(
|
|||||||
|
|
||||||
Opcode.NEG to InstructionFormat(BW, true, true, false, false),
|
Opcode.NEG to InstructionFormat(BW, true, true, false, false),
|
||||||
Opcode.ADD to InstructionFormat(BW, true, true, true, false),
|
Opcode.ADD to InstructionFormat(BW, true, true, true, false),
|
||||||
Opcode.ADDI to InstructionFormat(BW, true, true, false, true ),
|
|
||||||
Opcode.SUB to InstructionFormat(BW, true, true, true, false),
|
Opcode.SUB to InstructionFormat(BW, true, true, true, false),
|
||||||
Opcode.SUBI to InstructionFormat(BW, true, true, false, true ),
|
|
||||||
Opcode.MUL to InstructionFormat(BW, true, true, true, false),
|
Opcode.MUL to InstructionFormat(BW, true, true, true, false),
|
||||||
Opcode.DIV to InstructionFormat(BW, true, true, true, false),
|
Opcode.DIV to InstructionFormat(BW, true, true, true, false),
|
||||||
Opcode.EXT to InstructionFormat(BW, true, true, false, false),
|
Opcode.EXT to InstructionFormat(BW, true, true, false, false),
|
||||||
|
@ -109,9 +109,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
Opcode.BGES -> InsBGES(ins)
|
Opcode.BGES -> InsBGES(ins)
|
||||||
Opcode.NEG -> InsNEG(ins)
|
Opcode.NEG -> InsNEG(ins)
|
||||||
Opcode.ADD -> InsADD(ins)
|
Opcode.ADD -> InsADD(ins)
|
||||||
Opcode.ADDI -> InsADDI(ins)
|
|
||||||
Opcode.SUB -> InsSUB(ins)
|
Opcode.SUB -> InsSUB(ins)
|
||||||
Opcode.SUBI -> InsSUBI(ins)
|
|
||||||
Opcode.MUL -> InsMul(ins)
|
Opcode.MUL -> InsMul(ins)
|
||||||
Opcode.DIV -> InsDiv(ins)
|
Opcode.DIV -> InsDiv(ins)
|
||||||
Opcode.EXT -> InsEXT(ins)
|
Opcode.EXT -> InsEXT(ins)
|
||||||
@ -474,14 +472,6 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
registers.setW(reg1, result.toUShort())
|
registers.setW(reg1, result.toUShort())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun InsADDI(i: Instruction) {
|
|
||||||
when(i.type!!) {
|
|
||||||
DataType.BYTE -> arithByte("+", i.reg1!!, i.reg2!!, null, i.value!!.toUByte())
|
|
||||||
DataType.WORD -> arithWord("+", i.reg1!!, i.reg2!!, null, i.value!!.toUShort())
|
|
||||||
}
|
|
||||||
pc++
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun InsSUB(i: Instruction) {
|
private fun InsSUB(i: Instruction) {
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
DataType.BYTE -> arithByte("-", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
DataType.BYTE -> arithByte("-", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||||
@ -490,14 +480,6 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
pc++
|
pc++
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun InsSUBI(i: Instruction) {
|
|
||||||
when(i.type!!) {
|
|
||||||
DataType.BYTE -> arithByte("-", i.reg1!!, i.reg2!!, null, i.value!!.toUByte())
|
|
||||||
DataType.WORD -> arithWord("-", i.reg1!!, i.reg2!!, null, i.value!!.toUShort())
|
|
||||||
}
|
|
||||||
pc++
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun InsEXT(i: Instruction) {
|
private fun InsEXT(i: Instruction) {
|
||||||
when(i.type!!){
|
when(i.type!!){
|
||||||
DataType.BYTE -> registers.setW(i.reg1!!, registers.getB(i.reg2!!).toUShort())
|
DataType.BYTE -> registers.setW(i.reg1!!, registers.getB(i.reg2!!).toUShort())
|
||||||
|
Loading…
Reference in New Issue
Block a user