diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AnyExprAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AnyExprAsmGen.kt index c45a0dbc5..956f47883 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AnyExprAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AnyExprAsmGen.kt @@ -49,54 +49,24 @@ internal class AnyExprAsmGen( private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean { when(expr.operator) { - "+" -> { - TODO("word + at ${expr.position}") - } - "-" -> { - TODO("word - at ${expr.position}") - } - "*" -> { - TODO("word * at ${expr.position}") - } - "/" -> { - TODO("word / at ${expr.position}") - } - "<<" -> { - TODO("word << at ${expr.position}") - } - ">>" -> { - TODO("word >> at ${expr.position}") - } - "%" -> { - TODO("word % at ${expr.position}") - } - "&", "and" -> { - TODO("word and at ${expr.position}") - } - "|", "or" -> { - TODO("word or at ${expr.position}") - } - "^", "xor" -> { - TODO("word xor at ${expr.position}") - } - "==" -> { - TODO("word == at ${expr.position}") - } - "!=" -> { - TODO("word != at ${expr.position}") - } - "<" -> { - TODO("word < at ${expr.position}") - } - "<=" -> { - TODO("word <= at ${expr.position}") - } - ">" -> { - TODO("word > at ${expr.position}") - } - ">=" -> { - TODO("word >= at ${expr.position}") - } + "+" -> TODO("word + at ${expr.position}") + "-" -> TODO("word - at ${expr.position}") + "*" -> TODO("word * at ${expr.position}") + "/" -> TODO("word / at ${expr.position}") + "<<" -> TODO("word << at ${expr.position}") + ">>" -> TODO("word >> at ${expr.position}") + "%" -> TODO("word % at ${expr.position}") + "and" -> TODO("word logical and (with optional shortcircuit) ${expr.position}") + "or" -> TODO("word logical or (with optional shortcircuit) ${expr.position}") + "&" -> TODO("word and at ${expr.position}") + "|" -> TODO("word or at ${expr.position}") + "^", "xor" -> TODO("word xor at ${expr.position}") + "==" -> TODO("word == at ${expr.position}") + "!=" -> TODO("word != at ${expr.position}") + "<" -> TODO("word < at ${expr.position}") + "<=" -> TODO("word <= at ${expr.position}") + ">" -> TODO("word > at ${expr.position}") + ">=" -> TODO("word >= at ${expr.position}") else -> return false } } @@ -134,7 +104,9 @@ internal class AnyExprAsmGen( "%" -> { TODO("byte % at ${expr.position}") } - "&", "and" -> { + "and" -> TODO("logical and (with optional shortcircuit) ${expr.position}") + "or" -> TODO("logical or (with optional shortcircuit) ${expr.position}") + "&" -> { asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.out(" pha") asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE) @@ -142,7 +114,7 @@ internal class AnyExprAsmGen( asmgen.assignRegister(RegisterOrPair.A, assign.target) return true } - "|", "or" -> { + "|" -> { asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.out(" pha") asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index de3b01e67..b1ac70598 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -427,7 +427,8 @@ internal class AssignmentAsmGen(private val program: PtProgram, private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { val translatedOk = when (expr.operator) { in ComparisonOperators -> optimizedComparison(expr, assign) - in setOf("&", "|", "^", "and", "or", "xor") -> optimizedLogicalOrBitwiseExpr(expr, assign.target) + in setOf("&", "|", "^", "xor") -> optimizedBitwiseExpr(expr, assign.target) + in setOf("and", "or") -> optimizedLogicalAndOrExpr(expr, assign.target) "+", "-" -> optimizedPlusMinExpr(expr, assign.target) "<<", ">>" -> optimizedBitshiftExpr(expr, assign.target) "*" -> optimizedMultiplyExpr(expr, assign.target) @@ -988,15 +989,15 @@ internal class AssignmentAsmGen(private val program: PtProgram, return false } - private fun optimizedLogicalOrBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean { + private fun optimizedBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean { if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) { if (expr.right.isSimple()) { if (expr.right is PtNumber || expr.right is PtIdentifier) { - assignLogicalWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right) + assignBitwiseWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right) return true } else if (expr.left is PtNumber || expr.left is PtIdentifier) { - assignLogicalWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left) + assignBitwiseWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left) return true } } @@ -1006,10 +1007,10 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE) asmgen.restoreRegisterStack(CpuRegister.A, false) when (expr.operator) { - "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") - "|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1") + "&" -> asmgen.out(" and P8ZP_SCRATCH_B1") + "|" -> asmgen.out(" ora P8ZP_SCRATCH_B1") "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") - else -> throw AssemblyError("invalid operator") + else -> throw AssemblyError("invalid bitwise operator") } assignRegisterByte(target, CpuRegister.A, false, true) return true @@ -1017,20 +1018,68 @@ internal class AssignmentAsmGen(private val program: PtProgram, else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { if (expr.right.isSimple()) { if (expr.right is PtNumber || expr.right is PtIdentifier) { - assignLogicalWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right) + assignBitwiseWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right) return true } else if (expr.left is PtNumber || expr.left is PtIdentifier) { - assignLogicalWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left) + assignBitwiseWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left) return true } } asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1") when (expr.operator) { - "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa") - "|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa") + "&" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa") + "|" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa") "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa") - else -> throw AssemblyError("invalid operator") + else -> throw AssemblyError("invalid bitwise operator") + } + assignRegisterpairWord(target, RegisterOrPair.AY) + return true + } + return false + } + + private fun optimizedLogicalAndOrExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean { + if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) { + if (expr.right.isSimple()) { + if (expr.right is PtNumber || expr.right is PtIdentifier) { + assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right) + return true + } + else if (expr.left is PtNumber || expr.left is PtIdentifier) { + assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left) + return true + } + } + + assignExpressionToRegister(expr.left, RegisterOrPair.A, false) + asmgen.saveRegisterStack(CpuRegister.A, false) + assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE) + asmgen.restoreRegisterStack(CpuRegister.A, false) + when (expr.operator) { + "and" -> TODO("logical and (with optional shortcircuit) ${expr.position}") + "or" -> TODO("logical or (with optional shortcircuit) ${expr.position}") + else -> throw AssemblyError("invalid logical operator") + } + assignRegisterByte(target, CpuRegister.A, false, true) + return true + } + else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { + if (expr.right.isSimple()) { + if (expr.right is PtNumber || expr.right is PtIdentifier) { + assignLogicalAndOrWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right) + return true + } + else if (expr.left is PtNumber || expr.left is PtIdentifier) { + assignLogicalAndOrWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left) + return true + } + } + asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1") + when (expr.operator) { + "and" -> TODO("logical and (with optional shortcircuit) ${expr.position}") + "or" -> TODO("logical or (with optional shortcircuit) ${expr.position}") + else -> throw AssemblyError("invalid logical operator") } assignRegisterpairWord(target, RegisterOrPair.AY) return true @@ -1709,7 +1758,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, return true } - private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { + private fun assignBitwiseWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { assignExpressionToRegister(left, RegisterOrPair.A, false) val operand = when(right) { is PtNumber -> "#${right.number.toHex()}" @@ -1717,33 +1766,41 @@ internal class AssignmentAsmGen(private val program: PtProgram, else -> throw AssemblyError("wrong right operand type") } when (operator) { - "&", "and" -> asmgen.out(" and $operand") - "|", "or" -> asmgen.out(" ora $operand") + "&" -> asmgen.out(" and $operand") + "|" -> asmgen.out(" ora $operand") "^", "xor" -> asmgen.out(" eor $operand") else -> throw AssemblyError("invalid operator") } assignRegisterByte(target, CpuRegister.A, false, true) } - private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { + private fun assignLogicalAndOrWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { + when (operator) { + "and" -> TODO("logical and (with optional shortcircuit) ${left.position}") + "or" -> TODO("logical or (with optional shortcircuit) ${left.position}") + else -> throw AssemblyError("invalid logical operator") + } + } + + private fun assignBitwiseWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { assignExpressionToRegister(left, RegisterOrPair.AY, false) when(right) { is PtNumber -> { val number = right.number.toHex() when (operator) { - "&", "and" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa") - "|", "or" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa") + "&" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa") + "|" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa") "^", "xor" -> asmgen.out(" eor #<$number | tax | tya | eor #>$number | tay | txa") - else -> throw AssemblyError("invalid operator") + else -> throw AssemblyError("invalid bitwise operator") } } is PtIdentifier -> { val name = asmgen.asmSymbolName(right) when (operator) { - "&", "and" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa") - "|", "or" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa") + "&" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa") + "|" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa") "^", "xor" -> asmgen.out(" eor $name | tax | tya | eor $name+1 | tay | txa") - else -> throw AssemblyError("invalid operator") + else -> throw AssemblyError("invalid bitwise operator") } } else -> throw AssemblyError("wrong right operand type") @@ -1751,6 +1808,26 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignRegisterpairWord(target, RegisterOrPair.AY) } + private fun assignLogicalAndOrWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { + when(right) { + is PtNumber -> { + when (operator) { + "and" -> TODO("logical and (with optional shortcircuit) ${left.position}") + "or" -> TODO("logical or (with optional shortcircuit) ${left.position}") + else -> throw AssemblyError("invalid logical operator") + } + } + is PtIdentifier -> { + when (operator) { + "and" -> TODO("logical and (with optional shortcircuit) ${left.position}") + "or" -> TODO("logical or (with optional shortcircuit) ${left.position}") + else -> throw AssemblyError("invalid logical operator") + } + } + else -> throw AssemblyError("wrong right operand type") + } + } + private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { when (expr.operator) { "==" -> { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index c172cf9c2..7b1b3e04e 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -367,9 +367,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { "*" -> operatorMultiply(binExpr, vmDt) "/" -> operatorDivide(binExpr, vmDt, signed) "%" -> operatorModulo(binExpr, vmDt) - "|" -> operatorOr(binExpr, vmDt) - "&" -> operatorAnd(binExpr, vmDt) - "^" -> operatorXor(binExpr, vmDt) + "|" -> operatorOr(binExpr, vmDt, true) + "&" -> operatorAnd(binExpr, vmDt, true) + "^", "xor" -> operatorXor(binExpr, vmDt) + "or" -> operatorOr(binExpr, vmDt, false) + "and" -> operatorAnd(binExpr, vmDt, false) "<<" -> operatorShiftLeft(binExpr, vmDt) ">>" -> operatorShiftRight(binExpr, vmDt, signed) "==" -> operatorEquals(binExpr, vmDt, false) @@ -695,9 +697,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } } - private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult { + private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: IRDataType, bitwise: Boolean): ExpressionCodeResult { val result = mutableListOf() - if(codeGen.options.shortCircuit && (!binExpr.left.isSimple() && !binExpr.right.isSimple())) { + if(!bitwise && codeGen.options.shortCircuit && (!binExpr.left.isSimple() && !binExpr.right.isSimple())) { // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) val leftTr = translateExpression(binExpr.left) addToResult(result, leftTr, leftTr.resultReg, -1) @@ -724,9 +726,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } } - private fun operatorOr(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult { + private fun operatorOr(binExpr: PtBinaryExpression, vmDt: IRDataType, bitwise: Boolean): ExpressionCodeResult { val result = mutableListOf() - if(codeGen.options.shortCircuit && (!binExpr.left.isSimple() && !binExpr.right.isSimple())) { + if(!bitwise && codeGen.options.shortCircuit && (!binExpr.left.isSimple() && !binExpr.right.isSimple())) { // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT val leftTr = translateExpression(binExpr.left) addToResult(result, leftTr, leftTr.resultReg, -1) diff --git a/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt index fa4c6fa22..da752016e 100644 --- a/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt +++ b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt @@ -74,16 +74,8 @@ internal class BoolRemover(val program: Program) : AstWalker() { } override fun after(expr: BinaryExpression, parent: Node): Iterable { - // convert boolean and/or/xor/not operators to bitwise equivalents. - // so that codegen only has to work with bitwise boolean operations from now on. - // note: this has to be done here and not in BeforeAsmTypecastCleaner! (code size will increase if done there...) if(expr.operator in setOf("and", "or", "xor")) { - expr.operator = when(expr.operator) { - "and" -> "&" - "or" -> "|" - "xor" -> "^" - else -> "invalid" - } + // see if any of the arguments to a logical boolean expression need type casting to bool val mods = mutableListOf() val newLeft = wrapWithBooleanCastIfNeeded(expr.left, program) val newRight = wrapWithBooleanCastIfNeeded(expr.right, program) diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index dccbbabfb..251dafb70 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -305,8 +305,8 @@ class TestOptimization: FunSpec({ ubyte a1 ubyte a2 a1 = not not a1 ; a1 = a1==0 - a1 = not a1 or not a2 ; a1 = a1==0 | a2==0 - a1 = not a1 and not a2 ; a1 = a1==0 & a2==0 + a1 = not a1 or not a2 ; a1 = a1==0 or a2==0 + a1 = not a1 and not a2 ; a1 = a1==0 and a2==0 } } """ @@ -319,8 +319,8 @@ class TestOptimization: FunSpec({ val value3 = (stmts[6] as Assignment).value as BinaryExpression value1.operator shouldBe "==" value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) - value2.operator shouldBe "|" - value3.operator shouldBe "&" + value2.operator shouldBe "or" + value3.operator shouldBe "and" } test("asmgen correctly deals with float typecasting in augmented assignment") { diff --git a/compiler/test/TestTypecasts.kt b/compiler/test/TestTypecasts.kt index 52e5fa844..3d2bdcdbc 100644 --- a/compiler/test/TestTypecasts.kt +++ b/compiler/test/TestTypecasts.kt @@ -60,7 +60,7 @@ class TestTypecasts: FunSpec({ val stmts2 = result2.compilerAst.entrypoint.statements stmts2.size shouldBe 5 val expr2 = (stmts2[3] as Assignment).value as BinaryExpression - expr2.operator shouldBe "&" + expr2.operator shouldBe "and" expr2.right shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY) } @@ -93,18 +93,18 @@ main { ubyte ub3 ub3 = 1 ubyte @shared bvalue - bvalue = ub1 ^ ub2 ^ ub3 ^ true - bvalue = (((ub1^ub2)^ub3)^(ftrue(99)!=0)) - bvalue = ((ub1&ub2)&(ftrue(99)!=0)) + bvalue = ub1 xor ub2 xor ub3 xor true + bvalue = (((ub1 xor ub2)xor ub3) xor (ftrue(99)!=0)) + bvalue = ((ub1 and ub2) and (ftrue(99)!=0)) return */ stmts.size shouldBe 11 val assignValue1 = (stmts[7] as Assignment).value as BinaryExpression val assignValue2 = (stmts[8] as Assignment).value as BinaryExpression val assignValue3 = (stmts[9] as Assignment).value as BinaryExpression - assignValue1.operator shouldBe "^" - assignValue2.operator shouldBe "^" - assignValue3.operator shouldBe "&" + assignValue1.operator shouldBe "xor" + assignValue2.operator shouldBe "xor" + assignValue3.operator shouldBe "and" val right2 = assignValue2.right as BinaryExpression val right3 = assignValue3.right as BinaryExpression right2.operator shouldBe "!=" @@ -115,24 +115,6 @@ main { right3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) } - test("simple logical with bool no typecast") { - val text=""" -main { - bool bb - - sub start() { - bb = bb and 123 - } -}""" - val result = compileText(C64Target(), true, text, writeAssembly = true)!! - val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 2 - val assignValue = (stmts[0] as Assignment).value as BinaryExpression - assignValue.left shouldBe instanceOf() - assignValue.operator shouldBe "&" - (assignValue.right as NumericLiteral).number shouldBe 1.0 - } - test("simple logical with byte instead of bool ok with typecasting") { val text=""" main { @@ -147,7 +129,7 @@ main { stmts.size shouldBe 2 val assignValue = (stmts[0] as Assignment).value as BinaryExpression assignValue.left shouldBe instanceOf() // as a result of the cast to boolean - assignValue.operator shouldBe "&" + assignValue.operator shouldBe "and" (assignValue.right as NumericLiteral).number shouldBe 1.0 } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index f7a270e02..1a6f7cac4 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,11 +3,10 @@ TODO ==== - [on branch: shortcircuit] complete McCarthy evaluation. This may also reduce code size perhaps for things like if a>4 or a<2 .... - - vm ircodegen (DONE) + - note: shortcircuit only on logical boolean expressions (and,or) not on bitwise (&,|) + - vm ircodegen (DONE!) - in 6502 codegen (see vm's ExpressionGen operatorAnd / operatorOr) -- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? Bitwise operations, etc), but only after setting the status bits is verified! - ... diff --git a/examples/test.p8 b/examples/test.p8 index f45752871..bc7a5dd52 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -47,6 +47,24 @@ main { txt.nl() @($4000) &= 22 + txt.print("\n5a:\n") + result = bool_true() or bool_false() + txt.print("\n5b:\n") + result = bool_true() and bool_false() + txt.print("\n5c:\n") + result = bool_false() and bool_true() + txt.print("\n5d:\n") + result = bool_false() xor bool_true() + + + sub bool_true() -> bool { + txt.print("bool_true\n") + return true + } + sub bool_false() -> bool { + txt.print("bool_false\n") + return false + } sub calc_a1() -> ubyte { txt.print("calc_a1\n") return a1+zero