From 88a9f2d493c3824a90eb6c3b458325a42c8d44fe Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 16 Sep 2018 17:45:51 +0200 Subject: [PATCH] stackvm program tweaks --- compiler/src/prog8/compiler/Compiler.kt | 624 ++++++++++++------------ compiler/src/prog8/stackvm/StackVm.kt | 144 ++++-- 2 files changed, 413 insertions(+), 355 deletions(-) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index c228adc8b..9f4717869 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -122,369 +122,375 @@ class Compiler(private val options: CompilationOptions) { } } - class StatementTranslator(private val stackvmProg: StackVmProgram, private val namespace: INameScope): IAstProcessor { - var stmtUniqueSequenceNr = 0 - private set +} - override fun process(subroutine: Subroutine): IStatement { - stackvmProg.label(subroutine.scopedname) - translate(subroutine.statements) - return super.process(subroutine) - } +private class StatementTranslator(private val stackvmProg: StackVmProgram, private val namespace: INameScope): IAstProcessor { + var stmtUniqueSequenceNr = 0 + private set - override fun process(block: Block): IStatement { - stackvmProg.label(block.scopedname) - translate(block.statements) - return super.process(block) - } + override fun process(subroutine: Subroutine): IStatement { + stackvmProg.label(subroutine.scopedname) + translate(subroutine.statements) + return super.process(subroutine) + } - override fun process(directive: Directive): IStatement { - when(directive.directive) { - "%asminclude" -> throw CompilerException("can't use %asminclude in stackvm") - "%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm") - "%breakpoint" -> { - stackvmProg.line(directive.position) - stackvmProg.instruction("break") - } - } - return super.process(directive) - } + override fun process(block: Block): IStatement { + stackvmProg.label(block.scopedname) + translate(block.statements) + return super.process(block) + } - private fun translate(statements: List) { - for (stmt: IStatement in statements) { - stmtUniqueSequenceNr++ - when (stmt) { - is AnonymousStatementList -> translate(stmt.statements) - is Label -> translate(stmt) - is Return -> translate(stmt) - is Assignment -> translate(stmt) // normal and augmented assignments - is PostIncrDecr -> translate(stmt) - is Jump -> translate(stmt) - is FunctionCallStatement -> translate(stmt) - is IfStatement -> translate(stmt) - is BranchStatement -> translate(stmt) - is Break -> translate(stmt) - is Continue -> translate(stmt) - is ForLoop -> translate(stmt) - is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these. - is InlineAssembly -> throw CompilerException("inline assembly is not supported by the StackVM") - else -> TODO("translate statement $stmt to stackvm") - } + override fun process(directive: Directive): IStatement { + when(directive.directive) { + "%asminclude" -> throw CompilerException("can't use %asminclude in stackvm") + "%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm") + "%breakpoint" -> { + stackvmProg.line(directive.position) + stackvmProg.instruction("break") } } + return super.process(directive) + } - - private fun translate(stmt: Continue) { - stackvmProg.line(stmt.position) - stackvmProg.instruction("continue") - } - - private fun translate(stmt: Break) { - stackvmProg.line(stmt.position) - stackvmProg.instruction("break") - } - - private fun translate(branch: BranchStatement) { - /* - * A branch: IF_CC { stuff } else { other_stuff } - * Which is translated into: - * BCS _stmt_999_else - * stuff - * JUMP _stmt_999_continue - * _stmt_999_else: - * other_stuff ;; optional - * _stmt_999_continue: - * ... - */ - stackvmProg.line(branch.position) - val labelElse = makeLabel("else") - val labelContinue = makeLabel("continue") - val opcode = when(branch.condition) { - BranchCondition.CS -> "bcc" - BranchCondition.CC -> "bcs" - BranchCondition.EQ -> "bne" - BranchCondition.NE -> "beq" - BranchCondition.VS -> "bvc" - BranchCondition.VC -> "bvs" - BranchCondition.MI -> "bpl" - BranchCondition.PL -> "bmi" - } - if(branch.elsepart.isEmpty()) { - stackvmProg.instruction("$opcode $labelContinue") - translate(branch.statements) - stackvmProg.label(labelContinue) - } else { - stackvmProg.instruction("$opcode $labelElse") - translate(branch.statements) - stackvmProg.instruction("jump $labelContinue") - stackvmProg.label(labelElse) - translate(branch.elsepart) - stackvmProg.label(labelContinue) + private fun translate(statements: List) { + for (stmt: IStatement in statements) { + stmtUniqueSequenceNr++ + when (stmt) { + is AnonymousStatementList -> translate(stmt.statements) + is Label -> translate(stmt) + is Return -> translate(stmt) + is Assignment -> translate(stmt) // normal and augmented assignments + is PostIncrDecr -> translate(stmt) + is Jump -> translate(stmt) + is FunctionCallStatement -> translate(stmt) + is IfStatement -> translate(stmt) + is BranchStatement -> translate(stmt) + is Break -> translate(stmt) + is Continue -> translate(stmt) + is ForLoop -> translate(stmt) + is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these. + is InlineAssembly -> throw CompilerException("inline assembly is not supported by the StackVM") + else -> TODO("translate statement $stmt to stackvm") } } + } - private fun makeLabel(postfix: String): String = "_prog8stmt_${stmtUniqueSequenceNr}_$postfix" - private fun translate(stmt: IfStatement) { - /* - * An IF statement: IF (condition-expression) { stuff } else { other_stuff } - * Which is translated into: - * - * BEQ _stmt_999_else - * stuff - * JUMP _stmt_999_continue - * _stmt_999_else: - * other_stuff ;; optional - * _stmt_999_continue: - * ... - */ - stackvmProg.line(stmt.position) - translate(stmt.condition) - val labelElse = makeLabel("else") - val labelContinue = makeLabel("continue") - if(stmt.elsepart.isEmpty()) { - stackvmProg.instruction("beq $labelContinue") - translate(stmt.statements) - stackvmProg.label(labelContinue) - } else { - stackvmProg.instruction("beq $labelElse") - translate(stmt.statements) - stackvmProg.instruction("jump $labelContinue") - stackvmProg.label(labelElse) - translate(stmt.elsepart) - stackvmProg.label(labelContinue) - } + private fun translate(stmt: Continue) { + stackvmProg.line(stmt.position) + stackvmProg.instruction("continue") + } + + private fun translate(stmt: Break) { + stackvmProg.line(stmt.position) + stackvmProg.instruction("break") + } + + private fun translate(branch: BranchStatement) { + /* + * A branch: IF_CC { stuff } else { other_stuff } + * Which is translated into: + * BCS _stmt_999_else + * stuff + * JUMP _stmt_999_continue + * _stmt_999_else: + * other_stuff ;; optional + * _stmt_999_continue: + * ... + */ + stackvmProg.line(branch.position) + val labelElse = makeLabel("else") + val labelContinue = makeLabel("continue") + val opcode = when(branch.condition) { + BranchCondition.CS -> "bcc" + BranchCondition.CC -> "bcs" + BranchCondition.EQ -> "bne" + BranchCondition.NE -> "beq" + BranchCondition.VS -> "bvc" + BranchCondition.VC -> "bvs" + BranchCondition.MI -> "bpl" + BranchCondition.PL -> "bmi" } + if(branch.elsepart.isEmpty()) { + stackvmProg.instruction("$opcode $labelContinue") + translate(branch.statements) + stackvmProg.label(labelContinue) + } else { + stackvmProg.instruction("$opcode $labelElse") + translate(branch.statements) + stackvmProg.instruction("jump $labelContinue") + stackvmProg.label(labelElse) + translate(branch.elsepart) + stackvmProg.label(labelContinue) + } + } - private fun translate(expr: IExpression) { - when(expr) { - is RegisterExpr -> { - stackvmProg.instruction("push_var ${expr.register}") - } - is BinaryExpression -> { - translate(expr.left) - translate(expr.right) - translateBinaryOperator(expr.operator) - } - is FunctionCall -> { - expr.arglist.forEach { translate(it) } - val target = expr.target.targetStatement(namespace) - if(target is BuiltinFunctionStatementPlaceholder) { - // call to a builtin function - val funcname = expr.target.nameInSource[0].toUpperCase() - createFunctionCall(funcname) // call builtin function - } else { - when(target) { - is Subroutine -> { - stackvmProg.instruction("call ${target.scopedname}") - } - else -> TODO("non-builtin-function call to $target") - } - } - } - is IdentifierReference -> { - val target = expr.targetStatement(namespace) + private fun makeLabel(postfix: String): String = "_prog8stmt_${stmtUniqueSequenceNr}_$postfix" + + private fun translate(stmt: IfStatement) { + /* + * An IF statement: IF (condition-expression) { stuff } else { other_stuff } + * Which is translated into: + * + * BEQ _stmt_999_else + * stuff + * JUMP _stmt_999_continue + * _stmt_999_else: + * other_stuff ;; optional + * _stmt_999_continue: + * ... + */ + stackvmProg.line(stmt.position) + translate(stmt.condition) + val labelElse = makeLabel("else") + val labelContinue = makeLabel("continue") + if(stmt.elsepart.isEmpty()) { + stackvmProg.instruction("beq $labelContinue") + translate(stmt.statements) + stackvmProg.label(labelContinue) + } else { + stackvmProg.instruction("beq $labelElse") + translate(stmt.statements) + stackvmProg.instruction("jump $labelContinue") + stackvmProg.label(labelElse) + translate(stmt.elsepart) + stackvmProg.label(labelContinue) + } + } + + private fun translate(expr: IExpression) { + when(expr) { + is RegisterExpr -> { + stackvmProg.instruction("push_var ${expr.register}") + } + is BinaryExpression -> { + translate(expr.left) + translate(expr.right) + translateBinaryOperator(expr.operator) + } + is FunctionCall -> { + expr.arglist.forEach { translate(it) } + val target = expr.target.targetStatement(namespace) + if(target is BuiltinFunctionStatementPlaceholder) { + // call to a builtin function + val funcname = expr.target.nameInSource[0].toUpperCase() + createFunctionCall(funcname) // call builtin function + } else { when(target) { - is VarDecl -> { - stackvmProg.instruction("push_var ${target.scopedname}") + is Subroutine -> { + stackvmProg.instruction("call ${target.scopedname}") } - else -> throw CompilerException("expression identifierref should be a vardef, not $target") - } - } - is RangeExpr -> { - TODO("TRANSLATE $expr") // todo - } - else -> { - val lv = expr.constValue(namespace) ?: throw CompilerException("constant expression required, not $expr") - when(lv.type) { - DataType.BYTE -> stackvmProg.instruction("push b:%02x".format(lv.bytevalue!!)) - DataType.WORD -> stackvmProg.instruction("push w:%04x".format(lv.wordvalue!!)) - DataType.FLOAT -> stackvmProg.instruction("push f:${lv.floatvalue}") - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> stackvmProg.instruction("push \"${lv.strvalue}\"") - DataType.ARRAY, DataType.ARRAY_W -> { - lv.arrayvalue?.forEach { translate(it) } - stackvmProg.instruction("array w:${lv.arrayvalue!!.size.toString(16)}") - } - DataType.MATRIX -> TODO("matrix type") + else -> TODO("non-builtin-function call to $target") } } } + is IdentifierReference -> { + val target = expr.targetStatement(namespace) + when(target) { + is VarDecl -> { + stackvmProg.instruction("push_var ${target.scopedname}") + } + else -> throw CompilerException("expression identifierref should be a vardef, not $target") + } + } + is RangeExpr -> { + TODO("TRANSLATE $expr") // todo + } + else -> { + val lv = expr.constValue(namespace) ?: throw CompilerException("constant expression required, not $expr") + when(lv.type) { + DataType.BYTE -> stackvmProg.instruction("push b:%02x".format(lv.bytevalue!!)) + DataType.WORD -> stackvmProg.instruction("push w:%04x".format(lv.wordvalue!!)) + DataType.FLOAT -> stackvmProg.instruction("push f:${lv.floatvalue}") + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> stackvmProg.instruction("push \"${lv.strvalue}\"") + DataType.ARRAY, DataType.ARRAY_W -> { + lv.arrayvalue?.forEach { translate(it) } + stackvmProg.instruction("array w:${lv.arrayvalue!!.size.toString(16)}") + } + DataType.MATRIX -> TODO("matrix type") + } + } } + } - private fun translateBinaryOperator(operator: String) { - val instruction = when(operator) { - "+" -> "add" - "-" -> "sub" - "*" -> "mul" - "/" -> "div" - "%" -> "remainder" - "**" -> "pow" - "&" -> "bitand" - "|" -> "bitor" - "^" -> "bitxor" - "and" -> "and" - "or" -> "or" - "xor" -> "xor" - "<" -> "less" - ">" -> "greater" - "<=" -> "lesseq" - ">=" -> "greatereq" - "==" -> "equal" - "!=" -> "notequal" - else -> throw FatalAstException("const evaluation for invalid operator $operator") - } - stackvmProg.instruction(instruction) + private fun translateBinaryOperator(operator: String) { + val instruction = when(operator) { + "+" -> "add" + "-" -> "sub" + "*" -> "mul" + "/" -> "div" + "%" -> "remainder" + "**" -> "pow" + "&" -> "bitand" + "|" -> "bitor" + "^" -> "bitxor" + "and" -> "and" + "or" -> "or" + "xor" -> "xor" + "<" -> "less" + ">" -> "greater" + "<=" -> "lesseq" + ">=" -> "greatereq" + "==" -> "equal" + "!=" -> "notequal" + else -> throw FatalAstException("const evaluation for invalid operator $operator") } + stackvmProg.instruction(instruction) + } - private fun translate(stmt: FunctionCallStatement) { - stackvmProg.line(stmt.position) - val targetStmt = stmt.target.targetStatement(namespace)!! - if(targetStmt is BuiltinFunctionStatementPlaceholder) { - stmt.arglist.forEach { translate(it) } - val funcname = stmt.target.nameInSource[0].toUpperCase() - createFunctionCall(funcname) // call builtin function - return - } - - val targetname = when(targetStmt) { - is Label -> targetStmt.scopedname - is Subroutine -> targetStmt.scopedname - else -> throw AstException("invalid call target node type: ${targetStmt::class}") - } + private fun translate(stmt: FunctionCallStatement) { + stackvmProg.line(stmt.position) + val targetStmt = stmt.target.targetStatement(namespace)!! + if(targetStmt is BuiltinFunctionStatementPlaceholder) { stmt.arglist.forEach { translate(it) } - stackvmProg.instruction("call $targetname") + val funcname = stmt.target.nameInSource[0].toUpperCase() + createFunctionCall(funcname) // call builtin function + return } - private fun createFunctionCall(funcname: String) { - if (funcname.startsWith("_VM_")) - stackvmProg.instruction("syscall ${funcname.substring(4)}") // call builtin function - else - stackvmProg.instruction("syscall FUNC_$funcname") + val targetname = when(targetStmt) { + is Label -> targetStmt.scopedname + is Subroutine -> targetStmt.scopedname + else -> throw AstException("invalid call target node type: ${targetStmt::class}") } + stmt.arglist.forEach { translate(it) } + stackvmProg.instruction("call $targetname") + } - private fun translate(stmt: Jump) { - val instr = - if(stmt.address!=null) { - "jump \$${stmt.address.toString(16)}" - } else { - val target = stmt.identifier!!.targetStatement(namespace)!! - when(target) { - is Label -> "jump ${target.scopedname}" - is Subroutine -> "jump ${target.scopedname}" - else -> throw CompilerException("invalid jump target type ${target::class}") - } - } - stackvmProg.line(stmt.position) - stackvmProg.instruction(instr) - } + private fun createFunctionCall(funcname: String) { + if (funcname.startsWith("_VM_")) + stackvmProg.instruction("syscall ${funcname.substring(4)}") // call builtin function + else + stackvmProg.instruction("syscall FUNC_$funcname") + } - private fun translate(stmt: PostIncrDecr) { - stackvmProg.line(stmt.position) - if(stmt.target.register!=null) { - when(stmt.operator) { - "++" -> stackvmProg.instruction("inc_var ${stmt.target.register}") - "--" -> stackvmProg.instruction("dec_var ${stmt.target.register}") - } - } else { - val targetStatement = stmt.target.identifier!!.targetStatement(namespace) as VarDecl - when(stmt.operator) { - "++" -> stackvmProg.instruction("inc_var ${targetStatement.scopedname}") - "--" -> stackvmProg.instruction("dec_var ${targetStatement.scopedname}") - } - } - } - - private fun translate(stmt: Assignment) { - stackvmProg.line(stmt.position) - translate(stmt.value) - val valueDt = stmt.value.resultingDatatype(namespace) - val targetDt = stmt.target.determineDatatype(namespace, stmt) - if(valueDt!=targetDt) { - // convert value to target datatype if possible - when(targetDt) { - DataType.BYTE -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - DataType.WORD -> { - if(valueDt==DataType.BYTE) - stackvmProg.instruction("b2word") - else - throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - } - DataType.FLOAT -> { - when (valueDt) { - DataType.BYTE -> stackvmProg.instruction("b2float") - DataType.WORD -> stackvmProg.instruction("w2float") - else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - } - } - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - // todo: maybe if you assign byte or word to array/matrix, clear it with that value? - } - } - - if(stmt.aug_op!=null) { - // augmented assignment - if(stmt.target.identifier!=null) { - val target = stmt.target.identifier!!.targetStatement(namespace)!! + private fun translate(stmt: Jump) { + val instr = + if(stmt.address!=null) { + "jump \$${stmt.address.toString(16)}" + } else { + val target = stmt.identifier!!.targetStatement(namespace)!! when(target) { - is VarDecl -> stackvmProg.instruction("push_var ${target.scopedname}") - else -> throw CompilerException("invalid assignment target type ${target::class}") + is Label -> "jump ${target.scopedname}" + is Subroutine -> "jump ${target.scopedname}" + else -> throw CompilerException("invalid jump target type ${target::class}") } - } else if(stmt.target.register!=null) { - stackvmProg.instruction("push_var ${stmt.target.register}") } - translateAugAssignOperator(stmt.aug_op) - } + stackvmProg.line(stmt.position) + stackvmProg.instruction(instr) + } - // pop the result value back into the assignment target + private fun translate(stmt: PostIncrDecr) { + stackvmProg.line(stmt.position) + if(stmt.target.register!=null) { + when(stmt.operator) { + "++" -> stackvmProg.instruction("inc_var ${stmt.target.register}") + "--" -> stackvmProg.instruction("dec_var ${stmt.target.register}") + } + } else { + val targetStatement = stmt.target.identifier!!.targetStatement(namespace) as VarDecl + when(stmt.operator) { + "++" -> stackvmProg.instruction("inc_var ${targetStatement.scopedname}") + "--" -> stackvmProg.instruction("dec_var ${targetStatement.scopedname}") + } + } + } + + private fun translate(stmt: Assignment) { + stackvmProg.line(stmt.position) + translate(stmt.value) + val valueDt = stmt.value.resultingDatatype(namespace) + val targetDt = stmt.target.determineDatatype(namespace, stmt) + if(valueDt!=targetDt) { + // convert value to target datatype if possible + when(targetDt) { + DataType.BYTE -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") + DataType.WORD -> { + if(valueDt==DataType.BYTE) + stackvmProg.instruction("b2word") + else + throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") + } + DataType.FLOAT -> { + when (valueDt) { + DataType.BYTE -> stackvmProg.instruction("b2float") + DataType.WORD -> stackvmProg.instruction("w2float") + else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") + } + } + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") + DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") + // todo: maybe if you assign byte or word to array/matrix, clear it with that value? + } + } + + if(stmt.aug_op!=null) { + // augmented assignment if(stmt.target.identifier!=null) { val target = stmt.target.identifier!!.targetStatement(namespace)!! when(target) { - is VarDecl -> stackvmProg.instruction("pop_var ${target.scopedname}") + is VarDecl -> stackvmProg.instruction("push_var ${target.scopedname}") else -> throw CompilerException("invalid assignment target type ${target::class}") } } else if(stmt.target.register!=null) { - stackvmProg.instruction("pop_var ${stmt.target.register}") + stackvmProg.instruction("push_var ${stmt.target.register}") } + translateAugAssignOperator(stmt.aug_op) } - private fun translateAugAssignOperator(aug_op: String) { - val instruction = when(aug_op) { - "+=" -> "add" - "-=" -> "sub" - "/=" -> "div" - "*=" -> "mul" - "**=" -> "pow" - "&=" -> "bitand" - "|=" -> "bitor" - "^=" -> "bitxor" - else -> throw CompilerException("invalid aug assignment operator $aug_op") + // pop the result value back into the assignment target + if(stmt.target.identifier!=null) { + val target = stmt.target.identifier!!.targetStatement(namespace)!! + when(target) { + is VarDecl -> stackvmProg.instruction("pop_var ${target.scopedname}") + else -> throw CompilerException("invalid assignment target type ${target::class}") } - stackvmProg.instruction(instruction) + } else if(stmt.target.register!=null) { + stackvmProg.instruction("pop_var ${stmt.target.register}") } + } - private fun translate(stmt: Return) { - if(stmt.values.isNotEmpty()) { - TODO("return with value(s) not yet supported: $stmt") - } - stackvmProg.line(stmt.position) - stackvmProg.instruction("return") + private fun translateAugAssignOperator(aug_op: String) { + val instruction = when(aug_op) { + "+=" -> "add" + "-=" -> "sub" + "/=" -> "div" + "*=" -> "mul" + "**=" -> "pow" + "&=" -> "bitand" + "|=" -> "bitor" + "^=" -> "bitxor" + else -> throw CompilerException("invalid aug assignment operator $aug_op") } + stackvmProg.instruction(instruction) + } - private fun translate(stmt: Label) { - stackvmProg.label(stmt.scopedname) + private fun translate(stmt: Return) { + if(stmt.values.isNotEmpty()) { + TODO("return with value(s) not yet supported: $stmt") } + stackvmProg.line(stmt.position) + stackvmProg.instruction("return") + } + + private fun translate(stmt: Label) { + stackvmProg.label(stmt.scopedname) + } - private fun translate(loop: ForLoop) { - stackvmProg.line(loop.position) - println("@TODO: translate FOR LOOP $loop") // @todo FOR LOOP + private fun translate(loop: ForLoop) { + stackvmProg.line(loop.position) + if(loop.loopRegister!=null) { + val reg = loop.loopRegister + println("@TODO translate for loop (register $reg)") // TODO + } else { + val loopvar = (loop.loopVar!!.targetStatement(namespace) as VarDecl) + println("@TODO translate for loop (variable $loopvar)") // TODO } } } - class StackVmProgram(val name: String) { val variables = mutableMapOf() diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index e5627a699..129d05734 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -5,6 +5,7 @@ import prog8.compiler.Mflpt5 import prog8.compiler.Petscii import java.awt.EventQueue import java.io.File +import java.io.PrintStream import java.io.PrintWriter import java.util.* import java.util.regex.Pattern @@ -133,41 +134,41 @@ enum class Syscall(val callNr: Short) { GFX_CLEARSCR(17), // clear the screen with color pushed on stack GFX_TEXT(18), // write text on screen at (x,y,color,text) pushed on stack in that order - FUNC_P_CARRY(100), - FUNC_P_IRQD(101), - FUNC_ROL(102), - FUNC_ROR(103), - FUNC_ROL2(104), - FUNC_ROR2(105), - FUNC_LSL(106), - FUNC_LSR(107), - FUNC_SIN(108), - FUNC_COS(109), - FUNC_ABS(110), - FUNC_ACOS(111), - FUNC_ASIN(112), - FUNC_TAN(113), - FUNC_ATAN(114), - FUNC_LOG(115), - FUNC_LOG10(116), - FUNC_SQRT(117), - FUNC_RAD(118), - FUNC_DEG(119), - FUNC_ROUND(120), - FUNC_FLOOR(121), - FUNC_CEIL(122), - FUNC_MAX(123), - FUNC_MIN(124), - FUNC_AVG(125), - FUNC_SUM(126), - FUNC_LEN(127), - FUNC_ANY(128), - FUNC_ALL(129), - FUNC_LSB(130), - FUNC_MSB(131), - FUNC_RND(132), // push a random byte on the stack - FUNC_RNDW(133), // push a random word on the stack - FUNC_RNDF(134) // push a random float on the stack (between 0.0 and 1.0) + FUNC_P_CARRY(64), + FUNC_P_IRQD(65), + FUNC_ROL(66), + FUNC_ROR(67), + FUNC_ROL2(68), + FUNC_ROR2(69), + FUNC_LSL(70), + FUNC_LSR(71), + FUNC_SIN(72), + FUNC_COS(73), + FUNC_ABS(74), + FUNC_ACOS(75), + FUNC_ASIN(76), + FUNC_TAN(77), + FUNC_ATAN(78), + FUNC_LOG(79), + FUNC_LOG10(80), + FUNC_SQRT(81), + FUNC_RAD(82), + FUNC_DEG(83), + FUNC_ROUND(84), + FUNC_FLOOR(85), + FUNC_CEIL(86), + FUNC_MAX(87), + FUNC_MIN(88), + FUNC_AVG(89), + FUNC_SUM(90), + FUNC_LEN(91), + FUNC_ANY(92), + FUNC_ALL(93), + FUNC_LSB(94), + FUNC_MSB(95), + FUNC_RND(96), // push a random byte on the stack + FUNC_RNDW(97), // push a random word on the stack + FUNC_RNDF(98) // push a random float on the stack (between 0.0 and 1.0) } class Memory { @@ -259,7 +260,15 @@ class Value(val type: DataType, private val numericvalue: Number?, val stringval } override fun toString(): String { - return "$type: $numericvalue $stringvalue" + return when(type) { + DataType.BYTE -> "%02x".format(byteval) + DataType.WORD -> "%04x".format(wordval) + DataType.FLOAT -> floatval.toString() + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> "\"$stringvalue\"" + DataType.ARRAY -> TODO("array") + DataType.ARRAY_W -> TODO("word array") + DataType.MATRIX -> TODO("matrix") + } } fun numericValue(): Number { @@ -493,7 +502,7 @@ class Value(val type: DataType, private val numericvalue: Number?, val stringval } -data class Instruction(val opcode: Opcode, +open class Instruction(val opcode: Opcode, val arg: Value? = null, val callLabel: String? = null) { @@ -501,13 +510,29 @@ data class Instruction(val opcode: Opcode, var nextAlt: Instruction? = null override fun toString(): String { - return if(callLabel==null) - "$opcode $arg" - else - "$opcode $callLabel $arg" + val argStr = arg?.toString() ?: "" + val result = + when { + opcode==Opcode.SYSCALL -> { + val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() } + "syscall $syscall" + } + callLabel==null -> "${opcode.toString().toLowerCase()} $argStr" + else -> "${opcode.toString().toLowerCase()} $callLabel $argStr" + } + .trimEnd() + + if(opcode==Opcode.LINE) + return " _$result" + return " $result" } } +class Label(val name: String) : Instruction(opcode = Opcode.NOP) { + override fun toString(): String { + return "$name:" + } +} private class VmExecutionException(msg: String?) : Exception(msg) @@ -527,8 +552,9 @@ private class MyStack : Stack() { } } -class Program (prog: MutableList, - labels: Map, +class Program (val name: String, + prog: MutableList, + val labels: Map, val variables: Map, val memory: Map>) { @@ -554,7 +580,7 @@ class Program (prog: MutableList, } else throw VmExecutionException("syntax error at line ${lineNr+1}") } - return Program(instructions, labels, vars, memory) + return Program(filename, instructions, labels, vars, memory) } private fun loadInstructions(lines: Iterator>): Pair, Map> { @@ -706,10 +732,10 @@ class Program (prog: MutableList, prog.add(Instruction(Opcode.TERMINATE)) prog.add(Instruction(Opcode.NOP)) program = prog - connect(labels) + connect() } - private fun connect(labels: Map) { + private fun connect() { val it1 = program.iterator() val it2 = program.iterator() it2.next() @@ -753,6 +779,30 @@ class Program (prog: MutableList, } } } + + fun print(out: PrintStream) { + out.println("; stackVM program code for '$name'") + out.println("%memory") + if(memory.isNotEmpty()) { + TODO("print out initial memory load") + } + out.println("%end_memory") + out.println("%variables") + for (variable in variables) { + val valuestr = variable.value.toString() + out.println("${variable.key} ${variable.value.type.toString().toLowerCase()} $valuestr") + } + out.println("%end_variables") + out.println("%instructions") + val labels = this.labels.entries.associateBy({it.value}) {it.key} + for(instr in this.program) { + val label = labels[instr] + if(label!=null) + out.println("$label:") + out.println(instr) + } + out.println("%end_instructions") + } } @@ -1324,6 +1374,8 @@ fun main(args: Array) { } val program = Program.load(args.first()) + program.print(System.out) + val vm = StackVm(traceOutputFile = null) val dialog = ScreenDialog() vm.load(program, dialog.canvas)