From 38efe25c686502c82cf745edb8b2888aa02486dd Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 29 Apr 2023 22:11:26 +0200 Subject: [PATCH] abs() now works on multiple data types including float. no need to use floats.fabs() anymore --- .../src/prog8/code/core/BuiltinFunctions.kt | 5 +- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 30 +++-- .../codegen/intermediate/BuiltinFuncGen.kt | 15 +-- .../optimizer/ConstantIdentifierReplacer.kt | 21 +++ compiler/res/prog8lib/c64/floats_funcs.asm | 8 ++ compiler/res/prog8lib/prog8_funcs.asm | 7 +- compiler/test/TestOptimization.kt | 2 +- compiler/test/TestTypecasts.kt | 4 +- docs/source/libraries.rst | 124 +++++++++--------- docs/source/programming.rst | 3 +- docs/source/todo.rst | 3 +- 11 files changed, 134 insertions(+), 88 deletions(-) diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index 380fef668..a11f1250f 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -79,7 +79,10 @@ val BuiltinFunctions: Map = mapOf( // cmp returns a status in the carry flag, but not a proper return value "cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), "prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), - "abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD), + "abs" to FSignature(true, listOf(), null), + "abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE), + "abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD), + "abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT), "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), // normal functions follow: "sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE), diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 601ff6541..35fb36f8c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -33,7 +33,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, "mkword" -> funcMkword(fcall, resultToStack, resultRegister) "min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultToStack, resultRegister) "max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultToStack, resultRegister) - "abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope) + "abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultToStack, resultRegister, sscope) "any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope) "sqrt" -> funcSqrt(fcall, resultToStack, resultRegister, sscope) @@ -301,6 +301,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { + require(fcall.type != DataType.FLOAT) translateArguments(fcall, scope) if(resultToStack) asmgen.out(" jsr prog8_lib.func_sqrt16_stack") @@ -679,21 +680,32 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, val dt = fcall.args.single().type if(resultToStack) { when (dt) { - DataType.UBYTE -> asmgen.out(" ldy #0") DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack") - DataType.UWORD -> {} DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack") - else -> throw AssemblyError("weird type") + else -> throw AssemblyError("no support for abs onto stack for this dt") } } else { when (dt) { - DataType.UBYTE -> asmgen.out(" ldy #0") - DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY") - DataType.UWORD -> {} - DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY") + DataType.BYTE -> { + asmgen.out(" jsr prog8_lib.abs_b_into_A") + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false) + } + DataType.WORD -> { + asmgen.out(" jsr prog8_lib.abs_w_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY) + } + DataType.FLOAT -> { + asmgen.out(" jsr floats.func_abs_f_into_FAC1") + assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen)) + } + DataType.UBYTE -> { + asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen)) + } + DataType.UWORD -> { + asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen)) + } else -> throw AssemblyError("weird type") } - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY) } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index b35c1966f..547fafdbf 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -14,7 +14,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe return when(call.name) { "any" -> funcAny(call) "all" -> funcAll(call) - "abs" -> funcAbs(call) + "abs__byte", "abs__word", "abs__float" -> funcAbs(call) "cmp" -> funcCmp(call) "sgn" -> funcSgn(call) "sqrt" -> funcSqrt(call) @@ -165,12 +165,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe val tr = exprGen.translateExpression(call.args[0]) addToResult(result, tr, tr.resultReg, -1) when (sourceDt) { - DataType.UBYTE -> { - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = tr.resultReg) - } - return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) - } DataType.BYTE -> { val notNegativeLabel = codeGen.createLabelName() val compareReg = codeGen.registers.nextFree() @@ -196,7 +190,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe result += IRCodeChunk(notNegativeLabel, null) return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1) } - else -> throw AssemblyError("weird type") + DataType.FLOAT -> { + val resultFpReg = codeGen.registers.nextFreeFloat() + addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null) + return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg) + } + else -> throw AssemblyError("weird dt") } } diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt index f2419f796..b2e3c8edb 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -100,6 +100,27 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err functionCallExpr)) } } + else if(func==listOf("abs")) { + val t1 = functionCallExpr.args[0].inferType(program) + if(t1.isKnown) { + val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") } + val replaceFunc = when(dt) { + DataType.BYTE -> "abs__byte" + DataType.WORD -> "abs__word" + DataType.FLOAT -> "abs__float" + DataType.UBYTE, DataType.UWORD -> { + return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent)) + } + else -> { + errors.err("expected numeric argument", functionCallExpr.position) + return noModifications + } + } + return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference}, + IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position), + functionCallExpr)) + } + } return noModifications } } diff --git a/compiler/res/prog8lib/c64/floats_funcs.asm b/compiler/res/prog8lib/c64/floats_funcs.asm index 93e58f7de..3ec94bf11 100644 --- a/compiler/res/prog8lib/c64/floats_funcs.asm +++ b/compiler/res/prog8lib/c64/floats_funcs.asm @@ -144,3 +144,11 @@ func_all_f_stack .proc jsr a_times_5 jmp prog8_lib.func_all_b_stack .pend + +func_abs_f_into_FAC1 .proc + stx P8ZP_SCRATCH_REG + jsr MOVFM + jsr ABS + ldx P8ZP_SCRATCH_REG + rts + .pend diff --git a/compiler/res/prog8lib/prog8_funcs.asm b/compiler/res/prog8lib/prog8_funcs.asm index 5728d0bc1..688c3b46b 100644 --- a/compiler/res/prog8lib/prog8_funcs.asm +++ b/compiler/res/prog8lib/prog8_funcs.asm @@ -86,16 +86,15 @@ func_all_w_stack .proc abs_b_stack .proc ; -- push abs(A) on stack (as unsigned word) - jsr abs_b_into_AY + jsr abs_b_into_A sta P8ESTACK_LO,x stz P8ESTACK_HI,x dex rts .pend -abs_b_into_AY .proc - ; -- AY = abs(A) (abs always returns unsigned word) - ldy #0 +abs_b_into_A .proc + ; -- A = abs(A) cmp #0 bmi + rts diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 6df9b8ee7..31b97659e 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -532,7 +532,7 @@ class TestOptimization: FunSpec({ uword @shared zz zz += 60 ; NOT ok to remove initializer, should evaluate to 60 ubyte @shared xx - xx = 6+lsb(abs(xx)) ; is not an initializer because it references xx + xx = 6+lsb(mkword(xx,22)) ; is not an initializer because it references xx } } """ diff --git a/compiler/test/TestTypecasts.kt b/compiler/test/TestTypecasts.kt index 4db763268..4e17b8f8c 100644 --- a/compiler/test/TestTypecasts.kt +++ b/compiler/test/TestTypecasts.kt @@ -27,14 +27,14 @@ class TestTypecasts: FunSpec({ main { sub start() { float fl - floats.print_f(abs(fl)) + floats.print_f(lsb(fl)) } }""" val errors = ErrorReporterForTests() val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors) result shouldBe null errors.errors.size shouldBe 1 - errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UBYTE, BYTE, UWORD, WORD]" + errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UWORD, WORD]" } test("not casting bool operands to logical operators") { diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 7f4359827..80b2ceac2 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -50,11 +50,11 @@ sys (part of syslib) - 16 = compiled for Commander X16 with 65C02 CPU - 64 = compiled for Commodore 64 with 6502/6510 CPU -``exit(returncode)`` +``exit (returncode)`` Immediately stops the program and exits it, with the returncode in the A register. Note: custom interrupt handlers remain active unless manually cleared first! -``memcopy(from, to, numbytes)`` +``memcopy (from, to, numbytes)`` Efficiently copy a number of bytes from a memory location to another. *Warning:* can only copy *non-overlapping* memory areas correctly! Because this function imposes some overhead to handle the parameters, @@ -62,60 +62,60 @@ sys (part of syslib) Compare the generated code to see if it was beneficial or not. The most efficient will often be to write a specialized copy routine in assembly yourself! -``memset(address, numbytes, bytevalue)`` +``memset (address, numbytes, bytevalue)`` Efficiently set a part of memory to the given (u)byte value. But the most efficient will always be to write a specialized fill routine in assembly yourself! Note that for clearing the screen, very fast specialized subroutines are available in the ``textio`` and ``graphics`` library modules. -``memsetw(address, numwords, wordvalue)`` +``memsetw (address, numwords, wordvalue)`` Efficiently set a part of memory to the given (u)word value. But the most efficient will always be to write a specialized fill routine in assembly yourself! -``read_flags() -> ubyte`` +``read_flags () -> ubyte`` Returns the current value of the CPU status register. -``set_carry()`` +``set_carry ()`` Sets the CPU status register Carry flag. -``clear_carry()`` +``clear_carry ()`` Clears the CPU status register Carry flag. -``set_irqd()`` +``set_irqd ()`` Sets the CPU status register Interrupt Disable flag. -``clear_irqd()`` +``clear_irqd ()`` Clears the CPU status register Interrupt Disable flag. -``progend()`` +``progend ()`` Returns the last address of the program in memory + 1. Can be used to load dynamic data after the program, instead of hardcoding something. -``wait(uword jiffies)`` +``wait (uword jiffies)`` wait approximately the given number of jiffies (1/60th seconds) Note: the regular system irq handler has run for this to work as it depends on the system jiffy clock. If this is is not possible (for instance because your program is running its own irq handler logic *and* no longer calls the kernal's handler routine), you'll have to write your own wait routine instead. -``waitvsync()`` +``waitvsync ()`` busy wait till the next vsync has occurred (approximately), without depending on custom irq handling. can be used to avoid screen flicker/tearing when updating screen contents. note: a more accurate way to wait for vsync is to set up a vsync irq handler instead. note for cx16: the regular system irq handler has to run for this to work (this is not required on C64 and C128) -``waitrastborder()`` (c64/c128 targets only) +``waitrastborder ()`` (c64/c128 targets only) busy wait till the raster position has reached the bottom screen border (approximately) can be used to avoid screen flicker/tearing when updating screen contents. note: a more accurate way to do this is by using a raster irq handler instead. -``reset_system()`` +``reset_system ()`` Soft-reset the system back to initial power-on BASIC prompt. (called automatically by Prog8 when the main subroutine returns and the program is not using basicsafe zeropage option) -``poweroff_system()`` (commander x16 only) +``poweroff_system ()`` (commander x16 only) Powers down the computer. -``set_leds_brightness(ubyte activity, ubyte power)`` (commander x16 only) +``set_leds_brightness (ubyte activity, ubyte power)`` (commander x16 only) Sets the brightness of the activity and power leds on the computer. @@ -157,63 +157,63 @@ string ------ Provides string manipulation routines. -``length(str) -> ubyte length`` +``length (str) -> ubyte length`` Number of bytes in the string. This value is determined during runtime and counts upto the first terminating 0 byte in the string, regardless of the size of the string during compilation time. Don't confuse this with ``len`` and ``sizeof``! -``left(source, length, target)`` +``left (source, length, target)`` Copies the left side of the source string of the given length to target string. It is assumed the target string buffer is large enough to contain the result. Also, you have to make sure yourself that length is smaller or equal to the length of the source string. Modifies in-place, doesn't return a value (so can't be used in an expression). -``right(source, length, target)`` +``right (source, length, target)`` Copies the right side of the source string of the given length to target string. It is assumed the target string buffer is large enough to contain the result. Also, you have to make sure yourself that length is smaller or equal to the length of the source string. Modifies in-place, doesn't return a value (so can't be used in an expression). -``slice(source, start, length, target)`` +``slice (source, start, length, target)`` Copies a segment from the source string, starting at the given index, and of the given length to target string. It is assumed the target string buffer is large enough to contain the result. Also, you have to make sure yourself that start and length are within bounds of the strings. Modifies in-place, doesn't return a value (so can't be used in an expression). -``find(string, char) -> ubyte index + carry bit`` +``find (string, char) -> ubyte index + carry bit`` Locates the first position of the given character in the string, returns carry bit set if found and the index in the string. Or 0+carry bit clear if the character was not found. -``compare(string1, string2) -> ubyte result`` +``compare (string1, string2) -> ubyte result`` Returns -1, 0 or 1 depending on whether string1 sorts before, equal or after string2. Note that you can also directly compare strings and string values with each other using ``==``, ``<`` etcetera (it will use string.compare for you under water automatically). -``copy(from, to) -> ubyte length`` +``copy (from, to) -> ubyte length`` Copy a string to another, overwriting that one. Returns the length of the string that was copied. Often you don't have to call this explicitly and can just write ``string1 = string2`` but this function is useful if you're dealing with addresses for instance. -``lower(string)`` +``lower (string)`` Lowercases the PETSCII-string in place. -``upper(string)`` +``upper (string)`` Uppercases the PETSCII-string in place. -``lowerchar(char)`` +``lowerchar (char)`` Returns lowercased character. -``upperchar(char)`` +``upperchar (char)`` Returns uppercased character. -``startswith(string, prefix) -> bool`` +``startswith (string, prefix) -> bool`` Returns true if string starts with prefix, otherwise false -``endswith(string, suffix) -> bool`` +``endswith (string, suffix) -> bool`` Returns true if string ends with suffix, otherwise false -``pattern_match(string, pattern) -> ubyte`` (not on Virtual target) +``pattern_match (string, pattern) -> ubyte`` (not on Virtual target) Returns 1 (true) if the string matches the pattern, 0 (false) if not. '?' in the pattern matches any one character. '*' in the pattern matches any substring. @@ -225,61 +225,65 @@ floats Floating point support is only available on c64, cx16 and virtual targets for now. Provides definitions for the ROM/Kernal subroutines and utility routines dealing with floating -point variables. This includes ``print_f``, the routine used to print floating point numbers, -``fabs`` to get the absolute value of a floating point number, and a dozen or so floating point -math routines. +point variables. This includes ``print_f``, the routine used to print floating point numbers. -atan(x) +``atan (x)`` Arctangent. -ceil(x) +``ceil (x)`` Rounds the floating point up to an integer towards positive infinity. -cos(x) +``cos (x)`` Cosine. If you want a fast integer cosine, have a look at examples/cx16/sincos.p8 that contains various lookup tables generated by the 64tass assembler. -deg(x) +``deg (x)`` Radians to degrees. -floor (x) +``fabs (x)`` + Returns the absolute value of x. Deprecated, just use the builtin ``abs(x)`` function instead. + +``floor (x)`` Rounds the floating point down to an integer towards minus infinity. -ln(x) +``ln (x)`` Natural logarithm (base e). -log2(x) +``log2 (x)`` Base 2 logarithm. -minf(x, y) +``minf (x, y)`` returns the smallest of x and y. -maxf(x, y) +``maxf (x, y)`` returns the largest of x and y. -rad(x) +``print_f (x)`` + prints the floating point number x as a string. + +``rad (x)`` Degrees to radians. -round(x) +``round (x)`` Rounds the floating point to the closest integer. -sin(x) +``sin (x)`` Sine. If you want a fast integer sine, have a look at examples/cx16/sincos.p8 that contains various lookup tables generated by the 64tass assembler. -sqrtf(x) +``sqrtf (x)`` Floating point Square root. To do the reverse, squaring a floating point number, just write ``x*x``. -tan(x) +``tan (x)`` Tangent. -rndf() +``rndf ()`` returns the next random float between 0.0 and 1.0 from the Pseudo RNG sequence. -rndseedf(seed) +``rndseedf (seed)`` Sets a new seed for the float pseudo-RNG sequence. Use a negative non-zero number as seed value. @@ -306,41 +310,41 @@ Usually a custom lookup table is the way to go if your application needs these, but perhaps the provided ones can be of service too. -rnd() +``rnd ()`` Returns next random byte 0-255 from the pseudo-RNG sequence. -rndw() +``rndw ()`` Returns next random word 0-65535 from the pseudo-RNG sequence. -rndseed(uword seed1, uword seed2) +``rndseed (uword seed1, uword seed2)`` Sets a new seed for the pseudo-RNG sequence (both rnd and rndw). The seed consists of two words. Do not use zeros for the seed! -sin8u(x) +``sin8u (x)`` Fast 8-bit ubyte sine of angle 0..255, result is in range 0..255 -sin8(x) +``sin8 (x)`` Fast 8-bit byte sine of angle 0..255, result is in range -127..127 -sinr8u(x) +``sinr8u (x)`` Fast 8-bit ubyte sine of angle 0..179 (each is a 2 degree step), result is in range 0..255 Angles 180..255 will yield a garbage result! -sinr8(x) +``sinr8 (x)`` Fast 8-bit byte sine of angle 0..179 (each is a 2 degree step), result is in range -127..127 Angles 180..255 will yield a garbage result! -cos8u(x) +``cos8u (x)`` Fast 8-bit ubyte cosine of angle 0..255, result is in range 0..255 -cos8(x) +``cos8 (x)`` Fast 8-bit byte cosine of angle 0..255, result is in range -127..127 -cosr8u(x) +``cosr8u (x)`` Fast 8-bit ubyte cosine of angle 0..179 (each is a 2 degree step), result is in range 0..255 Angles 180..255 will yield a garbage result! -cosr8(x) +``cosr8 (x)`` Fast 8-bit byte cosine of angle 0..179 (each is a 2 degree step), result is in range -127..127 Angles 180..255 will yield a garbage result! diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 5b952a8bd..f9ef74c82 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -762,8 +762,7 @@ Math ^^^^ abs (x) - Absolute value of an integer. Value returned is an unsigned word. - For floating point numbers, use ``floats.fabs()`` instead. + Returns the absolute value of a number. min (x, y) Returns the smallest of x and y. Supported for integer types only, for floats use ``floats.minf()`` instead. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 533dc9f43..a9ecb34d6 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -6,7 +6,8 @@ For 9.0 major changes - DONE: added min() max() builtin functions - DONE: added 'cbm' block in the syslib module that now contains all CBM compatible kernal routines and variables - DONE: rename sqrt16() to just sqrt(), rename floats.sqrt() to floats.sqrtf() -- add "polymorphism" of min() and max() to several other builtin functions as well (abs, divmod, pop, push) Fix docs. +- DONE: abs() now supports multiple datatypes including float. No need to use floats.fabs() anymore. +- add "polymorphism" of min() and max() to several other builtin functions as well (divmod, pop, push) Fix docs. - 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition. It is documented behavior to now loop 'around' $00 but it's too easy to forget about! Lot of work because of so many special cases in ForLoopsAsmgen.....