diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index ee464e189..d6e81ea0e 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -112,6 +112,7 @@ val BuiltinFunctions: Map = mapOf( "sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)), "sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)), "sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)), + "sqrt__long" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.LONG)), "sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)), "divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)), "divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)), diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 41a1b18f9..32660e9ab 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -905,7 +905,7 @@ class AsmGen6502Internal ( TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true) - TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}") + TargetStorageKind.POINTER -> throw AssemblyError("assign long expression to pointer ${target.position}") TargetStorageKind.VOID -> { /* do nothing */ } } } else if(value is PtTypeCast && value.type.isLong) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 802d715f7..6fca535d3 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -40,7 +40,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, "max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(fcall, resultRegister) "abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope) "sgn" -> funcSgn(fcall, resultRegister, sscope) - "sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope) + "sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope) "divmod__ubyte" -> funcDivmod(fcall) "divmod__uword" -> funcDivmodW(fcall) "rol" -> funcRol(fcall) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt index 51af8e6cc..5d33bdeeb 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt @@ -386,7 +386,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat bne $falseLabel""") } else { // TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between... - asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32) + asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned) asmgen.out(""" lda cx16.r14 cmp #$${hex.substring(6, 8)} @@ -424,7 +424,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat bne $falseLabel""") } else { // TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between... - asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32) + asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned) asmgen.out(""" lda cx16.r14 cmp $varname2 @@ -493,7 +493,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat bne $falseLabel""") } else { asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1) - asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32) + asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned) asmgen.out(""" lda cx16.r14 ora cx16.r14+1 @@ -518,7 +518,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat beq $falseLabel""") } else { asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1) - asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32) + asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned) asmgen.out(""" lda cx16.r14 ora cx16.r14+1 diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 138aa8174..76b8e37af 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -4545,7 +4545,7 @@ $endLabel""") stz $startreg+2 stz $startreg+3""") } - TargetStorageKind.POINTER -> TODO("assign long to pointer ${target.position}") + TargetStorageKind.POINTER -> TODO("assign long 0 to pointer ${target.position}") TargetStorageKind.VOID -> { /* do nothing */ } } return diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/PointerAssignmentsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/PointerAssignmentsGen.kt index 8681941b7..7ae79cb49 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/PointerAssignmentsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/PointerAssignmentsGen.kt @@ -1841,7 +1841,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri pha""") } in combinedLongRegisters -> { - TODO("save on stack long register pair") + TODO("save on stack long register pair - do we really want to do this?") } else -> asmgen.saveRegisterStack(regs.asCpuRegister(), false) } @@ -1876,7 +1876,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri sta $regname""") } in combinedLongRegisters -> { - TODO("restore from stack long register") + TODO("restore from stack long register - do we really want to do this?") } else -> asmgen.restoreRegisterStack(regs.asCpuRegister(), false) } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 157c9431b..16d599073 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -17,7 +17,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call) "cmp" -> funcCmp(call) "sgn" -> funcSgn(call) - "sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call) + "sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(call) "divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE) "divmod__uword" -> funcDivmod(call, IRDataType.WORD) "rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt index 542a17b6a..8e04aaeed 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -244,8 +244,16 @@ class VarConstantValueTypeAdjuster( dt.isUnsignedByte -> "sqrt__ubyte" dt.isUnsignedWord -> "sqrt__uword" dt.isFloat -> "sqrt__float" + dt.isLong -> { + val value = functionCallExpr.args[0].constValue(program)?.number + if(value!=null && value<0) { + errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position) + return noModifications + } + "sqrt__long" + } else -> { - errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position) + errors.err("expected numeric argument", functionCallExpr.args[0].position) return noModifications } } diff --git a/compiler/src/prog8/compiler/BuiltinFunctions.kt b/compiler/src/prog8/compiler/BuiltinFunctions.kt index d883877af..46561b8e0 100644 --- a/compiler/src/prog8/compiler/BuiltinFunctions.kt +++ b/compiler/src/prog8/compiler/BuiltinFunctions.kt @@ -20,7 +20,19 @@ internal val constEvaluatorsForBuiltinFuncs: Map "sgn" to ::builtinSgn, "sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } }, "sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } }, - "sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } }, + "sqrt__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { + val value=it.toDouble() + if(value<0) + throw CannotEvaluateException("sqrt", "argument cannot be negative") + else + sqrt(value) + } }, + "sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { + if(it<0) + throw CannotEvaluateException("sqrt", "argument cannot be negative") + else + sqrt(it) + } }, "lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } }, "lsb__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } }, "lsw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 65535).toDouble() } }, @@ -71,11 +83,14 @@ private fun oneIntArgOutputInt(args: List, position: Position, progr if(!constval.type.isInteger) throw SyntaxError("built-in function requires one integer argument", position) } else { - if(constval.type!=BaseDataType.UBYTE && constval.type!=BaseDataType.UWORD) + if(!constval.type.isInteger) throw SyntaxError("built-in function requires one integer argument", position) } val integer = constval.number.toInt() - return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position) + val result = function(integer) + if(result.isNaN()) + throw CannotEvaluateException("built-in function", "result is NaN $position") + return NumericLiteral.optimalInteger(result.toInt(), args[0].position) } private fun oneFloatArgOutputFloat(args: List, position: Position, program: Program, function: (arg: Double)->Double): NumericLiteral { @@ -84,8 +99,10 @@ private fun oneFloatArgOutputFloat(args: List, position: Position, p val constval = args[0].constValue(program) ?: throw NotConstArgumentException() if(constval.type != BaseDataType.FLOAT) throw SyntaxError("built-in function requires one float argument", position) - - return NumericLiteral(BaseDataType.FLOAT, function(constval.number), args[0].position) + val result = function(constval.number) + if(result.isNaN()) + throw CannotEvaluateException("built-in function", "result is NaN $position") + return NumericLiteral(BaseDataType.FLOAT, result, args[0].position) } private fun builtinAbs(args: List, position: Position, program: Program): NumericLiteral { diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 37584380f..0981cc3ee 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -58,9 +58,9 @@ sgn (x) Get the sign of the value (integer or floating point). The result is a byte: -1, 0 or 1 (negative, zero, positive). -sqrt (w) +sqrt (x) Returns the square root of the number. - Supports unsigned integer (result is ubyte) and floating point numbers. + Accepts unsigned integer (result is ubyte), long (result is uword, but this may not be implemented on all targets), and floating point numbers. To do the reverse - squaring a number - just write ``x*x``. divmod (dividend, divisor, quotient, remainder) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 63b8f8274..ec6cb6d2b 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -175,6 +175,7 @@ Optimizations - optimize optimizedBitwiseExpr() for const and variable operands to not assign needlessly to R0-R3. - optimize inplacemodificationLongWithLiteralval() for more shift values such as 8, 16, 24 etc but take sign bit into account! - optimize simple cases in funcPeekL and funcPokeL +- longvar = lptr^^ now goes via temporary registers, optimize this to avoid using temps. Also check lptr^^ = lvar. - Port benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up. - Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappeared. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again? - in Identifier: use typedarray of strings instead of listOf? Other places? diff --git a/examples/test.p8 b/examples/test.p8 index 3ebaf4543..263743162 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -13,10 +13,20 @@ main { long @shared l2 = -1 long @shared l3 = $ffffffff long @shared l4 = $7fffffff + ^^long lptr = 50000 l1 ^= -1 l2 ^= $ffffffff l3 ^= $7fffffff + l3 ^= l4 + + lptr^^ = 82348234 + l2 = lptr^^ + + lptr^^ = 0 ; TODO fix crash + lptr^^ = l2 ; TODO fix crash + lptr^^ = 82348234+l2 ; TODO fix crash + l3 = lptr^^+1 ; TODO fix crash ; cx16.r5L = 10 diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 0a61df7ea..4a9efb379 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -167,7 +167,7 @@ modr reg1, reg2 - remainder (modulo) of unsigned div mod reg1, value - remainder (modulo) of unsigned division reg1 %= value note: division by zero yields max signed int $ff/$ffff divmodr reg1, reg2 - unsigned division reg1/reg2, storing division and remainder on value stack (so need to be POPped off) divmod reg1, value - unsigned division reg1/value, storing division and remainder on value stack (so need to be POPped off) -sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f) +sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be l.1, .w or .b, result type in reg1 is .w or .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f) square reg1, reg2 - reg1 is the square of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f) sgn reg1, reg2 - reg1.b is the sign of reg2 (or fpreg1, if sgn.f) (0.b, 1.b or -1.b) cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction) @@ -1102,6 +1102,8 @@ data class IRInstruction( if(type==IRDataType.LONG) { if(opcode==Opcode.SGN) return IRDataType.BYTE + if(opcode==Opcode.SQRT) + return IRDataType.WORD } if(opcode==Opcode.JUMPI || opcode==Opcode.CALLI || opcode==Opcode.STOREZI || opcode==Opcode.LSIGW || opcode==Opcode.MSIGW) return IRDataType.WORD diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 048f7b955..79deb99ce 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -1444,7 +1444,12 @@ class VirtualMachine(irProgram: IRProgram) { when(i.type!!) { IRDataType.BYTE -> registers.setUB(i.reg1!!, sqrt(registers.getUB(i.reg2!!).toDouble()).toInt().toUByte()) IRDataType.WORD -> registers.setUB(i.reg1!!, sqrt(registers.getUW(i.reg2!!).toDouble()).toInt().toUByte()) - IRDataType.LONG -> TODO("long sqrt") + IRDataType.LONG -> { + val value = registers.getSL(i.reg2!!) + if(value<0) + throw IllegalArgumentException("sqrt of negative long $value reg=${i.reg2}") + registers.setSL(i.reg1!!, sqrt(value.toDouble()).toInt()) + } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, sqrt(registers.getFloat(i.fpReg2!!))) } nextPc()