From 06b38506d13724dee8974ea6f40b960d33b21e75 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 22 Mar 2022 01:41:23 +0100 Subject: [PATCH] working on vm translator --- .../prog8/codegen/virtual/AssemblyProgram.kt | 57 +++++- .../codegen/virtual/BuiltinFunctionsGen.kt | 43 +++++ .../src/prog8/codegen/virtual/CodeGen.kt | 169 +++++++++++++----- .../prog8/codegen/virtual/ExpressionGen.kt | 83 +++++++++ .../codegen/virtual/VariableAllocator.kt | 13 +- docs/source/todo.rst | 4 + examples/test.p8 | 61 +------ virtualmachine/src/prog8/vm/Assembler.kt | 4 +- virtualmachine/src/prog8/vm/Instructions.kt | 26 ++- virtualmachine/src/prog8/vm/VirtualMachine.kt | 18 -- 10 files changed, 326 insertions(+), 152 deletions(-) create mode 100644 codeGenVirtual/src/prog8/codegen/virtual/BuiltinFunctionsGen.kt create mode 100644 codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt index ae48ae24d..210b761d3 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt @@ -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 + private val allocations: VariableAllocator ) : IAssemblyProgram { + + private val globalInits = mutableListOf() + private val blocks = mutableListOf() + 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): VmCodeLine() +internal class VmCodeComment(val comment: String): VmCodeLine() +internal class VmCodeOpcodeWithStringArg(val opcode: Opcode, val arg: String): VmCodeLine() + +internal class VmCodeChunk { + val lines = mutableListOf() + + operator fun plusAssign(line: VmCodeLine) { + lines.add(line) + } + + operator fun plusAssign(chunk: VmCodeChunk) { + lines.addAll(chunk.lines) + } +} \ No newline at end of file diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFunctionsGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFunctionsGen.kt new file mode 100644 index 000000000..199240187 --- /dev/null +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFunctionsGen.kt @@ -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 + } +} diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index 5a7fde4fc..c365ee4e0 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -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() 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) = "_${targetName.joinToString(".")}" } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt new file mode 100644 index 000000000..9d4dbaf41 --- /dev/null +++ b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt @@ -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 { + // 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 { + 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 { + 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? + } + +} diff --git a/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt b/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt index 419eea3ce..223232ca6 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt @@ -27,13 +27,10 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg freeStart = nextLocation } - fun asVmMemory(): List { -/* -$4000 strz "Hello from program! "derp" bye.\n" -$2000 ubyte 65,66,67,68,0 -$2100 uword $1111,$2222,$3333,$4444 - */ - val mm = mutableListOf() + fun get(name: List) = allocations.getValue(name) + + fun asVmMemory(): List, String>> { + val mm = mutableListOf, 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 } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4315231e2..e5a8b9379 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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. + ... diff --git a/examples/test.p8 b/examples/test.p8 index 17212da30..b04673d31 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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 } } diff --git a/virtualmachine/src/prog8/vm/Assembler.kt b/virtualmachine/src/prog8/vm/Assembler.kt index c2053b2d4..fdd7e691d 100644 --- a/virtualmachine/src/prog8/vm/Assembler.kt +++ b/virtualmachine/src/prog8/vm/Assembler.kt @@ -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() 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 diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index 9a6f12f33..03e87d172 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -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), diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 859c100bc..d6bc030e3 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -109,9 +109,7 @@ class VirtualMachine(val memory: Memory, program: List) { 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) { 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) { 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())