diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index 75bd6a4a7..95547eb26 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -120,6 +120,7 @@ val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD) val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD) val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT) val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT) +val IntegerArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W) val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F) val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD) val IterableDatatypes = arrayOf( diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index c3590821d..949cf3ad6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -947,7 +947,7 @@ $repeatLabel lda $counterVar assemblyLines.add(assembly) } - internal fun returnRegisterOfFunction(it: IdentifierReference, argumentTypesForBuiltinFunc: List?): RegisterOrPair { + internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair { return when (val targetRoutine = it.targetStatement(program)!!) { is BuiltinFunctionPlaceholder -> { val func = BuiltinFunctions.getValue(targetRoutine.name) @@ -956,16 +956,11 @@ $repeatLabel lda $counterVar in WordDatatypes -> RegisterOrPair.AY DataType.FLOAT -> RegisterOrPair.FAC1 else -> { - if(!func.hasReturn) - throw AssemblyError("func has no returntype") - else { - val args = argumentTypesForBuiltinFunc!!.map { defaultZero(it, Position.DUMMY) } - when(builtinFunctionReturnType(func.name, args, program).getOrElse { DataType.UNDEFINED }) { - in ByteDatatypes -> RegisterOrPair.A - in WordDatatypes -> RegisterOrPair.AY - DataType.FLOAT -> RegisterOrPair.FAC1 - else -> throw AssemblyError("weird returntype") - } + when(builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED }) { + in ByteDatatypes -> RegisterOrPair.A + in WordDatatypes -> RegisterOrPair.AY + DataType.FLOAT -> RegisterOrPair.FAC1 + else -> throw AssemblyError("weird returntype") } } } @@ -2827,7 +2822,7 @@ $repeatLabel lda $counterVar var valueDt = source.inferType(program).getOrElse { throw FatalAstException("invalid dt") } var valueSource: AsmAssignSource = if(source is IFunctionCall) { - val resultReg = returnRegisterOfFunction(source.target, listOf(valueDt)) + val resultReg = returnRegisterOfFunction(source.target) assignExpressionToRegister(source, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)) AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg) } else { @@ -2839,7 +2834,7 @@ $repeatLabel lda $counterVar segments.dropLast(1).forEach { it as IFunctionCall valueDt = translateUnaryFunctionCallWithArgSource(it.target, valueSource, false, subroutine) - val resultReg = returnRegisterOfFunction(it.target, listOf(valueDt)) + val resultReg = returnRegisterOfFunction(it.target) valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg) } // the last segment: unary function call taking a single param and optionally producing a result value. diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index e3837f0f5..55c4616e4 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -63,7 +63,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, return if(isStatement) { DataType.UNDEFINED } else { - builtinFunctionReturnType(func.name, argExpressions, program).getOrElse { throw AssemblyError("unknown dt") } + builtinFunctionReturnType(func.name).getOrElse { throw AssemblyError("unknown dt") } } } @@ -82,8 +82,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, "mkword" -> funcMkword(fcall, resultToStack, resultRegister) "abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope) "swap" -> funcSwap(fcall) - "min", "max" -> funcMinMax(fcall, func, resultToStack, resultRegister, sscope) - "sum" -> funcSum(fcall, resultToStack, resultRegister, sscope) "any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope) "sin", "cos", "tan", "atan", @@ -712,92 +710,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } else { when (dt.getOr(DataType.UNDEFINED)) { - DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A") - DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A") - DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A") + DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A | ldy #0") + DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A | ldy #0") + DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A | ldy #0") else -> throw AssemblyError("weird type $dt") } assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A) } } - private fun funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { - outputAddressAndLenghtOfArray(fcall.args[0]) - val dt = fcall.args.single().inferType(program) - if(resultToStack) { - when (dt.getOr(DataType.UNDEFINED)) { - DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_stack") - DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack") - DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_stack") - DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack") - DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack") - else -> throw AssemblyError("weird type $dt") - } - } else { - when (dt.getOr(DataType.UNDEFINED)) { - DataType.ARRAY_UB, DataType.STR -> { - asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A") - assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A) - } - DataType.ARRAY_B -> { - asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A") - assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A) - } - DataType.ARRAY_UW -> { - asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY") - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) - } - DataType.ARRAY_W -> { - asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY") - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) - } - DataType.ARRAY_F -> { - asmgen.out(" jsr floats.func_${function.name}_f_fac1") - assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen)) - } - else -> throw AssemblyError("weird type $dt") - } - } - } - - private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { - outputAddressAndLenghtOfArray(fcall.args[0]) - val dt = fcall.args.single().inferType(program) - if(resultToStack) { - when (dt.getOr(DataType.UNDEFINED)) { - DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_stack") - DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_stack") - DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_stack") - DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_sum_w_stack") - DataType.ARRAY_F -> asmgen.out(" jsr floats.func_sum_f_stack") - else -> throw AssemblyError("weird type $dt") - } - } else { - when (dt.getOr(DataType.UNDEFINED)) { - DataType.ARRAY_UB, DataType.STR -> { - asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY") - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) - } - DataType.ARRAY_B -> { - asmgen.out(" jsr prog8_lib.func_sum_b_into_AY") - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) - } - DataType.ARRAY_UW -> { - asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY") - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) - } - DataType.ARRAY_W -> { - asmgen.out(" jsr prog8_lib.func_sum_w_into_AY") - assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) - } - DataType.ARRAY_F -> { - asmgen.out(" jsr floats.func_sum_f_fac1") - assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen)) - } - else -> throw AssemblyError("weird type $dt") - } - } - } private fun funcSwap(fcall: IFunctionCall) { val first = fcall.args[0] diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 2ed2cdff2..60d7ec5b5 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -229,7 +229,7 @@ internal class AssignmentAsmGen(private val program: Program, asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register) if(assign.target.register==null) { // still need to assign the result to the target variable/etc. - val returntype = builtinFunctionReturnType(value.name, value.args, program) + val returntype = builtinFunctionReturnType(value.name) if(!returntype.isKnown) throw AssemblyError("unknown dt") when(returntype.getOr(DataType.UNDEFINED)) { diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index 1d8f54a05..043dc421a 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -13,9 +13,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { return when(call.name) { - "max" -> funcMax(call, resultRegister) - "min" -> funcMin(call, resultRegister) - "sum" -> funcSum(call, resultRegister) "any" -> funcAny(call, resultRegister) "all" -> funcAll(call, resultRegister) "abs" -> TODO("abs once we can compare plus minus") @@ -71,28 +68,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: } } - private fun funcSum(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { - val arrayName = call.args[0] as PtIdentifier - val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable - val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) - val syscall = - when(array.dt) { - DataType.ARRAY_UB, - DataType.ARRAY_B -> Syscall.SUM_BYTE - DataType.ARRAY_UW, - DataType.ARRAY_W -> Syscall.SUM_WORD - DataType.ARRAY_F -> TODO("float sum") - else -> throw IllegalArgumentException("weird type") - } - val code = VmCodeChunk() - code += exprGen.translateExpression(call.args[0], 0) - code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) - code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) - if(resultRegister!=0) - code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) - return code - } - private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { val arrayName = call.args[0] as PtIdentifier val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable @@ -137,50 +112,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: return code } - private fun funcMax(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { - val arrayName = call.args[0] as PtIdentifier - val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable - val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) - val syscall = - when(array.dt) { - DataType.ARRAY_UB -> Syscall.MAX_UBYTE - DataType.ARRAY_B -> Syscall.MAX_BYTE - DataType.ARRAY_UW -> Syscall.MAX_UWORD - DataType.ARRAY_W -> Syscall.MAX_WORD - DataType.ARRAY_F -> TODO("float max") - else -> throw IllegalArgumentException("weird type") - } - val code = VmCodeChunk() - code += exprGen.translateExpression(call.args[0], 0) - code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) - code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) - if(resultRegister!=0) - code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) - return code - } - - private fun funcMin(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { - val arrayName = call.args[0] as PtIdentifier - val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable - val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt)) - val syscall = - when(array.dt) { - DataType.ARRAY_UB -> Syscall.MIN_UBYTE - DataType.ARRAY_B -> Syscall.MIN_BYTE - DataType.ARRAY_UW -> Syscall.MIN_UWORD - DataType.ARRAY_W -> Syscall.MIN_WORD - DataType.ARRAY_F -> TODO("float min") - else -> throw IllegalArgumentException("weird type") - } - val code = VmCodeChunk() - code += exprGen.translateExpression(call.args[0], 0) - code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) - code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) - if(resultRegister!=0) - code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0) - return code - } - private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { val code = VmCodeChunk() code += exprGen.translateExpression(call.args.single(), 0) diff --git a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt index fc571bd7f..92f08d40a 100644 --- a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt @@ -267,6 +267,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable { if(functionCallExpr.target.nameInSource == listOf("lsb")) { + if(functionCallExpr.args.isEmpty()) + return noModifications val arg = functionCallExpr.args[0] if(arg is TypecastExpression) { val valueDt = arg.expression.inferType(program) @@ -283,6 +285,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr } } else if(functionCallExpr.target.nameInSource == listOf("msb")) { + if(functionCallExpr.args.isEmpty()) + return noModifications val arg = functionCallExpr.args[0] if(arg is TypecastExpression) { val valueDt = arg.expression.inferType(program) diff --git a/compiler/res/prog8lib/c64/floats_funcs.asm b/compiler/res/prog8lib/c64/floats_funcs.asm index cc6293c8b..12bfabf37 100644 --- a/compiler/res/prog8lib/c64/floats_funcs.asm +++ b/compiler/res/prog8lib/c64/floats_funcs.asm @@ -353,85 +353,3 @@ func_all_f_stack .proc jsr a_times_5 jmp prog8_lib.func_all_b_stack .pend - -func_max_f_stack .proc - jsr func_max_f_fac1 - jmp push_fac1 - .pend - -func_max_f_fac1 .proc - ; -- max(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A -_loop_count = P8ZP_SCRATCH_REG - stx floats_store_reg - sta _loop_count - lda #255 - sta _minmax_cmp+1 ; modifying - lda #<_largest_neg_float - ldy #>_largest_neg_float -_minmax_entry jsr MOVFM -- lda P8ZP_SCRATCH_W1 - ldy P8ZP_SCRATCH_W1+1 - jsr FCOMP -_minmax_cmp cmp #255 ; modified - bne + - lda P8ZP_SCRATCH_W1 - ldy P8ZP_SCRATCH_W1+1 - jsr MOVFM -+ lda P8ZP_SCRATCH_W1 - clc - adc #5 - sta P8ZP_SCRATCH_W1 - bcc + - inc P8ZP_SCRATCH_W1+1 -+ dec _loop_count - bne - - ldx floats_store_reg - rts -_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38 - .pend - -func_min_f_stack .proc - jsr func_min_f_fac1 - jmp push_fac1 - .pend - -func_min_f_fac1 .proc - ; -- min(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A - sta func_max_f_fac1._loop_count - lda #1 - sta func_max_f_fac1._minmax_cmp+1 - lda #<_largest_pos_float - ldy #>_largest_pos_float - jmp func_max_f_fac1._minmax_entry -_largest_pos_float .byte 255,127,255,255,255 ; largest positive float - rts - .pend - - -func_sum_f_stack .proc - jsr func_sum_f_fac1 - jmp push_fac1 - .pend - -func_sum_f_fac1 .proc - ; -- sum(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A -_loop_count = P8ZP_SCRATCH_REG - stx floats_store_reg - sta _loop_count - lda #FL_ZERO_const - jsr MOVFM -- lda P8ZP_SCRATCH_W1 - ldy P8ZP_SCRATCH_W1+1 - jsr FADD - lda P8ZP_SCRATCH_W1 - clc - adc #5 - sta P8ZP_SCRATCH_W1 - bcc + - inc P8ZP_SCRATCH_W1+1 -+ dec _loop_count - bne - - ldx floats_store_reg - rts - .pend diff --git a/compiler/res/prog8lib/prog8_funcs.asm b/compiler/res/prog8lib/prog8_funcs.asm index 449e3df8b..8b132f7ce 100644 --- a/compiler/res/prog8lib/prog8_funcs.asm +++ b/compiler/res/prog8lib/prog8_funcs.asm @@ -260,394 +260,6 @@ func_rndw_stack .proc rts .pend -func_min_ub_into_A .proc - ; -- min(ubarray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A) - tay - dey - lda #255 - sta P8ZP_SCRATCH_B1 -- lda (P8ZP_SCRATCH_W1),y - cmp P8ZP_SCRATCH_B1 - bcs + - sta P8ZP_SCRATCH_B1 -+ dey - cpy #255 - bne - - lda P8ZP_SCRATCH_B1 - rts - .pend - -func_min_ub_stack .proc - jsr func_min_ub_into_A - sta P8ESTACK_LO,x - dex - rts - .pend - -func_min_b_into_A .proc - ; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A) - tay - dey - lda #127 - sta P8ZP_SCRATCH_B1 -- lda (P8ZP_SCRATCH_W1),y - clc - sbc P8ZP_SCRATCH_B1 - bvc + - eor #$80 -+ bpl + - lda (P8ZP_SCRATCH_W1),y - sta P8ZP_SCRATCH_B1 -+ dey - cpy #255 - bne - - lda P8ZP_SCRATCH_B1 - rts - .pend - -func_min_b_stack .proc - jsr func_min_b_into_A - sta P8ESTACK_LO,x - dex - rts - .pend - -func_min_uw_into_AY .proc - ; -- min(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A) - asl a - tay - dey - dey - lda #$ff - sta _result_minuw - sta _result_minuw+1 -_loop - iny - lda (P8ZP_SCRATCH_W1),y - dey - cmp _result_minuw+1 - bcc _less - bne _gtequ - lda (P8ZP_SCRATCH_W1),y - cmp _result_minuw - bcs _gtequ -_less lda (P8ZP_SCRATCH_W1),y - sta _result_minuw - iny - lda (P8ZP_SCRATCH_W1),y - sta _result_minuw+1 - dey -_gtequ dey - dey - cpy #254 - bne _loop - lda _result_minuw - ldy _result_minuw+1 - rts -_result_minuw .word 0 - .pend - -func_min_w_into_AY .proc - ; -- min(warray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A) - asl a - tay - dey - dey - lda #$ff - sta _result_minw - lda #$7f - sta _result_minw+1 -_loop - lda (P8ZP_SCRATCH_W1),y - cmp _result_minw - iny - lda (P8ZP_SCRATCH_W1),y - dey - sbc _result_minw+1 - bvc + - eor #$80 -+ bpl _gtequ - lda (P8ZP_SCRATCH_W1),y - sta _result_minw - iny - lda (P8ZP_SCRATCH_W1),y - sta _result_minw+1 - dey -_gtequ dey - dey - cpy #254 - bne _loop - lda _result_minw - ldy _result_minw+1 - rts -_result_minw .word 0 - .pend - -func_min_uw_stack .proc - jsr func_min_uw_into_AY - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - -func_min_w_stack .proc - jsr func_min_w_into_AY - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - -func_max_ub_into_A .proc - ; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A) - tay - dey - lda #0 - sta P8ZP_SCRATCH_B1 -- lda (P8ZP_SCRATCH_W1),y - cmp P8ZP_SCRATCH_B1 - bcc + - sta P8ZP_SCRATCH_B1 -+ dey - cpy #255 - bne - - lda P8ZP_SCRATCH_B1 - rts - .pend - -func_max_ub_stack .proc - jsr func_max_ub_into_A - sta P8ESTACK_LO,x - dex - rts - .pend - -func_max_b_into_A .proc - ; -- max(barray) -> A (array in P8ZP_SCRATCH_W1, num elements in A) - tay - lda #-128 - sta P8ZP_SCRATCH_B1 -- lda (P8ZP_SCRATCH_W1),y - sec - sbc P8ZP_SCRATCH_B1 - bvc + - eor #$80 -+ bmi + - lda (P8ZP_SCRATCH_W1),y - sta P8ZP_SCRATCH_B1 -+ dey - cpy #255 - bne - - lda P8ZP_SCRATCH_B1 - rts - .pend - -func_max_b_stack .proc - jsr func_max_b_into_A - sta P8ESTACK_LO,x - dex - rts - .pend - -func_max_uw_into_AY .proc - ; -- max(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A) - asl a - tay - dey - dey - lda #0 - sta _result_maxuw - sta _result_maxuw+1 -_loop - iny - lda (P8ZP_SCRATCH_W1),y - dey - cmp _result_maxuw+1 - bcc _lesseq - bne _greater - lda (P8ZP_SCRATCH_W1),y - cmp _result_maxuw - bcc _lesseq -_greater lda (P8ZP_SCRATCH_W1),y - sta _result_maxuw - iny - lda (P8ZP_SCRATCH_W1),y - sta _result_maxuw+1 - dey -_lesseq dey - dey - cpy #254 - bne _loop - lda _result_maxuw - ldy _result_maxuw+1 - rts -_result_maxuw .word 0 - .pend - -func_max_uw_stack .proc - jsr func_max_uw_into_AY - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - -func_max_w_into_AY .proc - ; -- max(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A) - asl a - tay - dey - dey - lda #0 - sta _result_maxw - lda #$80 - sta _result_maxw+1 -_loop - lda (P8ZP_SCRATCH_W1),y - cmp _result_maxw - iny - lda (P8ZP_SCRATCH_W1),y - dey - sbc _result_maxw+1 - bvc + - eor #$80 -+ bmi _lesseq - lda (P8ZP_SCRATCH_W1),y - sta _result_maxw - iny - lda (P8ZP_SCRATCH_W1),y - sta _result_maxw+1 - dey -_lesseq dey - dey - cpy #254 - bne _loop - lda _result_maxw - ldy _result_maxw+1 - rts -_result_maxw .word 0 - .pend - -func_max_w_stack .proc - jsr func_max_w_into_AY - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - -func_sum_ub_into_AY .proc - ; -- sum(ubarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A) - tay - dey - lda #0 - sta P8ZP_SCRATCH_W2 - sta P8ZP_SCRATCH_W2+1 -- lda (P8ZP_SCRATCH_W1),y - clc - adc P8ZP_SCRATCH_W2 - sta P8ZP_SCRATCH_W2 - bcc + - inc P8ZP_SCRATCH_W2+1 -+ dey - cpy #255 - bne - - lda P8ZP_SCRATCH_W2 - ldy P8ZP_SCRATCH_W2+1 - rts - .pend - -func_sum_ub_stack .proc - jsr func_sum_ub_into_AY - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - - -func_sum_b_into_AY .proc - ; -- sum(barray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A) - tay - dey - lda #0 - sta P8ZP_SCRATCH_W2 - sta P8ZP_SCRATCH_W2+1 -_loop lda (P8ZP_SCRATCH_W1),y - pha - clc - adc P8ZP_SCRATCH_W2 - sta P8ZP_SCRATCH_W2 - ; sign extend the high byte - pla - and #$80 - beq + - lda #$ff -+ adc P8ZP_SCRATCH_W2+1 - sta P8ZP_SCRATCH_W2+1 - dey - cpy #255 - bne _loop - lda P8ZP_SCRATCH_W2 - ldy P8ZP_SCRATCH_W2+1 - rts - .pend - -func_sum_b_stack .proc - jsr func_sum_b_into_AY - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - -func_sum_uw_into_AY .proc - ; -- sum(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A) - asl a - tay - dey - dey - lda #0 - sta P8ZP_SCRATCH_W2 - sta P8ZP_SCRATCH_W2+1 -- lda (P8ZP_SCRATCH_W1),y - iny - clc - adc P8ZP_SCRATCH_W2 - sta P8ZP_SCRATCH_W2 - lda (P8ZP_SCRATCH_W1),y - adc P8ZP_SCRATCH_W2+1 - sta P8ZP_SCRATCH_W2+1 - dey - dey - dey - cpy #254 - bne - - lda P8ZP_SCRATCH_W2 - ldy P8ZP_SCRATCH_W2+1 - rts - .pend - -func_sum_uw_stack .proc - jsr func_sum_uw_into_AY - sta P8ESTACK_LO,x - tya - sta P8ESTACK_HI,x - dex - rts - .pend - - -func_sum_w_into_AY = func_sum_uw_into_AY -func_sum_w_stack = func_sum_uw_stack - func_sort_ub .proc ; 8bit unsigned sort diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 53fb90418..b22d192ef 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -195,8 +195,8 @@ private class BuiltinFunctionsFacade(functions: Map): IBuilt override val names = functions.keys override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet() - override fun constValue(name: String, args: List, position: Position): NumericLiteral? { - val func = BuiltinFunctions[name] + override fun constValue(funcName: String, args: List, position: Position): NumericLiteral? { + val func = BuiltinFunctions[funcName] if(func!=null) { val exprfunc = func.constExpressionFunc if(exprfunc!=null) { @@ -213,8 +213,7 @@ private class BuiltinFunctionsFacade(functions: Map): IBuilt } return null } - override fun returnType(name: String, args: MutableList) = - builtinFunctionReturnType(name, args, program) + override fun returnType(funcName: String) = builtinFunctionReturnType(funcName) } fun parseImports(filepath: Path, diff --git a/compiler/src/prog8/compiler/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/IntermediateAstMaker.kt index 6eb29fdf3..6cc89c8a0 100644 --- a/compiler/src/prog8/compiler/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/IntermediateAstMaker.kt @@ -161,7 +161,7 @@ class IntermediateAstMaker(val program: Program) { } private fun transform(srcNode: BuiltinFunctionCallStatement): PtBuiltinFunctionCall { - val type = builtinFunctionReturnType(srcNode.name, srcNode.args, program).getOr(DataType.UNDEFINED) + val type = builtinFunctionReturnType(srcNode.name).getOr(DataType.UNDEFINED) val noSideFx = BuiltinFunctions.getValue(srcNode.name).pure val call = PtBuiltinFunctionCall(srcNode.name, true, noSideFx, type, srcNode.position) for (arg in srcNode.args) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index e2b5b559f..84a7fd79b 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -496,7 +496,7 @@ internal class AstChecker(private val program: Program, if(constVal==null) { val sourceDatatype = assignment.value.inferType(program) if (sourceDatatype.isUnknown) { - if (assignment.value !is FunctionCallExpression) + if (assignment.value !is FunctionCallExpression && assignment.value !is PipeExpression) errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position) } else { checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED), @@ -986,14 +986,7 @@ internal class AstChecker(private val program: Program, } } else if(targetStatement is BuiltinFunctionPlaceholder) { - val args = if(functionCallExpr.parent is IPipe) { - // pipe segment, add implicit first argument - val firstArgDt = BuiltinFunctions.getValue(targetStatement.name).parameters.first().possibleDatatypes.first() - listOf(defaultZero(firstArgDt, functionCallExpr.position)) + functionCallExpr.args - } else { - functionCallExpr.args - } - if(builtinFunctionReturnType(targetStatement.name, args, program).isUnknown) { + if(builtinFunctionReturnType(targetStatement.name).isUnknown) { if(functionCallExpr.parent is Expression || functionCallExpr.parent is Assignment) errors.err("function doesn't return a value", functionCallExpr.position) } @@ -1074,12 +1067,6 @@ internal class AstChecker(private val program: Program, errors.err("any/all on a string is useless (is always true unless the string is empty)", position) } } - else if(target.name=="min" || target.name=="max") { - if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR - || args[0].inferType(program).getOr(DataType.STR) == DataType.STR) { - errors.err("min/max operate on arrays, not on strings", position) - } - } } else if(target is Subroutine) { if(target.isAsmSubroutine) { for (arg in args.zip(target.parameters)) { @@ -1509,7 +1496,7 @@ internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statem else errors.warn("result values of subroutine call are discarded (use void?)", call.position) } else if (target is BuiltinFunctionPlaceholder) { - val rt = builtinFunctionReturnType(target.name, call.args, program) + val rt = builtinFunctionReturnType(target.name) if (rt.isKnown) errors.warn("result value of a function call is discarded (use void?)", call.position) } diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index e564cbf40..40712b597 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -114,12 +114,12 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe } override fun visit(pipe: PipeExpression) { - processPipe(pipe.source, pipe.segments, pipe) + processPipe(pipe.source, pipe.segments) if(errors.noErrors()) { val last = (pipe.segments.last() as IFunctionCall).target when (val target = last.targetStatement(program)!!) { is BuiltinFunctionPlaceholder -> { - if (!BuiltinFunctions.getValue(target.name).hasReturn) + if (BuiltinFunctions.getValue(target.name).returnType==null) errors.err("invalid pipe expression; last term doesn't return a value", last.position) } is Subroutine -> { @@ -135,13 +135,13 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe } override fun visit(pipe: Pipe) { - processPipe(pipe.source, pipe.segments, pipe) + processPipe(pipe.source, pipe.segments) if(errors.noErrors()) { super.visit(pipe) } } - private fun processPipe(source: Expression, segments: List, scope: Node) { + private fun processPipe(source: Expression, segments: List) { val sourceArg = (source as? IFunctionCall)?.args?.firstOrNull() if(sourceArg!=null && segments.any { (it as IFunctionCall).args.firstOrNull() === sourceArg}) @@ -160,7 +160,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe val func = BuiltinFunctions.getValue(target.name) if(func.parameters.size!=1) errors.err("can only use unary function", funccall.position) - else if(!func.hasReturn && funccall !== segments.last()) + else if(func.returnType==null && funccall !== segments.last()) errors.err("function must return a single value", funccall.position) val paramDts = func.parameters.firstOrNull()?.possibleDatatypes @@ -170,8 +170,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe if(errors.noErrors()) { // type can depend on the argument(s) of the function. For now, we only deal with unary functions, // so we know there must be a single argument. Take its type from the previous expression in the pipe chain. - val zero = defaultZero(valueDt, funccall.position) - valueDt = builtinFunctionReturnType(func.name, listOf(zero), program).getOrElse { DataType.UNDEFINED } + valueDt = builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED } } } is Subroutine -> { diff --git a/compiler/test/TestBuiltinFunctions.kt b/compiler/test/TestBuiltinFunctions.kt index d413f9884..a107924b3 100644 --- a/compiler/test/TestBuiltinFunctions.kt +++ b/compiler/test/TestBuiltinFunctions.kt @@ -16,7 +16,6 @@ class TestBuiltinFunctions: FunSpec({ func.parameters[0].name shouldBe "value" func.parameters[0].possibleDatatypes shouldBe NumericDatatypes func.pure shouldBe true - func.hasReturn shouldBe true func.returnType shouldBe DataType.BYTE val conv = func.callConvention(listOf(DataType.UBYTE)) @@ -34,7 +33,6 @@ class TestBuiltinFunctions: FunSpec({ func.name shouldBe "rnd" func.parameters.size shouldBe 0 func.pure shouldBe false - func.hasReturn shouldBe true func.returnType shouldBe DataType.UBYTE val conv = func.callConvention(emptyList()) @@ -53,7 +51,6 @@ class TestBuiltinFunctions: FunSpec({ func.parameters[1].name shouldBe "value" func.parameters[1].possibleDatatypes shouldBe arrayOf(DataType.UBYTE) func.pure shouldBe false - func.hasReturn shouldBe false func.returnType shouldBe null val conv = func.callConvention(listOf(DataType.UWORD, DataType.UBYTE)) @@ -68,25 +65,5 @@ class TestBuiltinFunctions: FunSpec({ conv.returns.floatFac1 shouldBe false conv.returns.reg shouldBe null } - - test("func with variable return type") { - val func = BuiltinFunctions.getValue("abs") - func.name shouldBe "abs" - func.parameters.size shouldBe 1 - func.parameters[0].name shouldBe "value" - func.parameters[0].possibleDatatypes.toSet() shouldBe NumericDatatypes.toSet() - func.pure shouldBe true - func.hasReturn shouldBe true - func.returnType shouldBe null - - val conv = func.callConvention(listOf(DataType.UWORD)) - conv.params.size shouldBe 1 - conv.params[0].dt shouldBe DataType.UWORD - conv.params[0].reg shouldBe RegisterOrPair.AY - conv.params[0].variable shouldBe false - conv.returns.dt shouldBe DataType.UWORD - conv.returns.floatFac1 shouldBe false - conv.returns.reg shouldBe RegisterOrPair.AY - } }) diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index e86d63343..06cf41cc3 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -604,7 +604,7 @@ class TestOptimization: FunSpec({ uword @shared zz zz += 60 ; NOT ok to remove initializer, should evaluate to 60 ubyte @shared xx - xx = 6+abs(xx) ; is not an initializer because it references xx + xx = 6+lsb(abs(xx)) ; is not an initializer because it references xx } } """ diff --git a/compiler/test/TestPipes.kt b/compiler/test/TestPipes.kt index 6a65b4102..d82023ad8 100644 --- a/compiler/test/TestPipes.kt +++ b/compiler/test/TestPipes.kt @@ -133,7 +133,7 @@ class TestPipes: FunSpec({ 9999 |> abs() |> txt.print_uw() 9999 |> txt.print_uw() - 99 |> abs() |> txt.print_ub() + 99 |> abs() |> lsb() |> txt.print_ub() 99 |> txt.print_ub() } @@ -411,7 +411,7 @@ class TestPipes: FunSpec({ uword ww = 9999 ubyte bb = 99 ww |> abs() |> txt.print_uw() - bb |> abs() |> txt.print_ub() + bb |> abs() |> lsb() |> txt.print_ub() } } """ @@ -425,8 +425,9 @@ class TestPipes: FunSpec({ val pipebb = stmts[5] as Pipe pipebb.source shouldBe instanceOf() - pipebb.segments.size shouldBe 1 + pipebb.segments.size shouldBe 2 pipebb.segments[0] shouldBe instanceOf() + pipebb.segments[1] shouldBe instanceOf() } test("pipe statement with type errors") { diff --git a/compiler/test/helpers/Dummies.kt b/compiler/test/helpers/Dummies.kt index ebff9d355..ff2939713 100644 --- a/compiler/test/helpers/Dummies.kt +++ b/compiler/test/helpers/Dummies.kt @@ -11,12 +11,12 @@ internal object DummyFunctions : IBuiltinFunctions { override val names: Set = emptySet() override val purefunctionNames: Set = emptySet() override fun constValue( - name: String, + funcName: String, args: List, position: Position, ): NumericLiteral? = null - override fun returnType(name: String, args: MutableList) = InferredTypes.InferredType.unknown() + override fun returnType(funcName: String) = InferredTypes.InferredType.unknown() } internal object DummyMemsizer : IMemSizer { diff --git a/compilerAst/src/prog8/ast/IBuiltinFunctions.kt b/compilerAst/src/prog8/ast/IBuiltinFunctions.kt index b322f068d..088d4e4b9 100644 --- a/compilerAst/src/prog8/ast/IBuiltinFunctions.kt +++ b/compilerAst/src/prog8/ast/IBuiltinFunctions.kt @@ -8,6 +8,6 @@ import prog8.code.core.Position interface IBuiltinFunctions { val names: Set val purefunctionNames: Set - fun constValue(name: String, args: List, position: Position): NumericLiteral? - fun returnType(name: String, args: MutableList): InferredTypes.InferredType + fun constValue(funcName: String, args: List, position: Position): NumericLiteral? + fun returnType(funcName: String): InferredTypes.InferredType } diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 80c72df3a..cb0ff0508 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -986,7 +986,7 @@ class FunctionCallExpression(override var target: IdentifierReference, val stmt = target.targetStatement(program) ?: return InferredTypes.unknown() when (stmt) { is BuiltinFunctionPlaceholder -> { - return program.builtinFunctions.returnType(target.nameInSource[0], this.args) + return program.builtinFunctions.returnType(target.nameInSource[0]) } is Subroutine -> { if(stmt.returntypes.isEmpty()) @@ -1164,5 +1164,5 @@ class BuiltinFunctionCall(override var target: IdentifierReference, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun referencesIdentifier(nameInSource: List): Boolean = target.referencesIdentifier(nameInSource) || args.any{it.referencesIdentifier(nameInSource)} - override fun inferType(program: Program) = program.builtinFunctions.returnType(name, this.args) + override fun inferType(program: Program) = program.builtinFunctions.returnType(name) } diff --git a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt b/compilerAst/src/prog8/compiler/BuiltinFunctions.kt index 1078f3f6e..6300ccf4f 100644 --- a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt +++ b/compilerAst/src/prog8/compiler/BuiltinFunctions.kt @@ -38,36 +38,26 @@ class FParam(val name: String, val possibleDatatypes: Array) class FSignature(val name: String, val pure: Boolean, // does it have side effects? val parameters: List, - val hasReturn: Boolean, // is there a return value at all? - val returnType: DataType?, // specify return type if fixed, otherwise null if it depends on the arguments + val returnType: DataType?, val constExpressionFunc: ConstExpressionCaller? = null) { - init { - require(hasReturn || returnType==null) { "$name has invalid return spec" } - } - fun callConvention(actualParamTypes: List): CallConvention { - val returns: ReturnConvention - if(hasReturn) { - returns = when (returnType) { - DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false) - DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false) - DataType.FLOAT -> ReturnConvention(returnType, null, true) - in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false) - else -> { - // return type depends on arg type - when (val paramType = actualParamTypes.first()) { - DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false) - DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false) - DataType.FLOAT -> ReturnConvention(paramType, null, true) - in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false) - else -> ReturnConvention(paramType, null, false) - } + val returns: ReturnConvention = when (returnType) { + DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false) + DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false) + DataType.FLOAT -> ReturnConvention(returnType, null, true) + in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false) + null -> ReturnConvention(null, null, false) + else -> { + // return type depends on arg type + when (val paramType = actualParamTypes.first()) { + DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false) + DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false) + DataType.FLOAT -> ReturnConvention(paramType, null, true) + in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false) + else -> ReturnConvention(paramType, null, false) } } - } else { - require(returnType==null) - returns = ReturnConvention(null, null, false) } return when { @@ -95,167 +85,78 @@ class FSignature(val name: String, private val functionSignatures: List = listOf( // this set of function have no return value and operate in-place: - FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false,null), - FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null), - FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null), - FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null), - FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), false, null), - FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), false, null), + FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), + FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null), + FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null), // cmp returns a status in the carry flag, but not a proper return value - FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), false, null), - // these few have a return value depending on the argument(s): - FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args - FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args - FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args - FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), true, null, ::builtinAbs), // type depends on argument - FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), true, null, ::builtinLen), // type is UBYTE or UWORD depending on actual length - FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), true, DataType.UBYTE, ::builtinSizeof), + FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null), + FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), DataType.UWORD, ::builtinAbs), + FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD, ::builtinLen), // normal functions follow: - FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), true, DataType.BYTE, ::builtinSgn ), - FSignature("sin" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) }, - FSignature("cos" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) }, - FSignature("tan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) }, - FSignature("atan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) }, - FSignature("ln" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) }, - FSignature("log2" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) }, - FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } }, - FSignature("sqrt" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) }, - FSignature("rad" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) }, - FSignature("deg" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) }, - FSignature("round" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, ::round) }, - FSignature("floor" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) }, - FSignature("ceil" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) }, - FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), true, DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, - FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), true, DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, - FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } }, - FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} }, - FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinMkword), - FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), true, DataType.UBYTE), - FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), true, DataType.UWORD), - FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), false,null), - FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), false,null), - FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), false,null), - FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), false, null), - FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), false, null), - FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), false, null), - FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), false, null), - FSignature("rsave" , false, emptyList(), false,null), - FSignature("rsavex" , false, emptyList(), false,null), - FSignature("rrestore" , false, emptyList(), false,null), - FSignature("rrestorex" , false, emptyList(), false,null), - FSignature("rnd" , false, emptyList(), true, DataType.UBYTE), - FSignature("rndw" , false, emptyList(), true, DataType.UWORD), - FSignature("rndf" , false, emptyList(), true, DataType.FLOAT), - FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), true, DataType.UWORD), - FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), false, null), - FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), false, null), - FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), false, null), - FSignature("syscall" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE))), true, DataType.UWORD, null), - FSignature("syscall1" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE)), FParam("arg1", arrayOf(DataType.UWORD))), true, DataType.UWORD, null), - FSignature("syscall2" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE)), FParam("arg1", arrayOf(DataType.UWORD)), FParam("arg2", arrayOf(DataType.UWORD))), true, DataType.UWORD, null), - FSignature("syscall3" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE)), FParam("arg1", arrayOf(DataType.UWORD)), FParam("arg2", arrayOf(DataType.UWORD)), FParam("arg3", arrayOf(DataType.UWORD))), true, DataType.UWORD, null), + FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), DataType.UBYTE, ::builtinSizeof), + FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ), + FSignature("sin" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) }, + FSignature("cos" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) }, + FSignature("tan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) }, + FSignature("atan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) }, + FSignature("ln" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) }, + FSignature("log2" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) }, + FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } }, + FSignature("sqrt" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) }, + FSignature("rad" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) }, + FSignature("deg" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) }, + FSignature("round" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, ::round) }, + FSignature("floor" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) }, + FSignature("ceil" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) }, + FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, + FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, + FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } }, + FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} }, + FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword), + FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), + FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), + FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), + FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), + FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), + FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), null), + FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), null), + FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), null), + FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), null), + FSignature("rsave" , false, emptyList(), null), + FSignature("rsavex" , false, emptyList(), null), + FSignature("rrestore" , false, emptyList(), null), + FSignature("rrestorex" , false, emptyList(), null), + FSignature("rnd" , false, emptyList(), DataType.UBYTE), + FSignature("rndw" , false, emptyList(), DataType.UWORD), + FSignature("rndf" , false, emptyList(), DataType.FLOAT), + FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), + FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null), + FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), + FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), + FSignature("syscall" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE))), DataType.UWORD, null), + FSignature("syscall1" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE)), FParam("arg1", arrayOf(DataType.UWORD))), DataType.UWORD, null), + FSignature("syscall2" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE)), FParam("arg1", arrayOf(DataType.UWORD)), FParam("arg2", arrayOf(DataType.UWORD))), DataType.UWORD, null), + FSignature("syscall3" , false, listOf(FParam("callnr", arrayOf(DataType.UBYTE)), FParam("arg1", arrayOf(DataType.UWORD)), FParam("arg2", arrayOf(DataType.UWORD)), FParam("arg3", arrayOf(DataType.UWORD))), DataType.UWORD, null), ) val BuiltinFunctions = functionSignatures.associateBy { it.name } -private fun builtinMax(array: List): Double = array.maxByOrNull { it }!! - -private fun builtinMin(array: List): Double = array.minByOrNull { it }!! - -private fun builtinSum(array: List): Double = array.sumOf { it } - private fun builtinAny(array: List): Double = if(array.any { it!=0.0 }) 1.0 else 0.0 private fun builtinAll(array: List): Double = if(array.all { it!=0.0 }) 1.0 else 0.0 -fun builtinFunctionReturnType(function: String, args: List, program: Program): InferredTypes.InferredType { - +fun builtinFunctionReturnType(function: String): InferredTypes.InferredType { if(function in arrayOf("set_carry", "set_irqd", "clear_carry", "clear_irqd")) return InferredTypes.InferredType.void() - fun datatypeFromIterableArg(arglist: Expression): DataType { - if(arglist is ArrayLiteral) { - val dt = arglist.value.map {it.inferType(program).getOr(DataType.UNDEFINED)}.toSet() - if(dt.any { it !in NumericDatatypes }) { - throw FatalAstException("fuction $function only accepts array of numeric values") - } - if(DataType.FLOAT in dt) return DataType.FLOAT - if(DataType.UWORD in dt) return DataType.UWORD - if(DataType.WORD in dt) return DataType.WORD - if(DataType.BYTE in dt) return DataType.BYTE - return DataType.UBYTE - } - if(arglist is IdentifierReference) { - val idt = arglist.inferType(program) - return when(val dt = idt.getOrElse { throw FatalAstException("unknown dt") }) { - DataType.STR, in NumericDatatypes -> dt - in ArrayDatatypes -> ArrayToElementTypes.getValue(dt) - else -> throw FatalAstException("function '$function' requires one argument which is an iterable") - } - } - throw FatalAstException("function '$function' requires one argument which is an iterable") - } - val func = BuiltinFunctions.getValue(function) - if(func.returnType!=null) - return InferredTypes.knownFor(func.returnType) - if(!func.hasReturn) + if(func.returnType==null) return InferredTypes.InferredType.void() - - // function has return values, but the return type depends on the arguments - if(args.isEmpty()) - return InferredTypes.InferredType.unknown() - return when (function) { - "abs" -> { - val dt = args.single().inferType(program) - return if(dt.isNumeric) - dt - else - InferredTypes.InferredType.unknown() - } - "max", "min" -> { - when(val dt = datatypeFromIterableArg(args.single())) { - DataType.STR -> InferredTypes.knownFor(DataType.UBYTE) - in NumericDatatypes -> InferredTypes.knownFor(dt) - in ArrayDatatypes -> InferredTypes.knownFor(ArrayToElementTypes.getValue(dt)) - else -> InferredTypes.unknown() - } - } - "sum" -> { - when(datatypeFromIterableArg(args.single())) { - DataType.UBYTE, DataType.UWORD -> InferredTypes.knownFor(DataType.UWORD) - DataType.BYTE, DataType.WORD -> InferredTypes.knownFor(DataType.WORD) - DataType.FLOAT -> InferredTypes.knownFor(DataType.FLOAT) - DataType.ARRAY_UB, DataType.ARRAY_UW -> InferredTypes.knownFor(DataType.UWORD) - DataType.ARRAY_B, DataType.ARRAY_W -> InferredTypes.knownFor(DataType.WORD) - DataType.ARRAY_F -> InferredTypes.knownFor(DataType.FLOAT) - DataType.STR -> InferredTypes.knownFor(DataType.UWORD) - else -> InferredTypes.unknown() - } - } - "len" -> { - when(args.single().inferType(program).getOr(DataType.UNDEFINED)) { - in ArrayDatatypes -> { - val value = args.single() as? ArrayLiteral - if(value!=null) { - return if(value.value.size<256) InferredTypes.knownFor(DataType.UBYTE) else InferredTypes.knownFor(DataType.UWORD) - } else { - val targetVar = (args.single() as? IdentifierReference)?.targetVarDecl(program) - if (targetVar?.isArray == true) { - val length = targetVar.arraysize?.constIndex() - if(length!=null) - return if(length<256) InferredTypes.knownFor(DataType.UBYTE) else InferredTypes.knownFor(DataType.UWORD) - } - } - return InferredTypes.knownFor(DataType.UWORD) - } - DataType.STR -> return InferredTypes.knownFor(DataType.UBYTE) - else -> InferredTypes.unknown() - } - } - else -> return InferredTypes.unknown() - } + return InferredTypes.knownFor(func.returnType) } diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 6c8211906..c886aef33 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -611,7 +611,7 @@ In most places where a number or other value is expected, you can use just the n If possible, the expression is parsed and evaluated by the compiler itself at compile time, and the (constant) resulting value is used in its place. Expressions that cannot be compile-time evaluated will result in code that calculates them at runtime. Expressions can contain procedure and function calls. -There are various built-in functions such as sin(), cos(), min(), max() that can be used in expressions (see :ref:`builtinfunctions`). +There are various built-in functions such as sin(), cos() that can be used in expressions (see :ref:`builtinfunctions`). You can also reference idendifiers defined elsewhere in your code. Read the :ref:`syntaxreference` chapter for all details on the available operators and kinds of expressions you can write. @@ -804,19 +804,10 @@ len(x) (use the ``string.length`` routine if you want to dynamically determine the length by counting to the first 0-byte) -max(x) - Maximum of the values in the array value x - -min(x) - Minimum of the values in the array value x - reverse(array) Reverse the values in the array (in-place). Can be used after sort() to sort an array in descending order. -sum(x) - Sum of the values in the array value x - sort(array) Sort the array in ascending order (in-place) Supported are arrays of bytes or word values. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ffbf70443..d5755ed7b 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- can't use abs() etc in pipe expression because return type depends on argument type +- abs: remove support for floats. Just use floats.ABS() +- ... this will solve: can't use abs() etc in pipe expression because return type depends on argument type - pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls. - createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there. - allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type diff --git a/examples/test.p8 b/examples/test.p8 index 5ac132075..48fae4754 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,19 +7,8 @@ main { sub start() { - word[] values = [1111, -222, -9999, 88, 20222, 0, 0, 1111] - word[] values2 = [0,0,0,0,0,1,0,0,0] - txt.print_w(max(values)) - txt.nl() - txt.print_w(min(values)) - txt.nl() - txt.print_w(sum(values)) - txt.nl() - txt.print_ub(any(values)) - txt.nl() - txt.print_ub(any(values2)) - txt.nl() - txt.print_ub(all(values)) + ubyte qq = 99 as ubyte |> abs() |> abs() + txt.print_ub(qq) txt.nl() ; uword other = $fe4a