From b96bb23a541c3144f108d770a83cfd329bae28f5 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 1 Oct 2018 01:01:39 +0200 Subject: [PATCH] added gfx_line function --- compiler/examples/spincube.p8 | 96 +++++++++++++++++++ compiler/examples/swirl.p8 | 25 +++++ compiler/src/prog8/ast/AstChecker.kt | 12 ++- .../src/prog8/functions/BuiltinFunctions.kt | 6 ++ compiler/src/prog8/stackvm/ScreenDialog.kt | 5 + compiler/src/prog8/stackvm/StackVm.kt | 12 ++- compiler/src/prog8/stackvm/Value.kt | 10 +- 7 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 compiler/examples/spincube.p8 create mode 100644 compiler/examples/swirl.p8 diff --git a/compiler/examples/spincube.p8 b/compiler/examples/spincube.p8 new file mode 100644 index 000000000..d2d54dc5f --- /dev/null +++ b/compiler/examples/spincube.p8 @@ -0,0 +1,96 @@ +%option enable_floats + +~ irq { + word global_time + byte time_changed + + sub irq() { + global_time++ + time_changed = 1 + } +} + + +~ main { + + const word width = 320 + const word height = 200 + + float x1 = -1.0 + float y1 = 1.0 + + float x2 = 1.0 + float y2 = 1.0 + + float x3 = 1.0 + float y3 = -1.0 + + float x4 = -1.0 + float y4 = -1.0 + + + float rx1 + float rx2 + float rx3 + float rx4 + float ry1 + float ry2 + float ry3 + float ry4 + + sub start() { + float t + _vm_gfx_clearscr(0) + + while(1) { + if irq.time_changed { + irq.time_changed = 0 + _vm_gfx_clearscr(0) + _vm_gfx_text(130, 80, 5, "Spin !!!") + t = flt(irq.global_time) / 60.0 + rotate_all(t) + plot_pixels() + } + } + } + + sub rotate_all(t: float) { + rx1 = x1 * cos(t) - y1 * sin(t) + ry1 = x1 * sin(t) + y1 * cos(t) + + rx2 = x2 * cos(t) - y2 * sin(t) + ry2 = x2 * sin(t) + y2 * cos(t) + + rx3 = x3 * cos(t) - y3 * sin(t) + ry3 = x3 * sin(t) + y3 * cos(t) + + rx4 = x4 * cos(t) - y4 * sin(t) + ry4 = x4 * sin(t) + y4 * cos(t) + } + + + sub plot_pixels() { + word sx1 + word sx2 + word sx3 + word sx4 + word sy1 + word sy2 + word sy3 + word sy4 + + sx1 = floor(rx1 * height/3 + width/2) + sx2 = floor(rx2 * height/3 + width/2) + sx3 = floor(rx3 * height/3 + width/2) + sx4 = floor(rx4 * height/3 + width/2) + sy1 = floor(ry1 * height/3 + height/2) + sy2 = floor(ry2 * height/3 + height/2) + sy3 = floor(ry3 * height/3 + height/2) + sy4 = floor(ry4 * height/3 + height/2) + + _vm_gfx_line(sx1, sy1, sx2, sy2, 1) + _vm_gfx_line(sx2, sy2, sx3, sy3, 7) + _vm_gfx_line(sx3, sy3, sx4, sy4, 10) + _vm_gfx_line(sx4, sy4, sx1, sy1, 14) + } +} diff --git a/compiler/examples/swirl.p8 b/compiler/examples/swirl.p8 new file mode 100644 index 000000000..69dd9a2c1 --- /dev/null +++ b/compiler/examples/swirl.p8 @@ -0,0 +1,25 @@ +%option enable_floats + +~ main { + + sub start() { + + _vm_gfx_clearscr(0) + _vm_gfx_text(5, 5, 7, "Swirl !!!") + + const word width = 320 + const word height = 200 + float x + float y + float t + byte color + + while(1) { + x = ((sin(t*1.01) +cos(t*1.1234)) * width/4.1) + width/2 + y = ((cos(t)+sin(t*0.03456)) * height/4.1) + height/2 + _vm_gfx_pixel(floor(x),floor(y), color//16) + t += 0.01 + color++ + } + } +} diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 793babfe2..0eda440c7 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -201,8 +201,12 @@ class AstChecker(private val namespace: INameScope, .filter { it is InlineAssembly } .map { (it as InlineAssembly).assembly } .count { "rts" in it || "\trts" in it || "jmp" in it || "\tjmp" in it } - if (amount == 0 && subroutine.returnvalues.isNotEmpty()) - err("subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") + if (amount == 0) { + if(subroutine.returnvalues.isNotEmpty()) + err("subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") + // if there's no return statement, we add the implicit one at the end. + subroutine.statements.add(Return(emptyList(), subroutine.position)) + } } } @@ -530,7 +534,7 @@ class AstChecker(private val namespace: INameScope, else { for (arg in args.withIndex().zip(func.parameters)) { if(arg.first.value.resultingDatatype(namespace, heap) !in arg.second.possibleDatatypes) - checkResult.add(SyntaxError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.possibleDatatypes}", position)) + checkResult.add(ExpressionError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.possibleDatatypes}", position)) } } } else if(target is Subroutine) { @@ -539,7 +543,7 @@ class AstChecker(private val namespace: INameScope, else { for (arg in args.withIndex().zip(target.parameters)) { if(arg.first.value.resultingDatatype(namespace, heap) != arg.second.type) - checkResult.add(SyntaxError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.type}", position)) + checkResult.add(ExpressionError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.type}", position)) } } } diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index bdda131c4..6f34da233 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -68,6 +68,12 @@ val BuiltinFunctions = mapOf( BuiltinFunctionParam("x", listOf(DataType.BYTE, DataType.WORD)), BuiltinFunctionParam("y", listOf(DataType.BYTE, DataType.WORD)), BuiltinFunctionParam("color", listOf(DataType.BYTE))), null), + "_vm_gfx_line" to FunctionSignature(false, listOf( + BuiltinFunctionParam("x1", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("y1", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("x2", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("y2", listOf(DataType.BYTE, DataType.WORD)), + BuiltinFunctionParam("color", listOf(DataType.BYTE))), null), "_vm_gfx_text" to FunctionSignature(false, listOf( BuiltinFunctionParam("x", listOf(DataType.BYTE, DataType.WORD)), BuiltinFunctionParam("y", listOf(DataType.BYTE, DataType.WORD)), diff --git a/compiler/src/prog8/stackvm/ScreenDialog.kt b/compiler/src/prog8/stackvm/ScreenDialog.kt index c06507b9f..beb99e74f 100644 --- a/compiler/src/prog8/stackvm/ScreenDialog.kt +++ b/compiler/src/prog8/stackvm/ScreenDialog.kt @@ -37,12 +37,17 @@ class BitmapScreenPanel : JPanel() { fun setPixel(x: Int, y: Int, color: Int) { image.setRGB(x, y, palette[color and 15].rgb) } + fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int) { + g2d.color = palette[color and 15] + g2d.drawLine(x1, y1, x2, y2) + } fun writeText(x: Int, y: Int, text: String, color: Int) { g2d.font = Font(Font.MONOSPACED, Font.PLAIN, 10) g2d.color = palette[color and 15] g2d.drawString(text, x, y + g2d.font.size - 1) } + companion object { const val SCREENWIDTH = 320 const val SCREENHEIGHT = 200 diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 2338fc231..d1d3a7eb3 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -131,6 +131,7 @@ 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 + GFX_LINE(19), // draw line on screen at (x1,y1,x2,y2,color) pushed on stack in that order FUNC_SIN(66), FUNC_COS(67), @@ -277,9 +278,9 @@ class StackVm(private var traceOutputFile: String?) { irqStartInstruction = program.labels["irq.irq"] } - fun step(instructionCount: Int = 10000) { + fun step(instructionCount: Int = 5000) { // step is invoked every 1/100 sec - // we execute 10k instructions in one go so we end up doing 1 million vm instructions per second + // we execute 5k instructions in one go so we end up doing 0.5 million vm instructions per second val start = System.currentTimeMillis() for(i:Int in 1..instructionCount) { try { @@ -494,6 +495,13 @@ class StackVm(private var traceOutputFile: String?) { val (y, x) = evalstack.pop2() canvas?.setPixel(x.integerValue(), y.integerValue(), color.integerValue()) } + Syscall.GFX_LINE -> { + // draw line at (x1, y1, x2, y2, color) from stack + val color = evalstack.pop() + val (y2, x2) = evalstack.pop2() + val (y1, x1) = evalstack.pop2() + canvas?.drawLine(x1.integerValue(), y1.integerValue(), x2.integerValue(), y2.integerValue(), color.integerValue()) + } Syscall.GFX_CLEARSCR -> { val color = evalstack.pop() canvas?.clearScreen(color.integerValue()) diff --git a/compiler/src/prog8/stackvm/Value.kt b/compiler/src/prog8/stackvm/Value.kt index c17db5ee8..4c8ab46cc 100644 --- a/compiler/src/prog8/stackvm/Value.kt +++ b/compiler/src/prog8/stackvm/Value.kt @@ -116,16 +116,20 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { return when(leftDt) { DataType.BYTE -> { // BYTE can become WORD if right operand is WORD, or when value is too large for byte - if(result.toDouble() >= 256) + if(result.toDouble() >= 256 && rightDt!=DataType.FLOAT) return Value(DataType.WORD, result) when(rightDt) { DataType.BYTE -> Value(DataType.BYTE, result) DataType.WORD -> Value(DataType.WORD, result) - DataType.FLOAT -> throw VmExecutionException("floating point loss of precision") + DataType.FLOAT -> throw VmExecutionException("floating point loss of precision on byte") else -> throw VmExecutionException("$op on non-numeric result type") } } - DataType.WORD -> Value(DataType.WORD, result) + DataType.WORD -> { + if(rightDt==DataType.FLOAT) + throw VmExecutionException("floating point loss of precision on word") + Value(DataType.WORD, result) + } DataType.FLOAT -> Value(DataType.FLOAT, result) else -> throw VmExecutionException("$op on non-numeric type") }