From c05cd72d23c907447c3190217bcf6e54e2057908 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 15 Sep 2018 18:43:23 +0200 Subject: [PATCH] vm tweaks --- compiler/examples/test.p8 | 21 ++++++-- compiler/src/prog8/compiler/Compiler.kt | 51 +++++++++++++++++-- .../src/prog8/functions/BuiltinFunctions.kt | 7 +-- compiler/src/prog8/stackvm/StackVm.kt | 43 +++++++++++----- docs/source/programming.rst | 9 ++++ 5 files changed, 105 insertions(+), 26 deletions(-) diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 173f6d124..3cdc6df23 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -19,6 +19,7 @@ const float round1 = len([1,2,3]) const float sin1 = len([1,2,3]) float cos1 = len([1,2,3]) + } ~ main $c003 { @@ -41,7 +42,7 @@ const byte wa2b= abs(-99.w) const byte wa2c = abs(-99) const word wa2d = abs(-999) - const float wa3 = abs(-1.23456) + float wa3 = abs(-1.23456) const float wa4 = abs(-133) const float avg1 = avg([-1.23456, 99999]) const float sum1 = sum([-1.23456, 99999]) @@ -59,6 +60,15 @@ const word min1 = min([1,2,3,99+22]) word dinges = round(not_main.len1) + wa3 = rnd() + wa3 = rndw() + wa3 = rndf(22) + + A += 8 + A += rnd() + A =A+ rnd() + + A = X>2 X = Y>Y @@ -177,11 +187,12 @@ blerp3 = blerp2 A=blerp2 A=$02 - A=$002 - A=$00ff + ;A=$02.w ; @todo error word/byte + ;A=$002 ; @todo error word/byte + ;A=$00ff ; @todo error word/byte A=%11001100 - A=%0010011001 - A=99.w + ; A=%0010011001 ;@todo error word/byte + ; A=99.w ; @todo error word/byte XY=blerp1 X=blerp2 return diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index ea02c6214..3d218a5c6 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -142,7 +142,10 @@ class Compiler(private val options: CompilationOptions) { when(directive.directive) { "%asminclude" -> throw CompilerException("can't use %asminclude in stackvm") "%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm") - "%breakpoint" -> stackvmProg.instruction("break") + "%breakpoint" -> { + stackvmProg.line(directive.position) + stackvmProg.instruction("break") + } } return super.process(directive) } @@ -154,7 +157,7 @@ class Compiler(private val options: CompilationOptions) { is AnonymousStatementList -> translate(stmt.statements) is Label -> translate(stmt) is Return -> translate(stmt) - is Assignment -> translate(stmt) + is Assignment -> translate(stmt) // normal and augmented assignments is PostIncrDecr -> translate(stmt) is Jump -> translate(stmt) is FunctionCallStatement -> translate(stmt) @@ -179,6 +182,7 @@ class Compiler(private val options: CompilationOptions) { * _stmt_999_continue: * ... */ + stackvmProg.line(branch.position) val labelElse = makeLabel("else") val labelContinue = makeLabel("continue") val opcode = when(branch.condition) { @@ -220,6 +224,7 @@ class Compiler(private val options: CompilationOptions) { * _stmt_999_continue: * ... */ + stackvmProg.line(stmt.position) translate(stmt.condition) val labelElse = makeLabel("else") val labelContinue = makeLabel("continue") @@ -255,7 +260,9 @@ class Compiler(private val options: CompilationOptions) { stackvmProg.instruction("syscall FUNC_${expr.target.nameInSource[0].toUpperCase()}") // call builtin function } else { when(target) { - is Subroutine -> stackvmProg.instruction("call ${target.scopedname}") + is Subroutine -> { + stackvmProg.instruction("call ${target.scopedname}") + } else -> TODO("non-builtin-function call to $target") } } @@ -263,7 +270,9 @@ class Compiler(private val options: CompilationOptions) { is IdentifierReference -> { val target = expr.targetStatement(namespace) when(target) { - is VarDecl -> stackvmProg.instruction("push_var ${target.scopedname}") + is VarDecl -> { + stackvmProg.instruction("push_var ${target.scopedname}") + } else -> throw CompilerException("expression identifierref should be a vardef, not $target") } } @@ -309,6 +318,7 @@ class Compiler(private val options: CompilationOptions) { } private fun translate(stmt: FunctionCallStatement) { + stackvmProg.line(stmt.position) val targetStmt = stmt.target.targetStatement(namespace)!! if(targetStmt is BuiltinFunctionStatementPlaceholder) { stmt.arglist.forEach { translate(it) } @@ -337,10 +347,12 @@ class Compiler(private val options: CompilationOptions) { else -> throw CompilerException("invalid jump target type ${target::class}") } } + stackvmProg.line(stmt.position) stackvmProg.instruction(instr) } private fun translate(stmt: PostIncrDecr) { + stackvmProg.line(stmt.position) if(stmt.target.register!=null) { when(stmt.operator) { "++" -> stackvmProg.instruction("inc_var ${stmt.target.register}") @@ -356,7 +368,33 @@ class Compiler(private val options: CompilationOptions) { } 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) { @@ -402,6 +440,7 @@ class Compiler(private val options: CompilationOptions) { if(stmt.values.isNotEmpty()) { TODO("return with value(s) not yet supported: $stmt") } + stackvmProg.line(stmt.position) stackvmProg.instruction("return") } @@ -472,6 +511,10 @@ class StackVmProgram(val name: String) { fun label(name: String) { instructions.add("$name:") } + + fun line(position: Position) { + instructions.add(" _line ${position.line} ${position.file}") + } } enum class OutputType { diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 8d4fde311..44fcb4396 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -7,7 +7,7 @@ import kotlin.math.floor val BuiltinFunctionNames = setOf( "P_carry", "P_irqd", "rol", "ror", "rol2", "ror2", "lsl", "lsr", - "sin", "cos", "abs", "acos", "asin", "tan", "atan", + "sin", "cos", "abs", "acos", "asin", "tan", "atan", "rnd", "rndw", "rndf", "log", "log10", "sqrt", "rad", "deg", "round", "floor", "ceil", "max", "min", "avg", "sum", "len", "any", "all", "lsb", "msb") @@ -40,8 +40,9 @@ fun builtinFunctionReturnType(function: String, args: List, namespa } return when (function) { - "sin", "cos", "tan", "asin", "acos", "atan", "log", "log10", "sqrt", "rad", "deg", "avg" -> DataType.FLOAT - "len", "lsb", "msb", "any", "all" -> DataType.BYTE + "sin", "cos", "tan", "asin", "acos", "atan", "log", "log10", "sqrt", "rad", "deg", "avg", "rndf" -> DataType.FLOAT + "len", "lsb", "msb", "any", "all", "rnd" -> DataType.BYTE + "rndw" -> DataType.WORD "rol", "rol2", "ror", "ror2", "P_carry", "P_irqd" -> null // no return value so no datatype "abs" -> args.single().resultingDatatype(namespace) "max", "min", "sum" -> datatypeFromListArg(args.single()) diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 5e3920ad2..7be737003 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -118,7 +118,8 @@ enum class Opcode { CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations NOP, BREAK, // breakpoint - TERMINATE // end the program + TERMINATE, // end the program + _LINE // record source file line number } enum class Syscall(val callNr: Short) { @@ -131,10 +132,6 @@ enum class Syscall(val callNr: Short) { GFX_PIXEL(16), // plot a pixel at (x,y,color) pushed on stack in that order 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 - RANDOM(19), // push a random byte on the stack - RANDOM_W(20), // push a random word on the stack - RANDOM_F(21), // push a random float on the stack (between 0.0 and 1.0) - FUNC_P_CARRY(100), FUNC_P_IRQD(101), @@ -167,8 +164,10 @@ enum class Syscall(val callNr: Short) { FUNC_ANY(128), FUNC_ALL(129), FUNC_LSB(130), - FUNC_MSB(131) - + 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) } class Memory { @@ -575,6 +574,7 @@ class Program (prog: MutableList, val opcode=Opcode.valueOf(parts[0].toUpperCase()) val args = if(parts.size==2) parts[1] else null val instruction = when(opcode) { + Opcode._LINE -> Instruction(opcode, Value(DataType.STR, null, stringvalue = args)) Opcode.JUMP, Opcode.CALL, Opcode.BMI, Opcode.BPL, Opcode.BEQ, Opcode.BNE, Opcode.BCS, Opcode.BCC -> { if(args!!.startsWith('$')) { @@ -608,7 +608,7 @@ class Program (prog: MutableList, labels[nextInstructionLabelname] = instruction nextInstructionLabelname = "" } - } + } else throw VmExecutionException("syntax error at line ${lineNr+1}") } } @@ -770,6 +770,7 @@ class StackVm(val traceOutputFile: String?) { private lateinit var currentIns: Instruction private lateinit var canvas: BitmapScreenPanel private val rnd = Random() + private var sourceLine = "" fun load(program: Program, canvas: BitmapScreenPanel) { this.program = program.program @@ -809,7 +810,11 @@ class StackVm(val traceOutputFile: String?) { throw VmExecutionException("too many nested/recursive calls") } catch (bp: VmBreakpointException) { currentIns = currentIns.next + println("breakpoint encountered, source line: $sourceLine") throw bp + } catch (es: EmptyStackException) { + System.err.println("stack error! source line: $sourceLine") + throw es } } val time = System.currentTimeMillis()-start @@ -1012,16 +1017,16 @@ class StackVm(val traceOutputFile: String?) { val (y, x) = evalstack.pop2() canvas.writeText(x.integerValue(), y.integerValue(), text.stringvalue!!, color.integerValue()) } - Syscall.RANDOM -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255)) - Syscall.RANDOM_W -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535)) - Syscall.RANDOM_F -> evalstack.push(Value(DataType.FLOAT, rnd.nextDouble())) + Syscall.FUNC_RND -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255)) + Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535)) + Syscall.FUNC_RNDF -> evalstack.push(Value(DataType.FLOAT, rnd.nextDouble())) else -> throw VmExecutionException("unimplemented syscall $syscall") } } Opcode.SEC -> carry = true Opcode.CLC -> carry = false - Opcode.TERMINATE -> throw VmTerminationException("execution terminated") + Opcode.TERMINATE -> throw VmTerminationException("terminate instruction") Opcode.BREAK -> throw VmBreakpointException() Opcode.INC_MEM -> { @@ -1129,7 +1134,11 @@ class StackVm(val traceOutputFile: String?) { Opcode.BMI -> return if(evalstack.pop().numericValue().toDouble()<0.0) ins.next else ins.nextAlt!! Opcode.BPL -> return if(evalstack.pop().numericValue().toDouble()>=0.0) ins.next else ins.nextAlt!! Opcode.CALL -> callstack.push(ins.nextAlt) - Opcode.RETURN -> return callstack.pop() + Opcode.RETURN -> { + if(callstack.empty()) + throw VmTerminationException("return instruction with empty call stack") + return callstack.pop() + } Opcode.PUSH_VAR -> { val varname = ins.arg!!.stringvalue ?: throw VmExecutionException("${ins.opcode} expects string argument (the variable name)") val variable = variables[varname] ?: throw VmExecutionException("unknown variable: $varname") @@ -1262,6 +1271,9 @@ class StackVm(val traceOutputFile: String?) { throw VmExecutionException("attempt to make a float from a non-word value $byte") } } + Opcode._LINE -> { + sourceLine = ins.arg!!.stringvalue!! + } else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}") } @@ -1292,12 +1304,15 @@ fun main(args: Array) { dialog.isVisible = true dialog.start() - val programTimer = Timer(10) { _ -> + val programTimer = Timer(10) { a -> try { vm.step() } catch(bp: VmBreakpointException) { println("Breakpoint: execution halted. Press enter to resume.") readLine() + } catch (tx: VmTerminationException) { + println("Execution halted: ${tx.message}") + (a.source as Timer).stop() } } programTimer.start() diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 0f3fca2b0..3160e51d1 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -486,6 +486,15 @@ any(x) all(x) 1 ('true') if all of the values in the non-scalar (array or matrix) value x are 'true' (not zero), else 0 ('false') +rnd() + returns a pseudo-random byte from 0..255 + +rndw() + returns a pseudo-random word from 0..65535 + +rndf() + returns a pseudo-random float between 0.0 and 1.0 + lsl(x) Shift the bits in x (byte or word) one position to the left. Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)