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 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), "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), "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), "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow: // normal functions follow:
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE), "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) "mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(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) "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) "any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
"sqrt" -> funcSqrt(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?) { private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
require(fcall.type != DataType.FLOAT)
translateArguments(fcall, scope) translateArguments(fcall, scope)
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack") 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 val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt) { when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack") DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
DataType.UWORD -> {}
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack") 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 { } else {
when (dt) { when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0") DataType.BYTE -> {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY") asmgen.out(" jsr prog8_lib.abs_b_into_A")
DataType.UWORD -> {} 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") }
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") 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) { return when(call.name) {
"any" -> funcAny(call) "any" -> funcAny(call)
"all" -> funcAll(call) "all" -> funcAll(call)
"abs" -> funcAbs(call) "abs__byte", "abs__word", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call) "cmp" -> funcCmp(call)
"sgn" -> funcSgn(call) "sgn" -> funcSgn(call)
"sqrt" -> funcSqrt(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]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
when (sourceDt) { 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 -> { DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.nextFree()
@ -196,7 +190,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1) 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)) 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 return noModifications
} }
} }

View File

@ -144,3 +144,11 @@ func_all_f_stack .proc
jsr a_times_5 jsr a_times_5
jmp prog8_lib.func_all_b_stack jmp prog8_lib.func_all_b_stack
.pend .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 abs_b_stack .proc
; -- push abs(A) on stack (as unsigned word) ; -- push abs(A) on stack (as unsigned word)
jsr abs_b_into_AY jsr abs_b_into_A
sta P8ESTACK_LO,x sta P8ESTACK_LO,x
stz P8ESTACK_HI,x stz P8ESTACK_HI,x
dex dex
rts rts
.pend .pend
abs_b_into_AY .proc abs_b_into_A .proc
; -- AY = abs(A) (abs always returns unsigned word) ; -- A = abs(A)
ldy #0
cmp #0 cmp #0
bmi + bmi +
rts rts

View File

@ -532,7 +532,7 @@ class TestOptimization: FunSpec({
uword @shared zz uword @shared zz
zz += 60 ; NOT ok to remove initializer, should evaluate to 60 zz += 60 ; NOT ok to remove initializer, should evaluate to 60
ubyte @shared xx 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 { main {
sub start() { sub start() {
float fl float fl
floats.print_f(abs(fl)) floats.print_f(lsb(fl))
} }
}""" }"""
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors) val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors)
result shouldBe null result shouldBe null
errors.errors.size shouldBe 1 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") { test("not casting bool operands to logical operators") {

View File

@ -225,61 +225,65 @@ floats
Floating point support is only available on c64, cx16 and virtual targets for now. 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 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, 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.
atan(x) ``atan (x)``
Arctangent. Arctangent.
ceil(x) ``ceil (x)``
Rounds the floating point up to an integer towards positive infinity. Rounds the floating point up to an integer towards positive infinity.
cos(x) ``cos (x)``
Cosine. Cosine.
If you want a fast integer cosine, have a look at examples/cx16/sincos.p8 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. that contains various lookup tables generated by the 64tass assembler.
deg(x) ``deg (x)``
Radians to degrees. 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. Rounds the floating point down to an integer towards minus infinity.
ln(x) ``ln (x)``
Natural logarithm (base e). Natural logarithm (base e).
log2(x) ``log2 (x)``
Base 2 logarithm. Base 2 logarithm.
minf(x, y) ``minf (x, y)``
returns the smallest of x and y. returns the smallest of x and y.
maxf(x, y) ``maxf (x, y)``
returns the largest of x and 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. Degrees to radians.
round(x) ``round (x)``
Rounds the floating point to the closest integer. Rounds the floating point to the closest integer.
sin(x) ``sin (x)``
Sine. Sine.
If you want a fast integer sine, have a look at examples/cx16/sincos.p8 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. that contains various lookup tables generated by the 64tass assembler.
sqrtf(x) ``sqrtf (x)``
Floating point Square root. Floating point Square root.
To do the reverse, squaring a floating point number, just write ``x*x``. To do the reverse, squaring a floating point number, just write ``x*x``.
tan(x) ``tan (x)``
Tangent. Tangent.
rndf() ``rndf ()``
returns the next random float between 0.0 and 1.0 from the Pseudo RNG sequence. 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. 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. but perhaps the provided ones can be of service too.
rnd() ``rnd ()``
Returns next random byte 0-255 from the pseudo-RNG sequence. Returns next random byte 0-255 from the pseudo-RNG sequence.
rndw() ``rndw ()``
Returns next random word 0-65535 from the pseudo-RNG sequence. 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. 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! 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 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 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 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! 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 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! 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 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 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 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! 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 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! Angles 180..255 will yield a garbage result!

View File

@ -762,8 +762,7 @@ Math
^^^^ ^^^^
abs (x) abs (x)
Absolute value of an integer. Value returned is an unsigned word. Returns the absolute value of a number.
For floating point numbers, use ``floats.fabs()`` instead.
min (x, y) min (x, y)
Returns the smallest of x and y. Supported for integer types only, for floats use ``floats.minf()`` instead. 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 min() max() builtin functions
- DONE: added 'cbm' block in the syslib module that now contains all CBM compatible kernal routines and variables - 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() - 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. - 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! 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..... Lot of work because of so many special cases in ForLoopsAsmgen.....