abs() now works on multiple data types including float.

no need to use floats.fabs() anymore
This commit is contained in:
Irmen de Jong 2023-04-29 22:11:26 +02:00
parent 319079de7a
commit 38efe25c68
11 changed files with 134 additions and 88 deletions

View File

@ -79,7 +79,10 @@ val BuiltinFunctions: Map<String, FSignature> = 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),

View File

@ -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)
}
}

View File

@ -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")
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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
}
}
"""

View File

@ -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") {

View File

@ -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!

View File

@ -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.

View File

@ -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.....