diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index 98e555c2f..7d128ab04 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -96,9 +96,9 @@ val BuiltinFunctions: Map = mapOf( "sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), "sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE), "sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT), - "divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null), - "divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), - "divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), + "divmod" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + "divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), + "divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), "any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL), "all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL), "lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), @@ -133,4 +133,9 @@ val BuiltinFunctions: Map = mapOf( "call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), ) -val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse") +val InplaceModifyingBuiltinFunctions = setOf( + "setlsb", "setmsb", + "rol", "ror", "rol2", "ror2", + "sort", "reverse", + "divmod", "divmod__ubyte", "divmod__uword" +) diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt index cbfb3ea1f..184bb3ddd 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -75,7 +75,7 @@ class VarConstantValueTypeAdjuster( val declValue = decl.value?.constValue(program) if (declValue != null) { // variable is never written to, so it can be replaced with a constant, IF the value is a constant - errors.info("variable is never written to and was replaced by a constant", decl.position) + errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position) val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position) return listOf( IAstModification.ReplaceNode(decl, const, parent), @@ -94,7 +94,7 @@ class VarConstantValueTypeAdjuster( ) } // variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant - errors.info("variable is never written to and was replaced by a constant", decl.position) + errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position) val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position) return listOf( IAstModification.ReplaceNode(decl, const, parent), @@ -103,15 +103,15 @@ class VarConstantValueTypeAdjuster( } } /* - TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value) - if(writes.size==2) { - val firstAssignment = writes[0].parent as? Assignment - val secondAssignment = writes[1].parent as? Assignment - if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) { - errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position) + TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value) + if(writes.size==2) { + val firstAssignment = writes[0].parent as? Assignment + val secondAssignment = writes[1].parent as? Assignment + if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) { + errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position) + } } - } - */ + */ } return noModifications diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index f507dc420..ab9f02c99 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1264,7 +1264,14 @@ internal class AstChecker(private val program: Program, if(funcName[0]=="setlsb" || funcName[0]=="setmsb") { val firstArg = functionCallStatement.args[0] if(firstArg !is IdentifierReference && firstArg !is ArrayIndexedExpression) - errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position) + errors.err("invalid argument to a in-place modifying function", firstArg.position) + } else if(funcName[0]=="divmod" || funcName[0].startsWith("divmod__")) { + val thirdArg = functionCallStatement.args[2] + val fourthArg = functionCallStatement.args[3] + if(thirdArg !is IdentifierReference && thirdArg !is ArrayIndexedExpression) + errors.err("invalid argument to a in-place modifying function", thirdArg.position) + if(fourthArg !is IdentifierReference && fourthArg !is ArrayIndexedExpression) + errors.err("invalid argument to a in-place modifying function", fourthArg.position) } else { if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position) diff --git a/compiler/test/TestBuiltinFunctions.kt b/compiler/test/TestBuiltinFunctions.kt index e1fe1091f..a2d6e4116 100644 --- a/compiler/test/TestBuiltinFunctions.kt +++ b/compiler/test/TestBuiltinFunctions.kt @@ -2,6 +2,7 @@ package prog8tests import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import prog8.ast.expressions.NumericLiteral import prog8.ast.statements.Assignment import prog8.ast.statements.FunctionCallStatement @@ -98,5 +99,19 @@ main { (a4.value as NumericLiteral).number shouldBe 200*256+100 (a5.args[0] as NumericLiteral).number shouldBe 6.0 } + + test("divmod target args should be treated as variables that are written") { + val src=""" +main { + ubyte c + ubyte l + + sub start() { + divmod(99, 10, c, l) + } +}""" + + compileText(Cx16Target(), true, src, writeAssembly = true) shouldNotBe null + } }) diff --git a/docs/source/programming.rst b/docs/source/programming.rst index ca37b2a16..9e0a53f5d 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -832,7 +832,7 @@ divmod (dividend, divisor, quotient, remainder) Performs division only once and returns both quotient and remainder in a single call, where using '/' and '%' separately would perform the division operation twice. All values are ubytes or all are uwords. - The last two arguments must be ubyte variables to receive the quotient and remainder results, respectively. + The last two arguments must be variables to receive the quotient and remainder results, respectively. Array operations diff --git a/examples/test.p8 b/examples/test.p8 index 916a98edf..edd51e50e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,19 +1,15 @@ -%import diskio +; test_divmode.p8 +%import textio + %zeropage basicsafe %option no_sysinit + main { + ubyte c + ubyte l + sub start() { - bool @shared flag - - cx16.r0L = test(12345, flag, -42) - - } - - asmsub test(uword arg @AY, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc { - %asm {{ - txa - rts - }} + divmod(99, 10, c, l) } }