mirror of
https://github.com/irmen/prog8.git
synced 2024-07-05 22:29:04 +00:00
added boolean() builtin function and use it to get rid of !=0 comparisons
This commit is contained in:
parent
bf9d120081
commit
cc174b7b85
@ -44,6 +44,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"boolean" -> funcBoolean(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"rol" -> funcRol(fcall)
|
||||
@ -697,6 +698,28 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcBoolean(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true)
|
||||
asmgen.out(" jsr floats.SIGN")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
|
||||
if(resultToStack)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
else if(resultRegister!=null)
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, scope, program, asmgen), CpuRegister.A)
|
||||
}
|
||||
|
||||
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||
when(func.name) {
|
||||
"rnd" -> {
|
||||
|
@ -321,61 +321,6 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
if(!expr.inferType(program).isInteger)
|
||||
return false
|
||||
|
||||
/*
|
||||
TODO re-add these optimizations? after we improved the unneeded addition of !=0 expressions
|
||||
if(expr.operator=="and") {
|
||||
val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("weird dt") }
|
||||
if (dt in ByteDatatypes) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", dt, expr.definingSubroutine)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
if(assign.target.datatype in ByteDatatypes)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
else {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else throw AssemblyError("weird dt for and, expected byte $expr @${expr.position}")
|
||||
}
|
||||
else if(expr.operator=="or") {
|
||||
val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("weird dt") }
|
||||
if (dt in ByteDatatypes) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", dt, expr.definingSubroutine)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
if(assign.target.datatype in ByteDatatypes)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
else {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else throw AssemblyError("weird dt for or, expected byte $expr @${expr.position}")
|
||||
}
|
||||
else if(expr.operator=="xor") {
|
||||
val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("weird dt") }
|
||||
if (dt in ByteDatatypes) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", dt, expr.definingSubroutine)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
if(assign.target.datatype in ByteDatatypes)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
else {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else throw AssemblyError("weird dt for xor, expected byte $expr @${expr.position}")
|
||||
}
|
||||
*/
|
||||
|
||||
if(expr.operator!="+" && expr.operator!="-")
|
||||
return false
|
||||
|
||||
|
@ -32,6 +32,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
||||
"callrom" -> throw AssemblyError("callrom() is for cx16 target only")
|
||||
"msb" -> funcMsb(call, resultRegister)
|
||||
"lsb" -> funcLsb(call, resultRegister)
|
||||
"boolean" -> funcBoolean(call, resultRegister)
|
||||
"memory" -> funcMemory(call, resultRegister)
|
||||
"peek" -> funcPeek(call, resultRegister)
|
||||
"peekw" -> funcPeekW(call, resultRegister)
|
||||
@ -354,6 +355,30 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcBoolean(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
val vmDt = codeGen.vmType(call.args[0].type)
|
||||
when (vmDt) {
|
||||
VmDataType.FLOAT -> {
|
||||
val fpValueReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
val fpSignReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += exprGen.translateExpression(call.args.single(), -1, fpValueReg)
|
||||
code += VmCodeInstruction(Opcode.SGN, VmDataType.FLOAT, fpReg1 = fpSignReg, fpReg2 = fpValueReg)
|
||||
code += VmCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1 = resultRegister, fpReg1 = fpSignReg)
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
val msbReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister)
|
||||
code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
|
||||
}
|
||||
else -> {
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||
val vmDt = codeGen.vmType(call.args[0].type)
|
||||
val code = VmCodeChunk()
|
||||
|
@ -864,13 +864,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
"and", "or", "xor" -> {
|
||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
||||
// only integer numeric operands accepted
|
||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||
errors.err("logical operator can only be used on boolean operands", expr.right.position)
|
||||
val constLeft = expr.left.constValue(program)
|
||||
val constRight = expr.right.constValue(program)
|
||||
if(constLeft!=null && constLeft.number.toInt() !in 0..1 || constRight!=null && constRight.number.toInt() !in 0..1)
|
||||
errors.err("const literal argument of logical operator must be boolean (0 or 1)", expr.position)
|
||||
}
|
||||
"&", "|", "^" -> {
|
||||
// only integer numeric operands accepted
|
||||
|
@ -248,13 +248,12 @@ internal class StatementReorderer(val program: Program,
|
||||
}
|
||||
}
|
||||
else if(expr.operator in LogicalOperators) {
|
||||
// make sure that logical expressions like "var and other-logical-expression
|
||||
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
|
||||
// generating the wrong results later
|
||||
|
||||
// TODO use bool(expr) instead of expr!=0 rewriting
|
||||
fun wrapped(expr: Expression): Expression =
|
||||
BinaryExpression(expr, "!=", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
|
||||
fun wrapped(expr: Expression): Expression {
|
||||
return if(expr.inferType(program).isBytes)
|
||||
expr
|
||||
else
|
||||
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
|
||||
}
|
||||
|
||||
fun isLogicalExpr(expr: Expression?): Boolean {
|
||||
if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))
|
||||
|
@ -233,5 +233,14 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
||||
}
|
||||
|
||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
|
||||
if(functionCallExpr.args[0].inferType(program).isBytes) {
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,6 +106,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
||||
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("boolean" , true, listOf(FParam("value", NumericDatatypes)), DataType.UBYTE, ::builtinBoolean),
|
||||
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),
|
||||
@ -250,6 +251,23 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
|
||||
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinBoolean(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("boolean requires one argument", position)
|
||||
val constvalue = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
return if(constvalue.number==0.0)
|
||||
NumericLiteral(DataType.UBYTE, 0.0, args[0].position)
|
||||
else if(constvalue.type==DataType.UBYTE)
|
||||
constvalue
|
||||
else if(constvalue.type==DataType.FLOAT)
|
||||
NumericLiteral(DataType.UBYTE, constvalue.number.sign, args[0].position)
|
||||
else {
|
||||
val value = constvalue.number.toInt()
|
||||
val byteValue = ((value ushr 8) or value) and 255
|
||||
NumericLiteral(DataType.UBYTE, byteValue.toDouble(), args[0].position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sgn requires one argument", position)
|
||||
|
@ -775,6 +775,10 @@ sort(array)
|
||||
Miscellaneous
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
boolean(x)
|
||||
Returns a byte value representing the boolean (truthy) value of x, where x can be any numeric type.
|
||||
This means it returns 0 (false) when x equals 0, and a value other than 0 otherwise.
|
||||
|
||||
cmp(x,y)
|
||||
Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!
|
||||
You can use a conditional jumps (``if_cc`` etcetera) to act on this.
|
||||
|
@ -3,9 +3,6 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- add a builtin function 'bool' that takes a numeric value and returns ubyte false if it was zero and true otherwise
|
||||
- statementreorderer: after(expr: BinaryExpression): get rid of unneeded !=0 added to logical expressions
|
||||
by using this bool() function instead for the operand if it is not a byte type
|
||||
- optimize logical expressions in attemptAssignOptimizedBinexpr()
|
||||
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
||||
...
|
||||
|
@ -1,5 +1,4 @@
|
||||
%import textio
|
||||
;%import test_stack
|
||||
%zeropage basicsafe
|
||||
|
||||
; NOTE: meant to test to virtual machine output target (use -target vitual)
|
||||
@ -117,6 +116,7 @@ main {
|
||||
txt.print_ub(value)
|
||||
txt.nl()
|
||||
|
||||
|
||||
; a "pixelshader":
|
||||
; sys.gfx_enable(0) ; enable lo res screen
|
||||
; ubyte shifter
|
||||
|
Loading…
Reference in New Issue
Block a user