diff --git a/README.md b/README.md index ab4dc10aa..a5496e3dd 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ which aims to provide many conveniences over raw assembly code (even when using - abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses - various code optimizations (code structure, logical and numerical expressions, unused code removal...) - inline assembly allows you to have full control when every cycle or byte matters -- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy`` +- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse`` Rapid edit-compile-run-debug cycle: diff --git a/compiler/res/prog8lib/prog8lib.asm b/compiler/res/prog8lib/prog8lib.asm index d7016acce..a7fe56890 100644 --- a/compiler/res/prog8lib/prog8lib.asm +++ b/compiler/res/prog8lib/prog8lib.asm @@ -1535,14 +1535,15 @@ _sort_loop ldy c64.SCRATCH_ZPB1 ;start of subroutine sort _l1 dey dey beq _l3 + lda (c64.SCRATCH_ZPWORD1),y + cmp c64.SCRATCH_ZPWORD2 iny lda (c64.SCRATCH_ZPWORD1),y dey - cmp c64.SCRATCH_ZPWORD2+1 - bne + - lda (c64.SCRATCH_ZPWORD1),y - cmp c64.SCRATCH_ZPWORD2 -+ bmi _l1 + sbc c64.SCRATCH_ZPWORD2+1 + bvc + + eor #$80 ++ bmi _l1 _l2 sty _work1 ;index of potentially largest value lda (c64.SCRATCH_ZPWORD1),y sta c64.SCRATCH_ZPWORD2 ;potentially largest value @@ -1571,3 +1572,102 @@ _l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put _work1 .byte 0 _work3 .word 0 .pend + + +reverse_b .proc + ; --- reverse an array of bytes (in-place) + ; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A +_left_index = c64.SCRATCH_ZPWORD2 +_right_index = c64.SCRATCH_ZPWORD2+1 + pha + sec + sbc #1 + sta _left_index + lda #0 + sta _right_index + pla + lsr a + tay +_loop sty c64.SCRATCH_ZPREG + ldy _left_index + lda (c64.SCRATCH_ZPWORD1),y + pha + ldy _right_index + lda (c64.SCRATCH_ZPWORD1),y + ldy _left_index + sta (c64.SCRATCH_ZPWORD1),y + pla + ldy _right_index + sta (c64.SCRATCH_ZPWORD1),y + inc _right_index + dec _left_index + ldy c64.SCRATCH_ZPREG + dey + bne _loop + rts + .pend + + +reverse_w .proc + ; --- reverse an array of words (in-place) + ; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A +_left_index = c64.SCRATCH_ZPWORD2 +_right_index = c64.SCRATCH_ZPWORD2+1 + pha + asl a ; *2 because words + sec + sbc #2 + sta _left_index + lda #0 + sta _right_index + pla + lsr a + pha + tay + ; first reverse the lsbs +_loop_lo sty c64.SCRATCH_ZPREG + ldy _left_index + lda (c64.SCRATCH_ZPWORD1),y + pha + ldy _right_index + lda (c64.SCRATCH_ZPWORD1),y + ldy _left_index + sta (c64.SCRATCH_ZPWORD1),y + pla + ldy _right_index + sta (c64.SCRATCH_ZPWORD1),y + inc _right_index + inc _right_index + dec _left_index + dec _left_index + ldy c64.SCRATCH_ZPREG + dey + bne _loop_lo + ; now reverse the msbs + dec _right_index + inc _left_index + inc _left_index + inc _left_index + pla + tay +_loop_hi sty c64.SCRATCH_ZPREG + ldy _left_index + lda (c64.SCRATCH_ZPWORD1),y + pha + ldy _right_index + lda (c64.SCRATCH_ZPWORD1),y + ldy _left_index + sta (c64.SCRATCH_ZPWORD1),y + pla + ldy _right_index + sta (c64.SCRATCH_ZPWORD1),y + dec _right_index + dec _right_index + inc _left_index + inc _left_index + ldy c64.SCRATCH_ZPREG + dey + bne _loop_hi + + rts + .pend diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 8fd01b731..7dc4e231c 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -832,7 +832,7 @@ internal class AstChecker(private val program: Program, printWarning("result values of subroutine call are discarded", functionCallStatement.position) } - if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort")) { + if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) { // in-place modification, can't be done on literals if(functionCallStatement.arglist.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) { checkResult.add(ExpressionError("can't use that as argument to a in-place modifying function", functionCallStatement.position)) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 2ce4ae7a7..115141aa8 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -336,6 +336,38 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val else throw AssemblyError("weird type") } + "reverse" -> { + val variable = fcall.arglist.single() + if (variable is IdentifierReference) { + val decl = variable.targetVarDecl(program.namespace)!! + val varName = asmgen.asmIdentifierName(variable) + val numElements = decl.arraysize!!.size() + when (decl.datatype) { + DataType.ARRAY_UB, DataType.ARRAY_B -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1 + 1} + lda #$numElements + jsr prog8_lib.reverse_b + """) + } + DataType.ARRAY_UW, DataType.ARRAY_W -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1 + 1} + lda #$numElements + jsr prog8_lib.reverse_w + """) + } + DataType.ARRAY_F -> TODO("reverse floats (consider another solution if possible - this will be quite slow, if ever implemented)") + else -> throw AssemblyError("weird type") + } + } + } else -> { translateFunctionArguments(fcall.arglist, func) asmgen.out(" jsr prog8_lib.func_$functionName") diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index a5bda744d..adb96138d 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -28,6 +28,7 @@ val BuiltinFunctions = mapOf( "lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null), "lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null), "sort" to FunctionSignature(false, listOf(BuiltinFunctionParam("array", ArrayDatatypes)), null), + "reverse" to FunctionSignature(false, listOf(BuiltinFunctionParam("array", ArrayDatatypes)), null), // these few have a return value depending on the argument(s): "max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args "min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args diff --git a/docs/source/index.rst b/docs/source/index.rst index 9f78d7505..1fcb2557c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -158,7 +158,7 @@ Design principles and features - The compiler tries to optimize the program and generated code, but hand-tuning of the performance or space-critical parts will likely still be required. This is supported by the ability to easily write embedded assembly code directly in the program source code. -- There are many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy`` +- There are many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse`` .. _requirements: diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 7e5fcc5a0..f2a80bfc4 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -663,52 +663,58 @@ cos16u(x) Fast 16-bit uword cosine of angle 0..255, result is in range 0..65535 cos16(x) - Fast 16-bit word cosine of angle 0..255, result is in range -32767..32767 + Fast 16-bit word cosine of angle 0..255, result is in range -32767..32767 abs(x) - Absolute value. + Absolute value. tan(x) - Tangent. + Tangent. atan(x) - Arctangent. + Arctangent. ln(x) - Natural logarithm (base e). + Natural logarithm (base e). log2(x) Base 2 logarithm. sqrt16(w) - 16 bit unsigned integer Square root. Result is unsigned byte. + 16 bit unsigned integer Square root. Result is unsigned byte. sqrt(x) - Floating point Square root. + Floating point Square root. round(x) - Rounds the floating point to the closest integer. + Rounds the floating point to the closest integer. floor (x) - Rounds the floating point down to an integer towards minus infinity. + Rounds the floating point down to an integer towards minus infinity. ceil(x) - Rounds the floating point up to an integer towards positive infinity. + Rounds the floating point up to an integer towards positive infinity. rad(x) - Degrees to radians. + Degrees to radians. deg(x) - Radians to degrees. + Radians to degrees. max(x) - Maximum of the values in the array value x + Maximum of the values in the array value x min(x) - Minimum of the values in the array value x + Minimum of the values in the array value x sum(x) - Sum of the values in the array value x + Sum of the values in the array value x + +sort(array) + Sort the array in ascending order (in-place) + +reverse(array) + Reverse the values in the array (in-place). Can be used after sort() to sort an array in descending order. len(x) Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte). diff --git a/examples/cube3d-sprites.p8 b/examples/cube3d-sprites.p8 index 7e16e82d1..dea985142 100644 --- a/examples/cube3d-sprites.p8 +++ b/examples/cube3d-sprites.p8 @@ -135,18 +135,16 @@ main { ; first sort vertices to sprite order so the back/front order is correct as well ; (simple bubble sort as it's only 8 items to sort) - ; TODO make a builtin function sort() - sort(rotatedz) -; for ubyte sorti in 6 to 0 step -1 { -; for ubyte i1 in 0 to sorti { -; ubyte i2 = i1+1 -; if(rotatedz[i1] > rotatedz[i2]) { -; swap(rotatedx[i1], rotatedx[i2]) -; swap(rotatedy[i1], rotatedy[i2]) -; swap(rotatedz[i1], rotatedz[i2]) -; } -; } -; } + for ubyte sorti in 6 to 0 step -1 { + for ubyte i1 in 0 to sorti { + ubyte i2 = i1+1 + if(rotatedz[i1] > rotatedz[i2]) { + swap(rotatedx[i1], rotatedx[i2]) + swap(rotatedy[i1], rotatedy[i2]) + swap(rotatedz[i1], rotatedz[i2]) + } + } + } ubyte[] spritecolors = [1,1,7,15,12,11,9,9] diff --git a/examples/sorting.p8 b/examples/sorting.p8 new file mode 100644 index 000000000..2ab86c038 --- /dev/null +++ b/examples/sorting.p8 @@ -0,0 +1,62 @@ +%import c64lib +%import c64utils +%zeropage basicsafe + +main { + + sub start() { + + ubyte[] uba = [10,0,2,8,5,4,3,9] + uword[] uwa = [1000,0,200,8000,50,40000,3,900] + byte[] ba = [-10,0,-2,8,5,4,-3,9,-99] + word[] wa = [-1000,0,-200,8000,50,31111,3,-900] + + c64scr.print("original\n") + print_arrays() + + sort(uba) + sort(uwa) + sort(ba) + sort(wa) + + c64scr.print("sorted\n") + print_arrays() + + reverse(uba) + reverse(uwa) + reverse(ba) + reverse(wa) + + c64scr.print("reversed\n") + print_arrays() + return + + + sub print_arrays() { + for ubyte ub in uba { + c64scr.print_ub(ub) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for uword uw in uwa { + c64scr.print_uw(uw) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for byte bb in ba { + c64scr.print_b(bb) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for word ww in wa { + c64scr.print_w(ww) + c64.CHROUT(',') + } + c64.CHROUT('\n') + c64.CHROUT('\n') + } + } +} diff --git a/examples/test.p8 b/examples/test.p8 index 8dc83daec..1363faaee 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,7 +1,6 @@ %import c64lib %import c64utils -%import c64flt -%zeropage dontuse +%zeropage basicsafe main { @@ -9,9 +8,8 @@ main { ubyte[] uba = [10,0,2,8,5,4,3,9] uword[] uwa = [1000,0,200,8000,50,40000,3,900] - byte[] ba = [-10,0,-2,8,5,4,-3,9] + byte[] ba = [-10,0,-2,8,5,4,-3,9,-99] word[] wa = [-1000,0,-200,8000,50,31111,3,-900] - float[] fla = [-2.2, 1.1, 3.3, 0.0] for ubyte ub in uba { c64scr.print_ub(ub) @@ -66,11 +64,41 @@ main { c64.CHROUT(',') } c64.CHROUT('\n') + c64.CHROUT('\n') + + reverse(uba) + reverse(uwa) + reverse(ba) + reverse(wa) + + for ubyte ub3 in uba { + c64scr.print_ub(ub3) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for uword uw3 in uwa { + c64scr.print_uw(uw3) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for byte bb3 in ba { + c64scr.print_b(bb3) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for word ww3 in wa { + c64scr.print_w(ww3) + c64.CHROUT(',') + } + c64.CHROUT('\n') + c64.CHROUT('\n') - ubyte qq=X - c64scr.print_ub(qq) ; TODO 2 for loops that both define the same loopvar -> double definition -> fix second for -> 'unknown symbol' ???? + ; TODO code runs into a subroutine that follows it instead of inserting an implicit return } }