working on vm translator

This commit is contained in:
Irmen de Jong 2022-03-22 01:41:23 +01:00
parent fd581ffc37
commit 06b38506d1
10 changed files with 326 additions and 152 deletions

View File

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

View File

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

View File

@ -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(".")}"
} }

View 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?
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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