From 83ace753b28e0fcd50696032bf40fdd5b9811f5c Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 31 Mar 2019 13:54:22 +0200 Subject: [PATCH] got rid of problematic signed POW operator, added compiler checks for this --- compiler/res/version.txt | 2 +- compiler/src/prog8/ast/AstChecker.kt | 22 ++++--- compiler/src/prog8/compiler/Compiler.kt | 3 +- .../src/prog8/compiler/intermediate/Opcode.kt | 2 - .../src/prog8/compiler/target/c64/AsmGen.kt | 3 + compiler/src/prog8/stackvm/StackVm.kt | 16 ----- docs/source/syntaxreference.rst | 3 +- docs/source/targetsystem.rst | 4 -- docs/source/todo.rst | 3 +- examples/test.p8 | 66 +++++++++++++++++-- 10 files changed, 83 insertions(+), 41 deletions(-) diff --git a/compiler/res/version.txt b/compiler/res/version.txt index d798f5093..54ea73dde 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -1.5 (beta) +1.6 (beta) diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index d189c23e1..ee238c83a 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -673,6 +673,9 @@ private class AstChecker(private val namespace: INameScope, } override fun process(expr: BinaryExpression): IExpression { + val leftDt = expr.left.resultingDatatype(namespace, heap) + val rightDt = expr.right.resultingDatatype(namespace, heap) + when(expr.operator){ "/", "%" -> { val constvalRight = expr.right.constValue(namespace, heap) @@ -680,16 +683,23 @@ private class AstChecker(private val namespace: INameScope, if(divisor==0.0) checkResult.add(ExpressionError("division by zero", expr.right.position)) if(expr.operator=="%") { - val rightDt = constvalRight?.resultingDatatype(namespace, heap) - val leftDt = expr.left.resultingDatatype(namespace, heap) if ((rightDt != DataType.UBYTE && rightDt != DataType.UWORD) || (leftDt!=DataType.UBYTE && leftDt!=DataType.UWORD)) checkResult.add(ExpressionError("remainder can only be used on unsigned integer operands", expr.right.position)) } } + "**" -> { + if(leftDt in IntegerDatatypes) { + val constvalRight = expr.right.constValue(namespace, heap)?.asNumericValue?.toDouble() + if(constvalRight==null) { + if(rightDt==DataType.FLOAT || rightDt==DataType.BYTE || rightDt==DataType.WORD) + checkResult.add(ExpressionError("raising to a signed value requires floating point", expr.position)) + } else if(constvalRight<0.0) { + checkResult.add(ExpressionError("raising to negative power requires floating point", expr.position)) + } + } + } "and", "or", "xor" -> { // only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1) - val rightDt = expr.right.resultingDatatype(namespace, heap) - val leftDt = expr.left.resultingDatatype(namespace, heap) if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes) checkResult.add(ExpressionError("logical operator can only be used on boolean operands", expr.right.position)) val constLeft = expr.left.constValue(namespace, heap) @@ -699,15 +709,11 @@ private class AstChecker(private val namespace: INameScope, } "&", "|", "^" -> { // only integer numeric operands accepted - val rightDt = expr.right.resultingDatatype(namespace, heap) - val leftDt = expr.left.resultingDatatype(namespace, heap) if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes) checkResult.add(ExpressionError("bitwise operator can only be used on integer operands", expr.right.position)) } } - val leftDt = expr.left.resultingDatatype(namespace, heap) - val rightDt = expr.right.resultingDatatype(namespace, heap) if(leftDt !in NumericDatatypes) checkResult.add(ExpressionError("left operand is not numeric", expr.left.position)) if(rightDt!in NumericDatatypes) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index c331496f5..8565b6d50 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1239,9 +1239,8 @@ internal class Compiler(private val rootModule: Module, "**" -> { when(dt) { DataType.UBYTE -> Opcode.POW_UB - DataType.BYTE -> Opcode.POW_B + DataType.BYTE, DataType.WORD -> throw CompilerException("power operator only available for unsigned integer types and floats") DataType.UWORD -> Opcode.POW_UW - DataType.WORD -> Opcode.POW_W DataType.FLOAT -> Opcode.POW_F else -> throw CompilerException("only byte/word/float possible") } diff --git a/compiler/src/prog8/compiler/intermediate/Opcode.kt b/compiler/src/prog8/compiler/intermediate/Opcode.kt index 163d245f9..f2dadcf95 100644 --- a/compiler/src/prog8/compiler/intermediate/Opcode.kt +++ b/compiler/src/prog8/compiler/intermediate/Opcode.kt @@ -59,9 +59,7 @@ enum class Opcode { REMAINDER_UB, // signed remainder is undefined/unimplemented REMAINDER_UW, // signed remainder is undefined/unimplemented POW_UB, - POW_B, POW_UW, - POW_W, POW_F, NEG_B, NEG_W, diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 83dfbedf7..35dd03709 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -729,6 +729,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, Opcode.ABS_B -> " jsr prog8_lib.abs_b" Opcode.ABS_W -> " jsr prog8_lib.abs_w" Opcode.ABS_F -> " jsr c64flt.abs_f" + Opcode.POW_UB -> " jsr prog8_lib.pow_ub" // @todo implement + Opcode.POW_UW -> " jsr prog8_lib.pow_uw" // @todo implement + Opcode.POW_F -> " jsr c64flt.pow_f" // @todo implement Opcode.INV_BYTE -> { """ lda ${(ESTACK_LO + 1).toHex()},x diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index cd688d26c..710059b8b 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -631,22 +631,6 @@ class StackVm(private var traceOutputFile: String?) { evalstack.push(value) setFlags(value) } - Opcode.POW_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val value=second.pow(top) - evalstack.push(value) - setFlags(value) - } - Opcode.POW_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val value=second.pow(top) - evalstack.push(value) - setFlags(value) - } Opcode.POW_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index b2998663c..60bea7f77 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -372,7 +372,8 @@ Operators arithmetic: ``+`` ``-`` ``*`` ``/`` ``**`` ``%`` ``+``, ``-``, ``*``, ``/`` are the familiar arithmetic operations. ``/`` is division (will result in integer division when using on integer operands, and a floating point division when at least one of the operands is a float) - ``**`` is the power operator: ``3 ** 5`` is equal to 3*3*3*3*3 and is 243. + ``**`` is the power operator: ``3 ** 5`` is equal to 3*3*3*3*3 and is 243. (certain restrictions + apply when dealing with signed or negative numbers) ``%`` is the remainder operator: ``25 % 7`` is 4. Be careful: without a space, %10 will be parsed as the binary number 2 Remainder is only supported on integer operands (not floats). diff --git a/docs/source/targetsystem.rst b/docs/source/targetsystem.rst index 88c1ccfaf..874272188 100644 --- a/docs/source/targetsystem.rst +++ b/docs/source/targetsystem.rst @@ -173,7 +173,3 @@ as a subroutine ``irq`` in the module ``irq`` so like this:: ; ... irq handling here ... } } - - -.. todo:: - @todo the irq handler should use its own eval-stack to avoid stack interference issues diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4bbfd9edd..667796489 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -17,7 +17,7 @@ Memory Block Operations integrated in language? these should call (or emit inline) optimized pieces of assembly code, so they run as fast as possible -For now, we have the ``memcopy`` and ``memset`` builtin functions. +For now, we have the ``memcopy``, ``memset`` and ``strlen`` builtin functions. @@ -52,6 +52,7 @@ Allocate a fixed word in ZP that is the TOS so we can operate on TOS directly without having to to index into the stack? + More flexible (non-const) arrays? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Currently, array literals can only be constants diff --git a/examples/test.p8 b/examples/test.p8 index 8440a3618..adde1f9ad 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,15 +1,69 @@ %zeropage basicsafe +%option enable_floats +%import c64flt ~ main { - ubyte @zp var1 - sub start() { - ubyte @zp var1 - A=20 - A=var1 - Y=main.var1 + ubyte ub=2 + ubyte ub2=7 + uword uw=2 + uword uw2=5 + float fl=2.3 + float fl2=20 + + ub = ub ** 7 + c64scr.print_ub(ub) + c64.CHROUT('\n') + uw = uw ** 5 + c64scr.print_uw(uw) + c64.CHROUT('\n') + fl = fl ** 20.0 + c64flt.print_f(fl) + c64.CHROUT('\n') + + ub=3 ; @todo no instruction? + ub **=7 ; @todo no instruction? + c64scr.print_ub(ub) + c64.CHROUT('\n') + uw = 9 ; @todo no instruction? + uw **=5 ; @todo no instruction? + c64scr.print_uw(uw) + c64.CHROUT('\n') + fl = 2 ; @todo no instruction? + fl **=20.0 ; @todo no instruction? + c64flt.print_f(fl) + c64.CHROUT('\n') + + + ub=3 + ub **= 7 + + c64scr.print_ub(ub) + c64.CHROUT('\n') + uw = 9 + uw **= 5 + c64scr.print_uw(uw) + c64.CHROUT('\n') + fl = 2.3 + fl **= 20.0 + c64flt.print_f(fl) + c64.CHROUT('\n') + + ub=3 + ub **= ub2 + c64scr.print_ub(ub) + c64.CHROUT('\n') + uw = 9 + uw **= uw2 + c64scr.print_uw(uw) + c64.CHROUT('\n') + fl = 2.3 + fl **= fl2 + c64flt.print_f(fl) + c64.CHROUT('\n') + } }