mirror of
https://github.com/irmen/prog8.git
synced 2024-10-25 00:24:16 +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)
|
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"sgn" -> funcSgn(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)
|
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"rol" -> funcRol(fcall)
|
"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?) {
|
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
when(func.name) {
|
when(func.name) {
|
||||||
"rnd" -> {
|
"rnd" -> {
|
||||||
|
@ -321,61 +321,6 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
if(!expr.inferType(program).isInteger)
|
if(!expr.inferType(program).isInteger)
|
||||||
return false
|
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!="-")
|
if(expr.operator!="+" && expr.operator!="-")
|
||||||
return false
|
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")
|
"callrom" -> throw AssemblyError("callrom() is for cx16 target only")
|
||||||
"msb" -> funcMsb(call, resultRegister)
|
"msb" -> funcMsb(call, resultRegister)
|
||||||
"lsb" -> funcLsb(call, resultRegister)
|
"lsb" -> funcLsb(call, resultRegister)
|
||||||
|
"boolean" -> funcBoolean(call, resultRegister)
|
||||||
"memory" -> funcMemory(call, resultRegister)
|
"memory" -> funcMemory(call, resultRegister)
|
||||||
"peek" -> funcPeek(call, resultRegister)
|
"peek" -> funcPeek(call, resultRegister)
|
||||||
"peekw" -> funcPeekW(call, resultRegister)
|
"peekw" -> funcPeekW(call, resultRegister)
|
||||||
@ -354,6 +355,30 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
|||||||
return code
|
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 {
|
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||||
val vmDt = codeGen.vmType(call.args[0].type)
|
val vmDt = codeGen.vmType(call.args[0].type)
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
|
@ -864,13 +864,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"and", "or", "xor" -> {
|
"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)
|
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||||
errors.err("logical operator can only be used on boolean operands", expr.right.position)
|
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
|
// only integer numeric operands accepted
|
||||||
|
@ -248,13 +248,12 @@ internal class StatementReorderer(val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(expr.operator in LogicalOperators) {
|
else if(expr.operator in LogicalOperators) {
|
||||||
// make sure that logical expressions like "var and other-logical-expression
|
fun wrapped(expr: Expression): Expression {
|
||||||
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
|
return if(expr.inferType(program).isBytes)
|
||||||
// generating the wrong results later
|
expr
|
||||||
|
else
|
||||||
// TODO use bool(expr) instead of expr!=0 rewriting
|
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
|
||||||
fun wrapped(expr: Expression): Expression =
|
}
|
||||||
BinaryExpression(expr, "!=", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
|
|
||||||
|
|
||||||
fun isLogicalExpr(expr: Expression?): Boolean {
|
fun isLogicalExpr(expr: Expression?): Boolean {
|
||||||
if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))
|
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> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
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("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("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("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("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
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("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)
|
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 {
|
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sgn requires one argument", position)
|
throw SyntaxError("sgn requires one argument", position)
|
||||||
|
@ -775,6 +775,10 @@ sort(array)
|
|||||||
Miscellaneous
|
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)
|
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!
|
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.
|
You can use a conditional jumps (``if_cc`` etcetera) to act on this.
|
||||||
|
@ -3,9 +3,6 @@ TODO
|
|||||||
|
|
||||||
For next release
|
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()
|
- 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?
|
- 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 textio
|
||||||
;%import test_stack
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
; NOTE: meant to test to virtual machine output target (use -target vitual)
|
; NOTE: meant to test to virtual machine output target (use -target vitual)
|
||||||
@ -117,6 +116,7 @@ main {
|
|||||||
txt.print_ub(value)
|
txt.print_ub(value)
|
||||||
txt.nl()
|
txt.nl()
|
||||||
|
|
||||||
|
|
||||||
; a "pixelshader":
|
; a "pixelshader":
|
||||||
; sys.gfx_enable(0) ; enable lo res screen
|
; sys.gfx_enable(0) ; enable lo res screen
|
||||||
; ubyte shifter
|
; ubyte shifter
|
||||||
|
Loading…
Reference in New Issue
Block a user