diff --git a/compiler/res/prog8lib/c64flt.p8 b/compiler/res/prog8lib/c64flt.p8 index 134f4d9a4..c8f0e3378 100644 --- a/compiler/res/prog8lib/c64flt.p8 +++ b/compiler/res/prog8lib/c64flt.p8 @@ -954,6 +954,16 @@ func_sum_f .proc bne - + jmp push_fac1_as_result .pend + +sign_f .proc + jsr pop_float_fac1 + jsr SIGN + sta c64.ESTACK_LO,x + dex + rts + .pend + + }} } ; ------ end of block c64flt diff --git a/compiler/res/prog8lib/math.asm b/compiler/res/prog8lib/math.asm index cec232150..e6fdcdb20 100644 --- a/compiler/res/prog8lib/math.asm +++ b/compiler/res/prog8lib/math.asm @@ -643,3 +643,40 @@ mul_word_40 .proc sta c64.ESTACK_HI+1,x rts .pend + +sign_b .proc + lda c64.ESTACK_LO+1,x + beq _sign_zero + bmi _sign_neg +_sign_pos lda #1 + sta c64.ESTACK_LO+1,x + rts +_sign_neg lda #-1 +_sign_zero sta c64.ESTACK_LO+1,x + rts + .pend + +sign_ub .proc + lda c64.ESTACK_LO+1,x + beq sign_b._sign_zero + bne sign_b._sign_pos + .pend + +sign_w .proc + lda c64.ESTACK_HI+1,x + bmi sign_b._sign_neg + beq sign_ub + bne sign_b._sign_pos + .pend + +sign_uw .proc + lda c64.ESTACK_HI+1,x + beq _sign_possibly_zero +_sign_pos lda #1 + sta c64.ESTACK_LO+1,x + rts +_sign_possibly_zero lda c64.ESTACK_LO+1,x + bne _sign_pos + sta c64.ESTACK_LO+1,x + rts + .pend diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 0925641a2..69c46361a 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -101,6 +101,18 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val else -> throw AssemblyError("weird type $dt") } } + "sgn" -> { + translateFunctionArguments(fcall.arglist, func) + val dt = fcall.arglist.single().inferType(program) + when(dt.typeOrElse(DataType.STRUCT)) { + DataType.UBYTE -> asmgen.out(" jsr math.sign_ub") + DataType.BYTE -> asmgen.out(" jsr math.sign_b") + DataType.UWORD -> asmgen.out(" jsr math.sign_uw") + DataType.WORD -> asmgen.out(" jsr math.sign_w") + DataType.FLOAT -> asmgen.out(" jsr c64flt.sign_f") + else -> throw AssemblyError("weird type $dt") + } + } "sin", "cos", "tan", "atan", "ln", "log2", "sqrt", "rad", "deg", "round", "floor", "ceil", @@ -200,7 +212,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val is ArrayIndexedExpression -> TODO("lsr sbyte $what") is DirectMemoryRead -> TODO("lsr sbyte $what") is RegisterExpr -> TODO("lsr sbyte $what") - is IdentifierReference -> TODO("lsr sbyte $what") + is IdentifierReference -> { + val variable = asmgen.asmIdentifierName(what) + asmgen.out(" lda $variable | asl a | ror $variable") + } else -> throw AssemblyError("weird type") } } @@ -217,7 +232,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.WORD -> { when (what) { is ArrayIndexedExpression -> TODO("lsr sword $what") - is IdentifierReference -> TODO("lsr sword $what") + is IdentifierReference -> { + val variable = asmgen.asmIdentifierName(what) + asmgen.out(" lda $variable+1 | asl a | ror $variable+1 | ror $variable") + } else -> throw AssemblyError("weird type") } } diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index e863e59cb..d66ec4f77 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -34,6 +34,7 @@ val BuiltinFunctions = mapOf( "abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument "len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length // normal functions follow: + "sgn" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ), "sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) }, "sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ), "sin8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ), @@ -184,7 +185,7 @@ fun builtinFunctionReturnType(function: String, args: List, program: } -class NotConstArgumentException: AstException("not a const argument to a built-in function") +class NotConstArgumentException: AstException("not a const argument to a built-in function") // TODO: ugly, remove throwing exceptions for control flow private fun oneDoubleArg(args: List, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue { @@ -359,6 +360,13 @@ private fun builtinCos16u(args: List, position: Position, program: P return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position) } +private fun builtinSgn(args: List, position: Position, program: Program): NumericLiteralValue { + if (args.size != 1) + throw SyntaxError("sgn requires one argument", position) + val constval = args[0].constValue(program) ?: throw NotConstArgumentException() + return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toShort(), position) +} + private fun numericLiteral(value: Number, position: Position): NumericLiteralValue { val floatNum=value.toDouble() val tweakedValue: Number = diff --git a/compiler/src/prog8/optimizer/SimplifyExpressions.kt b/compiler/src/prog8/optimizer/SimplifyExpressions.kt index c70f17659..2dc1acd79 100644 --- a/compiler/src/prog8/optimizer/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizer/SimplifyExpressions.kt @@ -8,6 +8,7 @@ import prog8.ast.statements.Assignment import prog8.ast.statements.Statement import kotlin.math.abs import kotlin.math.log2 +import kotlin.math.pow /* todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it) @@ -216,12 +217,6 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying } } - // WORD >> 8 --> msb(WORD) - if(expr.operator == ">>" && leftDt in WordDatatypes && rightVal?.number == 8) { - optimizationsDone++ - return FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) - } - if (expr.operator == "+" || expr.operator == "-" && leftVal == null && rightVal == null && leftDt in NumericDatatypes && rightDt in NumericDatatypes) { @@ -346,6 +341,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying "-" -> return optimizeSub(expr, leftVal, rightVal) "**" -> return optimizePower(expr, leftVal, rightVal) "%" -> return optimizeRemainder(expr, leftVal, rightVal) + ">>" -> return optimizeShiftRight(expr, rightVal) + "<<" -> return optimizeShiftLeft(expr, rightVal) } return expr } @@ -611,12 +608,16 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying } + private val powersOfTwo = (1 .. 16).map { (2.0).pow(it) } + private val negativePowersOfTwo = powersOfTwo.map { -it } + private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression { if(leftVal==null && rightVal==null) return expr - // cannot shuffle assiciativity with division! + // TODO fix bug in this routine! + // cannot shuffle assiciativity with division! if(rightVal!=null) { // right value is a constant, see if we can optimize val rightConst: NumericLiteralValue = rightVal @@ -640,7 +641,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying return expr.left } } - 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0, 8192.0, 16384.0, 32768.0, 65536.0 -> { + in powersOfTwo -> { if(leftDt in IntegerDatatypes) { // divided by a power of two => shift right optimizationsDone++ @@ -648,7 +649,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position) } } - -2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> { + in negativePowersOfTwo -> { if(leftDt in IntegerDatatypes) { // divided by a negative power of two => negate, then shift right optimizationsDone++ @@ -733,4 +734,97 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying return expr } + + private fun optimizeShiftLeft(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression { + if(amountLv==null) + return expr + + val amount=amountLv.number.toInt() + if(amount==0) { + optimizationsDone++ + return expr.left + } + val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT) + when(targetDt) { + DataType.UBYTE, DataType.BYTE -> { + if(amount>=8) { + optimizationsDone++ + return NumericLiteralValue.optimalInteger(0, expr.position) + } + } + DataType.UWORD, DataType.WORD -> { + if(amount>=16) { + optimizationsDone++ + return NumericLiteralValue.optimalInteger(0, expr.position) + } + else if(amount>=8) { + optimizationsDone++ + val lsb=TypecastExpression(expr.left, DataType.UBYTE, true, expr.position) + if(amount==8) { + return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), lsb), expr.position) + } + val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position) + return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), shifted), expr.position) + } + } + else -> {} + } + return expr + } + + private fun optimizeShiftRight(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression { + if(amountLv==null) + return expr + + val amount=amountLv.number.toInt() + if(amount==0) { + optimizationsDone++ + return expr.left + } + val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT) + when(targetDt) { + DataType.UBYTE -> { + if(amount>=8) { + optimizationsDone++ + return NumericLiteralValue.optimalInteger(0, expr.position) + } + } + DataType.BYTE -> { + if(amount>8) { + expr.right = NumericLiteralValue.optimalInteger(8, expr.right.position) + return expr + } + } + DataType.UWORD -> { + if(amount>=16) { + optimizationsDone++ + return NumericLiteralValue.optimalInteger(0, expr.position) + } + else if(amount>=8) { + optimizationsDone++ + val msb=FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) + if(amount==8) + return msb + return BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position) + } + } + DataType.WORD -> { + if(amount>16) { + expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position) + return expr + } else if(amount>=8) { + optimizationsDone++ + val msbAsByte = TypecastExpression( + FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position), + DataType.BYTE, + true, expr.position) + if(amount==8) + return msbAsByte + return BinaryExpression(msbAsByte, ">>", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position) + } + } + else -> {} + } + return expr + } } diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 16b7628ea..ff5c275af 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -516,8 +516,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV optimizationsDone++ return NopStatement.insteadOf(assignment) } - if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) || - ((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) { + if ((targetDt == DataType.UWORD && cv > 15.0) || (targetDt == DataType.UBYTE && cv > 7.0)) { assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position) assignment.value.linkParents(assignment) optimizationsDone++ diff --git a/docs/source/programming.rst b/docs/source/programming.rst index bbeef71b9..7e5fcc5a0 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -727,6 +727,9 @@ lsb(x) msb(x) Get the most significant byte of the word x. +sgn(x) + Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive). + mkword(lsb, msb) Efficiently create a word value from two bytes (the lsb and the msb). Avoids multiplication and shifting. diff --git a/examples/bdmusic_irq.p8 b/examples/bdmusic-irq.p8 similarity index 100% rename from examples/bdmusic_irq.p8 rename to examples/bdmusic-irq.p8 diff --git a/examples/test.p8 b/examples/test.p8 index d52107157..22cfb34c9 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,55 +6,78 @@ main { sub start() { + byte ub = 100 + byte ub2 + word uw = 22222 + word uw2 - uword start=1027 - uword stop=2020 - uword i - ubyte ib + ub = -100 + c64scr.print_b(ub >> 1) + c64.CHROUT('\n') + c64scr.print_b(ub >> 2) + c64.CHROUT('\n') + c64scr.print_b(ub >> 7) + c64.CHROUT('\n') + c64scr.print_b(ub >> 8) + c64.CHROUT('\n') + c64scr.print_b(ub >> 9) + c64.CHROUT('\n') + c64scr.print_b(ub >> 16) + c64.CHROUT('\n') + c64scr.print_b(ub >> 26) + c64.CHROUT('\n') + c64.CHROUT('\n') - c64scr.print("\n\n\n\n\n\n\n\n") - memset($0400, 40*25, 30) - - ubyte ibstart = 1 - for ib in ibstart to 255-ibstart { - @(ib+1024) = 44 - } - - for ib in 253 to 2 step -1 { - @(ib+1024) = 3 - } - - ibstart = 3 - for ib in 255-ibstart to ibstart step -1 { - @(ib+1024) = 45 - } + ub = 100 + c64scr.print_b(ub >> 1) + c64.CHROUT('\n') + c64scr.print_b(ub >> 2) + c64.CHROUT('\n') + c64scr.print_b(ub >> 7) + c64.CHROUT('\n') + c64scr.print_b(ub >> 8) + c64.CHROUT('\n') + c64scr.print_b(ub >> 9) + c64.CHROUT('\n') + c64scr.print_b(ub >> 16) + c64.CHROUT('\n') + c64scr.print_b(ub >> 26) + c64.CHROUT('\n') + c64.CHROUT('\n') - for i in 1025 to 2022 { - @(i) = 1 - } + uw = -22222 + c64scr.print_w(uw >> 1) + c64.CHROUT('\n') + c64scr.print_w(uw >> 7) + c64.CHROUT('\n') + c64scr.print_w(uw >> 8) + c64.CHROUT('\n') + c64scr.print_w(uw >> 9) + c64.CHROUT('\n') + c64scr.print_w(uw >> 15) + c64.CHROUT('\n') + c64scr.print_w(uw >> 16) + c64.CHROUT('\n') + c64scr.print_w(uw >> 26) + c64.CHROUT('\n') + c64.CHROUT('\n') - for i in 2021 to 1026 step -1 { - @(i) = 92 - } - - for i in start to stop { - @(i) = 0 - } - - for i in stop-1 to start+1 step -1 { - @(i) = 91 - } - - - ubyte xx=X - c64scr.print_ub(xx) - - -; for i in stop to start { -; c64scr.print_uw(i) -; c64.CHROUT(',') -; } + uw = 22222 + c64scr.print_w(uw >> 1) + c64.CHROUT('\n') + c64scr.print_w(uw >> 7) + c64.CHROUT('\n') + c64scr.print_w(uw >> 8) + c64.CHROUT('\n') + c64scr.print_w(uw >> 9) + c64.CHROUT('\n') + c64scr.print_w(uw >> 15) + c64.CHROUT('\n') + c64scr.print_w(uw >> 16) + c64.CHROUT('\n') + c64scr.print_w(uw >> 26) + c64.CHROUT('\n') } }