mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +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.IAssemblyProgram
|
||||
import prog8.vm.Instruction
|
||||
import prog8.vm.Opcode
|
||||
import java.io.BufferedWriter
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.div
|
||||
|
||||
|
||||
internal class AssemblyProgram(override val name: String,
|
||||
private val allocations: VariableAllocator,
|
||||
private val instructions: MutableList<String>
|
||||
private val allocations: VariableAllocator
|
||||
) : IAssemblyProgram {
|
||||
|
||||
private val globalInits = mutableListOf<VmCodeLine>()
|
||||
private val blocks = mutableListOf<VmCodeChunk>()
|
||||
|
||||
override fun assemble(options: CompilationOptions): Boolean {
|
||||
val outfile = options.outputDir / ("$name.p8virt")
|
||||
println("write code to ${outfile}")
|
||||
outfile.bufferedWriter().use {
|
||||
allocations.asVmMemory().forEach { alloc -> it.write(alloc + "\n") }
|
||||
it.write("------PROGRAM------\n")
|
||||
instructions.forEach { ins -> it.write(ins + "\n") }
|
||||
outfile.bufferedWriter().use { out ->
|
||||
allocations.asVmMemory().forEach { (name, alloc) ->
|
||||
out.write("; ${name.joinToString(".")}\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
|
||||
}
|
||||
|
||||
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.vm.Instruction
|
||||
import prog8.vm.Opcode
|
||||
import prog8.vm.DataType
|
||||
|
||||
|
||||
class CodeGen(internal val program: PtProgram,
|
||||
internal val symbolTable: SymbolTable,
|
||||
@ -12,6 +14,9 @@ class CodeGen(internal val program: PtProgram,
|
||||
internal val errors: IErrorReporter
|
||||
): IAssemblyGenerator {
|
||||
|
||||
internal val allocations = VariableAllocator(symbolTable, program, errors)
|
||||
private val builtinFunctions = BuiltinFunctionsGen(this)
|
||||
private val expressionEval = ExpressionGen(this, builtinFunctions)
|
||||
private val instructions = mutableListOf<String>()
|
||||
|
||||
init {
|
||||
@ -21,54 +26,76 @@ class CodeGen(internal val program: PtProgram,
|
||||
|
||||
override fun compileToAssembly(): IAssemblyProgram? {
|
||||
instructions.clear()
|
||||
val allocations = VariableAllocator(symbolTable, program, errors)
|
||||
val vmprog = AssemblyProgram(program.name, allocations)
|
||||
|
||||
outComment("GLOBAL VARS INITS")
|
||||
program.allBlocks().forEach {
|
||||
it.children
|
||||
.singleOrNull { node->node is PtScopeVarsInit }
|
||||
?.let { inits->
|
||||
translateNode(inits)
|
||||
?.let { inits ->
|
||||
vmprog.addGlobalInits(translate(inits as PtScopeVarsInit))
|
||||
it.children.remove(inits)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
outComment("PROGRAM CODE")
|
||||
for (block in program.allBlocks()) {
|
||||
translateNode(block)
|
||||
vmprog.addBlock(translate(block))
|
||||
}
|
||||
return AssemblyProgram(program.name, allocations, instructions)
|
||||
|
||||
return vmprog
|
||||
}
|
||||
|
||||
private fun out(ins: Instruction) {
|
||||
instructions.add(ins.toString())
|
||||
private fun translate(assignment: PtAssignment): VmCodeChunk {
|
||||
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) {
|
||||
instructions.add("; $ins")
|
||||
}
|
||||
|
||||
private fun translateNode(node: PtNode) {
|
||||
when(node) {
|
||||
private fun translateNode(node: PtNode): VmCodeChunk {
|
||||
return when(node) {
|
||||
is PtBlock -> translate(node)
|
||||
is PtSub -> translate(node)
|
||||
is PtScopeVarsDecls -> { /* vars should be looked up via symbol table */ }
|
||||
is PtVariable -> { /* var should be looked up via symbol table */ }
|
||||
is PtMemMapped -> { /* memmapped var should be looked up via symbol table */ }
|
||||
is PtConstant -> { /* constants have all been folded into the code */ }
|
||||
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 PtAssignTarget -> TODO()
|
||||
is PtAssignment -> translate(node)
|
||||
is PtScopeVarsInit -> translate(node)
|
||||
is PtBreakpoint -> translate(node)
|
||||
is PtConditionalBranch -> TODO()
|
||||
is PtAddressOf -> TODO()
|
||||
is PtArrayIndexer -> TODO()
|
||||
is PtArrayLiteral -> TODO()
|
||||
is PtBinaryExpression -> TODO()
|
||||
is PtBuiltinFunctionCall -> TODO()
|
||||
is PtBuiltinFunctionCall -> builtinFunctions.translate(node)
|
||||
is PtContainmentCheck -> TODO()
|
||||
is PtFunctionCall -> TODO()
|
||||
is PtFunctionCall -> translate(node)
|
||||
is PtIdentifier -> TODO()
|
||||
is PtMemoryByte -> TODO()
|
||||
is PtNumber -> TODO()
|
||||
@ -78,55 +105,103 @@ class CodeGen(internal val program: PtProgram,
|
||||
is PtString -> TODO()
|
||||
is PtTypeCast -> TODO()
|
||||
is PtForLoop -> TODO()
|
||||
is PtGosub -> TODO()
|
||||
is PtGosub -> translate(node)
|
||||
is PtIfElse -> TODO()
|
||||
is PtIncludeBinary -> TODO()
|
||||
is PtJump -> TODO()
|
||||
is PtNodeGroup -> TODO()
|
||||
is PtNop -> { }
|
||||
is PtNop -> VmCodeChunk()
|
||||
is PtPostIncrDecr -> TODO()
|
||||
is PtProgram -> TODO()
|
||||
is PtRepeatLoop -> TODO()
|
||||
is PtReturn -> TODO()
|
||||
is PtReturn -> translate(node)
|
||||
is PtSubroutineParameter -> TODO()
|
||||
is PtWhen -> TODO()
|
||||
is PtWhenChoice -> TODO()
|
||||
is PtLabel -> TODO()
|
||||
is PtBreakpoint -> TODO()
|
||||
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")
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(breakpoint: PtBreakpoint) {
|
||||
out(Instruction(Opcode.BREAKPOINT))
|
||||
private fun translate(fcall: PtFunctionCall): VmCodeChunk {
|
||||
val chunk = VmCodeChunk()
|
||||
// TODO evaluate function call arguments
|
||||
chunk += VmCodeOpcodeWithStringArg(Opcode.GOSUB, gosubArg(fcall.functionName))
|
||||
return chunk
|
||||
}
|
||||
|
||||
private fun translate(init: PtScopeVarsInit) {
|
||||
init.children.forEach { translateNode(it) }
|
||||
private fun translate(gosub: PtGosub): VmCodeChunk {
|
||||
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) {
|
||||
outComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
|
||||
private fun translate(ret: PtReturn): VmCodeChunk {
|
||||
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
|
||||
.singleOrNull { it is PtScopeVarsInit }
|
||||
?.let { inits ->
|
||||
sub.children.remove(inits)
|
||||
translateNode(inits)
|
||||
chunk += translateNode(inits)
|
||||
}
|
||||
|
||||
// TODO rest
|
||||
outComment("SUB-END ${sub.scopedName}\n")
|
||||
}
|
||||
|
||||
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)
|
||||
for (child in sub.children) {
|
||||
chunk += 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
|
||||
}
|
||||
|
||||
fun asVmMemory(): List<String> {
|
||||
/*
|
||||
$4000 strz "Hello from program! "derp" bye.\n"
|
||||
$2000 ubyte 65,66,67,68,0
|
||||
$2100 uword $1111,$2222,$3333,$4444
|
||||
*/
|
||||
val mm = mutableListOf<String>()
|
||||
fun get(name: List<String>) = allocations.getValue(name)
|
||||
|
||||
fun asVmMemory(): List<Pair<List<String>, String>> {
|
||||
val mm = mutableListOf<Pair<List<String>, String>>()
|
||||
for (variable in st.allVariables) {
|
||||
val location = allocations.getValue(variable.scopedName)
|
||||
val typeStr = when(variable.dt) {
|
||||
@ -67,7 +64,7 @@ $2100 uword $1111,$2222,$3333,$4444
|
||||
}
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
mm.add("${location.toHex()} $typeStr $value")
|
||||
mm.add(Pair(variable.scopedName, "${location.toHex()} $typeStr $value"))
|
||||
}
|
||||
return mm
|
||||
}
|
||||
|
@ -3,6 +3,10 @@ TODO
|
||||
|
||||
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
|
||||
|
||||
main {
|
||||
ubyte[256] sieve
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
ubyte variable = 42
|
||||
|
||||
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)
|
||||
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()
|
||||
variable = foo()
|
||||
}
|
||||
|
||||
sub find_next_prime() -> ubyte {
|
||||
|
||||
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
|
||||
sub foo() -> ubyte {
|
||||
return variable+1
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ class Assembler {
|
||||
fun initializeMemory(memsrc: String, memory: Memory) {
|
||||
val instrPattern = Regex("""(.+?)\s+([a-z]+)\s+(.+)""", RegexOption.IGNORE_CASE)
|
||||
for(line in memsrc.lines()) {
|
||||
if(line.isBlank())
|
||||
if(line.isBlank() || line.startsWith(';'))
|
||||
continue
|
||||
val match = instrPattern.matchEntire(line.trim())
|
||||
if(match==null)
|
||||
@ -54,7 +54,7 @@ class Assembler {
|
||||
placeholders.clear()
|
||||
val program = mutableListOf<Instruction>()
|
||||
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()) {
|
||||
if(line.isBlank() || line.startsWith(';'))
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
@ -49,9 +49,9 @@ Subroutine parameters set in Reg 0, 1, 2... before gosub.
|
||||
Return value in Reg 0 before return.
|
||||
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
||||
|
||||
ARITHMETIC - all have a type of b/w/f. (note: float not yet implemented)
|
||||
(note: for calculations, all types -result, and both operands- are identical)
|
||||
INTEGER ARITHMETIC - all have a type of b/w.
|
||||
(note: the types of the result and both operands, are all identical UNLESS OTHERWISE NOTED).
|
||||
|
||||
neg reg1, reg2 - reg1 = sign negation of reg2
|
||||
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)
|
||||
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)
|
||||
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!
|
||||
@ -145,9 +143,7 @@ enum class Opcode {
|
||||
|
||||
NEG,
|
||||
ADD,
|
||||
ADDI,
|
||||
SUB,
|
||||
SUBI,
|
||||
MUL,
|
||||
DIV,
|
||||
EXT,
|
||||
@ -171,7 +167,7 @@ enum class Opcode {
|
||||
enum class DataType {
|
||||
BYTE,
|
||||
WORD
|
||||
// TODO add LONG? FLOAT?
|
||||
// TODO add INT (32-bit)?
|
||||
}
|
||||
|
||||
data class Instruction(
|
||||
@ -190,22 +186,22 @@ data class Instruction(
|
||||
else -> result.add(" ")
|
||||
}
|
||||
reg1?.let {
|
||||
result.add(it.toString())
|
||||
result.add("r$it")
|
||||
result.add(",")
|
||||
}
|
||||
reg2?.let {
|
||||
result.add(it.toString())
|
||||
result.add("r$it")
|
||||
result.add(",")
|
||||
}
|
||||
reg3?.let {
|
||||
result.add(it.toString())
|
||||
result.add("r$it")
|
||||
result.add(",")
|
||||
}
|
||||
value?.let {
|
||||
result.add(it.toString())
|
||||
}
|
||||
if(result.last() == ",")
|
||||
result.dropLast(1)
|
||||
result.removeLast()
|
||||
return result.joinToString("").trimEnd()
|
||||
}
|
||||
}
|
||||
@ -253,9 +249,7 @@ val instructionFormats = mutableMapOf(
|
||||
|
||||
Opcode.NEG to InstructionFormat(BW, true, true, false, 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.SUBI to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.MUL 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),
|
||||
|
@ -109,9 +109,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
Opcode.BGES -> InsBGES(ins)
|
||||
Opcode.NEG -> InsNEG(ins)
|
||||
Opcode.ADD -> InsADD(ins)
|
||||
Opcode.ADDI -> InsADDI(ins)
|
||||
Opcode.SUB -> InsSUB(ins)
|
||||
Opcode.SUBI -> InsSUBI(ins)
|
||||
Opcode.MUL -> InsMul(ins)
|
||||
Opcode.DIV -> InsDiv(ins)
|
||||
Opcode.EXT -> InsEXT(ins)
|
||||
@ -474,14 +472,6 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
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) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> arithByte("-", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
@ -490,14 +480,6 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
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) {
|
||||
when(i.type!!){
|
||||
DataType.BYTE -> registers.setW(i.reg1!!, registers.getB(i.reg2!!).toUShort())
|
||||
|
Loading…
x
Reference in New Issue
Block a user