diff --git a/codeCore/src/prog8/code/ast/AstExpressions.kt b/codeCore/src/prog8/code/ast/AstExpressions.kt index 6adfea186..aeaeff577 100644 --- a/codeCore/src/prog8/code/ast/AstExpressions.kt +++ b/codeCore/src/prog8/code/ast/AstExpressions.kt @@ -180,7 +180,13 @@ class PtFunctionCall(val name: String, } -class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) +class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) { + override fun toString(): String { + return "[PtIdentifier:$name $type $position]" + } + + fun copy() = PtIdentifier(name, type, position) +} class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) { diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 8245e7910..4720b9190 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -38,20 +38,17 @@ class PtSub( class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position) -class PtAssignment(position: Position) : PtNode(position) { +sealed interface IPtAssignment { + val children: MutableList val target: PtAssignTarget get() = children[0] as PtAssignTarget val value: PtExpression get() = children[1] as PtExpression } +class PtAssignment(position: Position) : PtNode(position), IPtAssignment -class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position) { - val target: PtAssignTarget - get() = children[0] as PtAssignTarget - val value: PtExpression - get() = children[1] as PtExpression -} +class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment class PtAssignTarget(position: Position) : PtNode(position) { @@ -95,7 +92,7 @@ class PtForLoop(position: Position) : PtNode(position) { class PtIfElse(position: Position) : PtNode(position) { - val condition: PtExpression // either PtRpn or PtBinaryExpression + val condition: PtExpression get() = children[0] as PtExpression val ifScope: PtNodeGroup get() = children[1] as PtNodeGroup diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index 296cbe660..5b9b062cf 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -79,6 +79,7 @@ val BuiltinFunctions: Map = mapOf( "reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), // cmp returns a status in the carry flag, but not a proper return value "cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), + "prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), "abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD), "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), // normal functions follow: diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 5d98a1130..b3b8b341f 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -21,13 +21,6 @@ class AsmGen6502: ICodeGeneratorBackend { options: CompilationOptions, errors: IErrorReporter ): IAssemblyProgram? { - if(options.useNewExprCode) { - // TODO("transform BinExprs?") - // errors.warn("EXPERIMENTAL NEW EXPRESSION CODEGEN IS USED. CODE SIZE+SPEED POSSIBLY SUFFERS.", Position.DUMMY) - } - - // printAst(program, true) { println(it) } - val asmgen = AsmGen6502Internal(program, symbolTable, options, errors) return asmgen.compileToAssembly() } @@ -551,28 +544,76 @@ class AsmGen6502Internal ( } private fun translate(stmt: PtIfElse) { - val condition = stmt.condition as PtBinaryExpression - requireComparisonExpression(condition) // IfStatement: condition must be of form 'x ' - if (stmt.elseScope.children.isEmpty()) { - val jump = stmt.ifScope.children.singleOrNull() - if (jump is PtJump) { - translateCompareAndJumpIfTrue(condition, jump) + val condition = stmt.condition as? PtBinaryExpression + if(condition!=null) { + require(!options.useNewExprCode) + requireComparisonExpression(condition) // IfStatement: condition must be of form 'x ' + if (stmt.elseScope.children.isEmpty()) { + val jump = stmt.ifScope.children.singleOrNull() + if (jump is PtJump) { + translateCompareAndJumpIfTrue(condition, jump) + } else { + val endLabel = makeLabel("if_end") + translateCompareOrJumpIfFalse(condition, endLabel) + translate(stmt.ifScope) + out(endLabel) + } } else { + // both true and else parts + val elseLabel = makeLabel("if_else") val endLabel = makeLabel("if_end") - translateCompareAndJumpIfFalse(condition, endLabel) + translateCompareOrJumpIfFalse(condition, elseLabel) translate(stmt.ifScope) + jmp(endLabel) + out(elseLabel) + translate(stmt.elseScope) out(endLabel) } } else { - // both true and else parts - val elseLabel = makeLabel("if_else") - val endLabel = makeLabel("if_end") - translateCompareAndJumpIfFalse(condition, elseLabel) - translate(stmt.ifScope) - jmp(endLabel) - out(elseLabel) - translate(stmt.elseScope) - out(endLabel) + // condition is a simple expression "if X" --> "if X!=0" + val zero = PtNumber(DataType.UBYTE,0.0, stmt.position) + val leftConst = stmt.condition as? PtNumber + if (stmt.elseScope.children.isEmpty()) { + val jump = stmt.ifScope.children.singleOrNull() + if (jump is PtJump) { + // jump somewhere if X!=0 + val label = when { + jump.generatedLabel!=null -> jump.generatedLabel!! + jump.identifier!=null -> asmSymbolName(jump.identifier!!) + jump.address!=null -> jump.address!!.toHex() + else -> throw AssemblyError("weird jump") + } + when(stmt.condition.type) { + in WordDatatypes -> translateWordEqualsOrJumpElsewhere(stmt.condition, zero, leftConst, zero, label) + in ByteDatatypes -> translateByteEqualsOrJumpElsewhere(stmt.condition, zero, leftConst, zero, label) + else -> throw AssemblyError("weird condition dt") + } + } else { + // skip the true block (=jump to end label) if X==0 + val endLabel = makeLabel("if_end") + when(stmt.condition.type) { + in WordDatatypes -> translateWordNotEqualsOrJumpElsewhere(stmt.condition, zero, leftConst, zero, endLabel) + in ByteDatatypes -> translateByteNotEqualsOrJumpElsewhere(stmt.condition, zero, leftConst, zero, endLabel) + else -> throw AssemblyError("weird condition dt") + } + translate(stmt.ifScope) + out(endLabel) + } + } else { + // both true and else parts + val elseLabel = makeLabel("if_else") + val endLabel = makeLabel("if_end") + when(stmt.condition.type) { + in WordDatatypes -> translateWordNotEqualsOrJumpElsewhere(stmt.condition, zero, leftConst, zero, elseLabel) + in ByteDatatypes -> translateByteNotEqualsOrJumpElsewhere(stmt.condition, zero, leftConst, zero, elseLabel) + else -> throw AssemblyError("weird condition dt") + } + translate(stmt.ifScope) + jmp(endLabel) + out(elseLabel) + translate(stmt.elseScope) + out(endLabel) + } } } @@ -740,7 +781,7 @@ $repeatLabel lda $counterVar } val isNested = parent is PtRepeatLoop - if(!isNested && !options.useNewExprCode) { + if(!isNested) { // we can re-use a counter var from the subroutine if it already has one for that datatype val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("counter") } if(existingVar!=null) { @@ -987,6 +1028,7 @@ $repeatLabel lda $counterVar val operator: String if (pointerOffsetExpr is PtBinaryExpression) { + require(!options.useNewExprCode) operator = pointerOffsetExpr.operator left = pointerOffsetExpr.left right = pointerOffsetExpr.right @@ -1132,15 +1174,16 @@ $repeatLabel lda $counterVar jump.address!=null -> jump.address!!.toHex() else -> throw AssemblyError("weird jump") } - if (rightConstVal!=null && rightConstVal.number == 0.0) - testZeroAndJump(left, invertedComparisonOperator, label) + if (rightConstVal!=null && rightConstVal.number == 0.0) { + testZeroOrJumpElsewhere(left, invertedComparisonOperator, label) + } else { val leftConstVal = left as? PtNumber - testNonzeroComparisonAndJump(left, invertedComparisonOperator, right, label, leftConstVal, rightConstVal) + testNonzeroComparisonOrJumpElsewhere(left, invertedComparisonOperator, right, label, leftConstVal, rightConstVal) } } - private fun translateCompareAndJumpIfFalse(expr: PtBinaryExpression, jumpIfFalseLabel: String) { + private fun translateCompareOrJumpIfFalse(expr: PtBinaryExpression, jumpIfFalseLabel: String) { val left = expr.left val right = expr.right val operator = expr.operator @@ -1148,19 +1191,19 @@ $repeatLabel lda $counterVar val rightConstVal = right as? PtNumber if (rightConstVal!=null && rightConstVal.number == 0.0) - testZeroAndJump(left, operator, jumpIfFalseLabel) + testZeroOrJumpElsewhere(left, operator, jumpIfFalseLabel) else - testNonzeroComparisonAndJump(left, operator, right, jumpIfFalseLabel, leftConstVal, rightConstVal) + testNonzeroComparisonOrJumpElsewhere(left, operator, right, jumpIfFalseLabel, leftConstVal, rightConstVal) } - private fun testZeroAndJump( + private fun testZeroOrJumpElsewhere( left: PtExpression, operator: String, jumpIfFalseLabel: String ) { val dt = left.type if(dt in IntegerDatatypes && left is PtIdentifier) - return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel) + return testVariableZeroOrJumpElsewhere(left, dt, operator, jumpIfFalseLabel) when(dt) { DataType.BOOL, DataType.UBYTE, DataType.UWORD -> { @@ -1246,7 +1289,7 @@ $repeatLabel lda $counterVar } } - private fun testVariableZeroAndJump(variable: PtIdentifier, dt: DataType, operator: String, jumpIfFalseLabel: String) { + private fun testVariableZeroOrJumpElsewhere(variable: PtIdentifier, dt: DataType, operator: String, jumpIfFalseLabel: String) { // optimized code if the expression is just an identifier (variable) val varname = asmVariableName(variable) when(dt) { @@ -1306,7 +1349,7 @@ $repeatLabel lda $counterVar } } - internal fun testNonzeroComparisonAndJump( + internal fun testNonzeroComparisonOrJumpElsewhere( left: PtExpression, operator: String, right: PtExpression, @@ -1319,70 +1362,70 @@ $repeatLabel lda $counterVar when (operator) { "==" -> { when (dt) { - in ByteDatatypes -> translateByteEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - in WordDatatypes -> translateWordEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.FLOAT -> translateFloatEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringEqualsJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) + in ByteDatatypes -> translateByteEqualsOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + in WordDatatypes -> translateWordEqualsOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.FLOAT -> translateFloatEqualsOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.STR -> translateStringEqualsOrJumpElsewhere(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } "!=" -> { when (dt) { - in ByteDatatypes -> translateByteNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - in WordDatatypes -> translateWordNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.FLOAT -> translateFloatNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringNotEqualsJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) + in ByteDatatypes -> translateByteNotEqualsOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + in WordDatatypes -> translateWordNotEqualsOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.FLOAT -> translateFloatNotEqualsOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.STR -> translateStringNotEqualsOrJumpElsewhere(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } "<" -> { when(dt) { - DataType.UBYTE -> translateUbyteLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.BYTE -> translateByteLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.UWORD -> translateUwordLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.WORD -> translateWordLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.FLOAT -> translateFloatLessJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringLessJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) + DataType.UBYTE -> translateUbyteLessOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.BYTE -> translateByteLessOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.UWORD -> translateUwordLessOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.WORD -> translateWordLessOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.FLOAT -> translateFloatLessOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.STR -> translateStringLessOrJumpElsewhere(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } "<=" -> { when(dt) { - DataType.UBYTE -> translateUbyteLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.BYTE -> translateByteLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.UWORD -> translateUwordLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.WORD -> translateWordLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.FLOAT -> translateFloatLessOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringLessOrEqualJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) + DataType.UBYTE -> translateUbyteLessOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.BYTE -> translateByteLessOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.UWORD -> translateUwordLessOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.WORD -> translateWordLessOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.FLOAT -> translateFloatLessOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.STR -> translateStringLessOrEqualOrJumpElsewhere(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } ">" -> { when(dt) { - DataType.UBYTE -> translateUbyteGreaterJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.BYTE -> translateByteGreaterJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.UWORD -> translateUwordGreaterJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.WORD -> translateWordGreaterJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.FLOAT -> translateFloatGreaterJump(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringGreaterJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) + DataType.UBYTE -> translateUbyteGreaterOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.BYTE -> translateByteGreaterOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.UWORD -> translateUwordGreaterOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.WORD -> translateWordGreaterOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.FLOAT -> translateFloatGreaterOrJumpElsewhere(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel) + DataType.STR -> translateStringGreaterOrJumpElsewhere(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } ">=" -> { when(dt) { - DataType.UBYTE -> translateUbyteGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.BYTE -> translateByteGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.UWORD -> translateUwordGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.WORD -> translateWordGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.FLOAT -> translateFloatGreaterOrEqualJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) - DataType.STR -> translateStringGreaterOrEqualJump(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) + DataType.UBYTE -> translateUbyteGreaterOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.BYTE -> translateByteGreaterOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.UWORD -> translateUwordGreaterOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.WORD -> translateWordGreaterOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.FLOAT -> translateFloatGreaterOrEqualOrJumpElsewhere(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel) + DataType.STR -> translateStringGreaterOrEqualOrJumpElsewhere(left as PtIdentifier, right as PtIdentifier, jumpIfFalseLabel) else -> throw AssemblyError("weird operand datatype") } } } } - private fun translateFloatLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateFloatLessOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } @@ -1427,7 +1470,7 @@ $repeatLabel lda $counterVar } } - private fun translateFloatLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateFloatLessOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } @@ -1472,7 +1515,7 @@ $repeatLabel lda $counterVar } } - private fun translateFloatGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateFloatGreaterOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } @@ -1517,7 +1560,7 @@ $repeatLabel lda $counterVar } } - private fun translateFloatGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateFloatGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(leftConstVal!=null && rightConstVal!=null) { throw AssemblyError("const-compare should have been optimized away") } @@ -1562,7 +1605,7 @@ $repeatLabel lda $counterVar } } - private fun translateUbyteLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUbyteLessOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | bcs $jumpIfFalseLabel") @@ -1607,7 +1650,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateByteLessOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" @@ -1648,7 +1691,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUwordLessOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -1692,7 +1735,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_less_uw | beq $jumpIfFalseLabel") } - private fun translateWordLessJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateWordLessOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -1738,7 +1781,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel") } - private fun translateUbyteGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUbyteGreaterOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(""" @@ -1784,7 +1827,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateByteGreaterOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" @@ -1827,7 +1870,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUwordGreaterOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -1877,7 +1920,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_W2+1", "P8ZP_SCRATCH_W2") } - private fun translateWordGreaterJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateWordGreaterOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCmpOperand: String, lsbCmpOperand: String) { out(""" @@ -1928,7 +1971,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel") } - private fun translateUbyteLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUbyteLessOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(""" @@ -1975,7 +2018,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateByteLessOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" clc @@ -2018,7 +2061,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUwordLessOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -2070,7 +2113,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel") } - private fun translateWordLessOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateWordLessOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(leftName: String) { out(""" @@ -2125,7 +2168,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel") } - private fun translateUbyteGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUbyteGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | bcc $jumpIfFalseLabel") @@ -2168,7 +2211,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateByteGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateByteGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(sbcOperand: String) { out(""" sec @@ -2208,7 +2251,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateUwordGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateUwordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -2251,7 +2294,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel") } - private fun translateWordGreaterOrEqualJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateWordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(msbCpyOperand: String, lsbCmpOperand: String) { out(""" @@ -2297,7 +2340,7 @@ $repeatLabel lda $counterVar return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel") } - private fun translateByteEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateByteEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | bne $jumpIfFalseLabel") } @@ -2342,7 +2385,7 @@ $repeatLabel lda $counterVar out(" cmp P8ZP_SCRATCH_B1 | bne $jumpIfFalseLabel") } - private fun translateByteNotEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateByteNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { fun code(cmpOperand: String) { out(" cmp $cmpOperand | beq $jumpIfFalseLabel") @@ -2388,7 +2431,7 @@ $repeatLabel lda $counterVar return code("P8ZP_SCRATCH_B1") } - private fun translateWordEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateWordEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { if(rightConstVal!=leftConstVal) @@ -2472,7 +2515,7 @@ $repeatLabel lda $counterVar } - private fun translateWordNotEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateWordNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { @@ -2560,7 +2603,7 @@ $repeatLabel lda $counterVar } - private fun translateFloatEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateFloatEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { if(rightConstVal!=leftConstVal) @@ -2644,7 +2687,7 @@ $repeatLabel lda $counterVar } } - private fun translateFloatNotEqualsJump(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { + private fun translateFloatNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { if(rightConstVal!=null) { if(leftConstVal!=null) { if(rightConstVal==leftConstVal) @@ -2729,7 +2772,7 @@ $repeatLabel lda $counterVar } } - private fun translateStringEqualsJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { + private fun translateStringEqualsOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2740,11 +2783,10 @@ $repeatLabel lda $counterVar lda #<$leftNam ldy #>$leftNam jsr prog8_lib.strcmp_mem - cmp #0 bne $jumpIfFalseLabel""") } - private fun translateStringNotEqualsJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { + private fun translateStringNotEqualsOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2755,11 +2797,10 @@ $repeatLabel lda $counterVar lda #<$leftNam ldy #>$leftNam jsr prog8_lib.strcmp_mem - cmp #0 beq $jumpIfFalseLabel""") } - private fun translateStringLessJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { + private fun translateStringLessOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2773,7 +2814,7 @@ $repeatLabel lda $counterVar bpl $jumpIfFalseLabel""") } - private fun translateStringGreaterJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { + private fun translateStringGreaterOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2788,7 +2829,7 @@ $repeatLabel lda $counterVar bmi $jumpIfFalseLabel""") } - private fun translateStringLessOrEqualJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { + private fun translateStringLessOrEqualOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2804,7 +2845,7 @@ $repeatLabel lda $counterVar +""") } - private fun translateStringGreaterOrEqualJump(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { + private fun translateStringGreaterOrEqualOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { val leftNam = asmVariableName(left) val rightNam = asmVariableName(right) out(""" @@ -2853,6 +2894,7 @@ $repeatLabel lda $counterVar out(" sta P8ESTACK_LO,x | dex") } is PtBinaryExpression -> { + require(!options.useNewExprCode) val addrExpr = expr.address as PtBinaryExpression if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { if(pushResultOnEstack) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 4433537a6..8df55cfff 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -72,6 +72,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, "rrestorex" -> funcRrestoreX() "cmp" -> funcCmp(fcall) "callfar" -> funcCallFar(fcall) + "prog8_lib_stringcompare" -> funcStringCompare(fcall, resultToStack) else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}") } @@ -111,6 +112,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, sty $remainderVar+1""") } + private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) { + assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", DataType.UWORD) + assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, false) + asmgen.out(" jsr prog8_lib.strcmp_mem") + if(resultToStack) + asmgen.out(" sta P8ESTACK_LO,x | dex") + } + private fun funcRsave() { if (asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out(""" @@ -697,6 +706,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } } is PtBinaryExpression -> { + require(!asmgen.options.useNewExprCode) val result = asmgen.pointerViaIndexRegisterPossible(addrExpr) val pointer = result?.first as? PtIdentifier if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) { @@ -759,6 +769,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } else fallback() } is PtBinaryExpression -> { + require(!asmgen.options.useNewExprCode) val result = asmgen.pointerViaIndexRegisterPossible(addrExpr) val pointer = result?.first as? PtIdentifier if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt index 0e80ba5cc..1b74cb6aa 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt @@ -245,6 +245,8 @@ internal class ExpressionsAsmGen(private val program: PtProgram, } private fun translateExpression(expr: PtBinaryExpression) { + require(!asmgen.options.useNewExprCode) + // Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED! if(translateSomewhatOptimized(expr.left, expr.operator, expr.right)) return diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 493c49433..8b56e594b 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -147,6 +147,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignMemoryByte(assign.target, null, value.address as PtIdentifier) } is PtBinaryExpression -> { + require(!asmgen.options.useNewExprCode) val addrExpr = value.address as PtBinaryExpression if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { assignRegisterByte(assign.target, CpuRegister.A) @@ -301,6 +302,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignRegisterByte(assign.target, CpuRegister.A) } is PtBinaryExpression -> { + require(!asmgen.options.useNewExprCode) if(!attemptAssignOptimizedBinexpr(value, assign)) { // All remaining binary expressions just evaluate via the stack for now. // (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here, @@ -346,6 +348,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { + require(!asmgen.options.useNewExprCode) if(expr.operator in ComparisonOperators) { if(expr.right.asConstInteger() == 0) { if(expr.operator == "==" || expr.operator=="!=") { @@ -728,6 +731,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { + require(!asmgen.options.useNewExprCode) when (expr.operator) { "==" -> { when(val dt = expr.left.type) { @@ -907,6 +911,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignMemoryByteIntoWord(target, null, value.address as PtIdentifier) } is PtBinaryExpression -> { + require(!asmgen.options.useNewExprCode) val addrExpr = value.address as PtBinaryExpression if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { asmgen.out(" ldy #0") @@ -2519,7 +2524,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - private fun assignConstantFloat(target: AsmAssignTarget, float: Double) { + internal fun assignConstantFloat(target: AsmAssignTarget, float: Double) { if (float == 0.0) { // optimized case for float zero when(target.kind) { @@ -2829,6 +2834,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, asmgen.storeAIntoPointerVar(addressExpr) } addressExpr is PtBinaryExpression -> { + require(!asmgen.options.useNewExprCode) if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true)) storeViaExprEval() } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index fed3661b9..e7284e706 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -668,43 +668,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } "<" -> { asmgen.assignExpressionToRegister(value, RegisterOrPair.A) - asmgen.out(""" - cmp $name - bcc + - beq + - lda #1 - bne ++ -+ lda #0 -+ sta $name""") + if(dt==DataType.UBYTE) + TODO("ubyte <") + else + TODO("byte <") } "<=" -> { asmgen.assignExpressionToRegister(value, RegisterOrPair.A) - asmgen.out(""" - cmp $name - lda #0 - adc #0 - sta $name""") + if(dt==DataType.UBYTE) + TODO("ubyte <=") + else + TODO("byte <=") } ">" -> { asmgen.assignExpressionToRegister(value, RegisterOrPair.A) - asmgen.out(""" - cmp $name - bcc + - lda #0 - beq ++ -+ lda #1 -+ sta $name""") + if(dt==DataType.UBYTE) + TODO("ubyte >") + else + TODO("byte >") } ">=" -> { asmgen.assignExpressionToRegister(value, RegisterOrPair.A) - asmgen.out(""" - cmp $name - bcc + - beq + - lda #0 - beq ++ -+ lda #1 -+ sta $name""") + if(dt==DataType.UBYTE) + TODO("ubyte >=") + else + TODO("byte >=") } else -> throw AssemblyError("invalid operator for in-place modification $operator") } @@ -782,41 +770,106 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, + sta $name""") } "<" -> { - asmgen.out(""" - lda $name - cmp $otherName - bcs + - lda #1 - bne ++ -+ lda #0 -+ sta $name""") + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda $name + cmp $otherName + bcc + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } + else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + sec + sbc $otherName + bvc + + eor #$80 ++ bmi + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } "<=" -> { - asmgen.out(""" - lda $otherName - cmp $name - lda #0 - adc #0 - sta $name""") + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda $otherName + cmp $name + bcs + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + clc + sbc $otherName + bvc + + eor #$80 ++ bmi + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } ">" -> { - asmgen.out(""" - lda $otherName - cmp $name - bcc + - lda #0 - beq ++ -+ lda #1 -+ sta $name""") + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda $name + cmp $otherName + beq + + bcs ++ ++ lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + clc + sbc $otherName + bvc + + eor #$80 ++ bpl + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } ">=" -> { - asmgen.out(""" - lda $name - cmp $otherName - lda #0 - adc #0 - sta $name""") - } + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda $name + cmp $otherName + bcs + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + sec + sbc $otherName + bvc + + eor #$80 ++ bpl + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } else -> throw AssemblyError("invalid operator for in-place modification $operator") } } @@ -908,40 +961,106 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, + sta $name""") } "<" -> { - asmgen.out(""" - lda $name - cmp #$value - bcs + - lda #1 - bne ++ -+ lda #0 -+ sta $name""") + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda $name + cmp #$value + bcc + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } + else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + sec + sbc #$value + bvc + + eor #$80 ++ bmi + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } "<=" -> { - asmgen.out(""" - lda #$value - cmp $name - lda #0 - adc #0 - sta $name""") + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda #$value + cmp $name + bcs + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + clc + sbc #$value + bvc + + eor #$80 ++ bmi + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } ">" -> { - asmgen.out(""" - lda #$value - cmp $name - bcc + - lda #0 - beq ++ -+ lda #1 -+ sta $name""") + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda $name + cmp #$value + beq + + bcs ++ ++ lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + clc + sbc #$value + bvc + + eor #$80 ++ bpl + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } ">=" -> { - asmgen.out(""" - lda $name - cmp #$value - lda #0 - adc #0 - sta $name""") + if(dt==DataType.UBYTE) { + asmgen.out(""" + lda $name + cmp #$value + bcs + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } else { + // see http://www.6502.org/tutorials/compare_beyond.html + asmgen.out(""" + lda $name + sec + sbc #$value + bvc + + eor #$80 ++ bpl + + lda #0 + beq ++ ++ lda #1 ++ sta $name""") + } } else -> throw AssemblyError("invalid operator for in-place modification $operator") } @@ -1319,8 +1438,174 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, lda #0 sta $name+1""") } - // pretty uncommon, who's going to assign a comparison boolean expresion to a word var?: - "<", "<=", ">", ">=" -> TODO("word-litval-to-var comparisons") + "<" -> { + if(dt==DataType.UWORD) { + asmgen.out(""" + lda $name+1 + cmp #>$value + bcc ++ + bne + + lda $name + cmp #<$value + bcc ++ ++ lda #0 ; false + sta $name + sta $name+1 + beq ++ ++ lda #1 ; true + sta $name + lda #0 + sta $name+1 ++""") + } + else { + // signed + asmgen.out(""" + lda $name + cmp #<$value + lda $name+1 + sbc #>$value + bvc + + eor #$80 ++ bmi + + lda #0 + sta $name + sta $name+1 + beq ++ ++ lda #1 ; true + sta $name + lda #0 + sta $name+1 ++""") + } + } + "<=" -> { + if(dt==DataType.UWORD) { + asmgen.out(""" + lda $name+1 + cmp #>$value + beq + + bcc ++ +- lda #0 ; false + sta $name + sta $name+1 + beq +++ ++ lda $name ; next + cmp #<$value + bcc + + bne - ++ lda #1 ; true + sta $name + lda #0 + sta $name+1 ++""") + } + else { + // signed + asmgen.out(""" + lda #<$value + cmp $name + lda #>$value + sbc $name+1 + bvc + + eor #$80 ++ bpl + + lda #0 + sta $name + sta $name+1 + beq ++ ++ lda #1 + sta $name + lda #0 + sta $name+1 ++""") + } + } + ">" -> { + // word > value --> value < word + if(dt==DataType.UWORD) { + asmgen.out(""" + lda #>$value + cmp $name+1 + bcc ++ + bne + + lda #<$value + cmp $name + bcc ++ ++ lda #0 ; false + sta $name + sta $name+1 + beq ++ ++ lda #1 ; true + sta $name + lda #0 + sta $name+1 ++""") + } + else { + // signed + asmgen.out(""" + lda #<$value + cmp $name + lda #>$value + sbc $name+1 + bvc + + eor #$80 ++ bmi + + lda #0 + sta $name + sta $name+1 + beq ++ ++ lda #1 ; true + sta $name + lda #0 + sta $name+1 ++""") + } + } + ">=" -> { + // word >= value --> value <= word + if(dt==DataType.UWORD) { + asmgen.out(""" + lda #>$value + cmp $name+1 + beq + + bcc ++ +- lda #0 ; false + sta $name + sta $name+1 + beq +++ ++ lda #<$value ; next + cmp $name + bcc + + bne - ++ lda #1 ; true + sta $name + lda #0 + sta $name+1 ++""") + } + else { + // signed + asmgen.out(""" + lda $name + cmp #<$value + lda $name+1 + sbc #>$value + bvc + + eor #$80 ++ bpl + + lda #0 + sta $name + sta $name+1 + beq ++ ++ lda #1 + sta $name + lda #0 + sta $name+1 ++""") + } + } else -> throw AssemblyError("invalid operator for in-place modification $operator") } } @@ -1650,33 +1935,63 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, sta $name+1""") } "<=" -> { - val compareRoutine = if(dt==DataType.UWORD) "reg_lesseq_uw" else "reg_lesseq_w" - asmgen.out(""" + if(dt==DataType.UWORD) { + asmgen.out(""" lda $otherName ldy $otherName+1 sta P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_W2+1 lda $name ldy $name+1 - jsr prog8_lib.$compareRoutine + jsr prog8_lib.reg_lesseq_uw sta $name lda #0 sta $name+1""") + } else { + // note: reg_lesseq_w routine takes the arguments in reverse order + asmgen.out(""" + lda $name + ldy $name+1 + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + lda $otherName + ldy $otherName+1 + jsr prog8_lib.reg_lesseq_w + sta $name + lda #0 + sta $name+1""") + } } ">=" -> { // a>=b --> b<=a - val compareRoutine = if(dt==DataType.UWORD) "reg_lesseq_uw" else "reg_lesseq_w" - asmgen.out(""" + if(dt==DataType.UWORD) { + asmgen.out(""" lda $name ldy $name+1 sta P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_W2+1 lda $otherName ldy $otherName+1 - jsr prog8_lib.$compareRoutine + jsr prog8_lib.reg_lesseq_uw sta $name lda #0 - sta $name+1""") + sta $name+1""" + ) + } else { + // note: reg_lesseq_w routine takes the arguments in reverse order + asmgen.out(""" + lda $otherName + ldy $otherName+1 + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + lda $name + ldy $name+1 + jsr prog8_lib.reg_lesseq_w + sta $name + lda #0 + sta $name+1""" + ) + } } else -> throw AssemblyError("invalid operator for in-place modification $operator") } @@ -2011,9 +2326,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, """) } // pretty uncommon, who's going to assign a comparison boolean expresion to a float var: - "==", "!=", "<", "<=", ">", ">=" -> TODO("float-value-to-var comparisons") + "==" -> TODO("float-value-to-var comparison ==") + "!=" -> TODO("float-value-to-var comparison !=") + "<", "<=", ">", ">=" -> TODO("float-value-to-var comparisons") else -> throw AssemblyError("invalid operator for in-place float modification $operator") } + // store Fac1 back into memory asmgen.out(""" ldx #<$name ldy #>$name @@ -2066,7 +2384,67 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, """) } // pretty uncommon, who's going to assign a comparison boolean expresion to a float var: - "==", "!=", "<", "<=", ">", ">=" -> TODO("float-var-to-var comparisons") + "==" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$otherName + ldy #>$otherName + jsr floats.var_fac1_notequal_f + eor #1 + jsr floats.FREADSA""") + } + "!=" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$otherName + ldy #>$otherName + jsr floats.var_fac1_notequal_f + jsr floats.FREADSA""") + } + "<" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$otherName + ldy #>$otherName + jsr floats.var_fac1_less_f + jsr floats.FREADSA""") + } + "<=" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$otherName + ldy #>$otherName + jsr floats.var_fac1_lesseq_f + jsr floats.FREADSA""") + } + ">" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$otherName + ldy #>$otherName + jsr floats.var_fac1_greater_f + jsr floats.FREADSA""") + } + ">=" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$otherName + ldy #>$otherName + jsr floats.var_fac1_greatereq_f + jsr floats.FREADSA""") + } else -> throw AssemblyError("invalid operator for in-place float modification $operator") } // store Fac1 back into memory @@ -2129,8 +2507,67 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, jsr floats.FDIV """) } - // pretty uncommon, who's going to assign a comparison boolean expresion to a float var: - "==", "!=", "<", "<=", ">", ">=" -> TODO("float-litval-to-var comparisons") + "==" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$constValueName + ldy #>$constValueName + jsr floats.var_fac1_notequal_f + eor #1 + jsr floats.FREADSA""") + } + "!=" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$constValueName + ldy #>$constValueName + jsr floats.var_fac1_notequal_f + jsr floats.FREADSA""") + } + "<" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$constValueName + ldy #>$constValueName + jsr floats.var_fac1_less_f + jsr floats.FREADSA""") + } + "<=" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$constValueName + ldy #>$constValueName + jsr floats.var_fac1_lesseq_f + jsr floats.FREADSA""") + } + ">" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$constValueName + ldy #>$constValueName + jsr floats.var_fac1_greater_f + jsr floats.FREADSA""") + } + ">=" -> { + asmgen.out(""" + lda #<$name + ldy #>$name + jsr floats.MOVFM + lda #<$constValueName + ldy #>$constValueName + jsr floats.var_fac1_greatereq_f + jsr floats.FREADSA""") + } else -> throw AssemblyError("invalid operator for in-place float modification $operator") } // store Fac1 back into memory diff --git a/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt index a2782af40..2bf4c82b6 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/ExperiCodeGen.kt @@ -14,11 +14,6 @@ class ExperiCodeGen: ICodeGeneratorBackend { errors: IErrorReporter ): IAssemblyProgram? { - if(options.useNewExprCode) { - // TODO("transform BinExprs?") - // errors.warn("EXPERIMENTAL NEW EXPRESSION CODEGEN IS USED. CODE SIZE+SPEED POSSIBLY SUFFERS.", Position.DUMMY) - } - // you could write a code generator directly on the PtProgram AST, // but you can also use the Intermediate Representation to build a codegen on: val irCodeGen = IRCodeGen(program, symbolTable, options, errors) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index f76fe32dd..6fc9c81f8 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -93,7 +93,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express } else { require(origAssign.operator.endsWith('=')) if(codeGen.options.useNewExprCode) { - TODO("use something else than a BinExpr?") + TODO("use something else than a BinExpr") } else { value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position) val left: PtExpression = origAssign.target.children.single() as PtExpression @@ -268,7 +268,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express } else { val mult : PtExpression if(codeGen.options.useNewExprCode) { - TODO("use something else than a BinExpr?") + TODO("use something else than a BinExpr") } else { mult = PtBinaryExpression("*", DataType.UBYTE, array.position) mult.children += array.index diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index e6385cdac..044f46137 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -43,6 +43,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "ror" -> funcRolRor(Opcode.ROXR, call) "rol2" -> funcRolRor(Opcode.ROL, call) "ror2" -> funcRolRor(Opcode.ROR, call) + "prog8_lib_stringcompare" -> funcStringCompare(call) else -> throw AssemblyError("missing builtinfunc for ${call.name}") } } @@ -68,6 +69,18 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe return ExpressionCodeResult(result, type, -1, -1) } + private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult { + val result = mutableListOf() + val left = exprGen.translateExpression(call.args[0]) + val right = exprGen.translateExpression(call.args[1]) + val targetReg = codeGen.registers.nextFree() + addToResult(result, left, 65500, -1) + addToResult(result, right, 65501, -1) + addInstr(result, IRInstruction(Opcode.SYSCALL, value=IMSyscall.COMPARE_STRINGS.number), null) + addInstr(result, IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=targetReg, reg2=0), null) + return ExpressionCodeResult(result, IRDataType.BYTE, targetReg, -1) + } + private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val leftTr = exprGen.translateExpression(call.args[0]) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index edfa803fd..177b79486 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -320,6 +320,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult { + require(!codeGen.options.useNewExprCode) val vmDt = codeGen.irType(binExpr.left.type) val signed = binExpr.left.type in SignedDatatypes return when(binExpr.operator) { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 80b89036b..fa1ccf7db 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -899,6 +899,7 @@ class IRCodeGen( val goto = ifElse.ifScope.children.firstOrNull() as? PtJump when (condition) { is PtBinaryExpression -> { + require(!options.useNewExprCode) if(condition.operator !in ComparisonOperators) throw AssemblyError("if condition should only be a binary comparison expression") diff --git a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt index 5b1995599..0718ae7a6 100644 --- a/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/vm/VmCodeGen.kt @@ -14,13 +14,6 @@ class VmCodeGen: ICodeGeneratorBackend { options: CompilationOptions, errors: IErrorReporter ): IAssemblyProgram? { - - if(options.useNewExprCode) { - // TODO("transform BinExprs?") - // errors.warn("EXPERIMENTAL NEW EXPRESSION CODEGEN IS USED. CODE SIZE+SPEED POSSIBLY SUFFERS.", Position.DUMMY) - } - - val irCodeGen = IRCodeGen(program, symbolTable, options, errors) val irProgram = irCodeGen.generate() return VmAssemblyProgram(irProgram.name, irProgram) diff --git a/compiler/res/prog8lib/c128/floats.asm b/compiler/res/prog8lib/c128/floats.asm index 92f6afc0f..cd60909d9 100644 --- a/compiler/res/prog8lib/c128/floats.asm +++ b/compiler/res/prog8lib/c128/floats.asm @@ -251,7 +251,7 @@ pop_float_fac1 .proc .pend copy_float .proc - ; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1, + ; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_W1, ; into the 5 bytes pointed to by A/Y. Clobbers A,Y. sta _target+1 sty _target+2 diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index d7d45b277..da67db0c2 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -4,11 +4,12 @@ import com.github.michaelbull.result.onFailure import prog8.ast.IBuiltinFunctions import prog8.ast.Program import prog8.ast.base.AstException +import prog8.ast.base.FatalAstException import prog8.ast.expressions.Expression import prog8.ast.expressions.NumericLiteral import prog8.ast.statements.Directive import prog8.code.SymbolTableMaker -import prog8.code.ast.PtProgram +import prog8.code.ast.* import prog8.code.core.* import prog8.code.target.* import prog8.codegen.vm.VmCodeGen @@ -409,6 +410,11 @@ private fun createAssemblyAndAssemble(program: PtProgram, else throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}") + if(compilerOptions.useNewExprCode) + transformNewExpressions(program) + + // printAst(program, true) { println(it) } + val stMaker = SymbolTableMaker(program, compilerOptions) val symbolTable = stMaker.make() val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors) @@ -420,3 +426,188 @@ private fun createAssemblyAndAssemble(program: PtProgram, false } } + +private fun transformNewExpressions(program: PtProgram) { + val newVariables = mutableMapOf>() + var countByteVars = 0 + var countWordVars = 0 + var countFloatVars = 0 + // TODO: find a reliable way to reuse the temp vars across expressions + + fun getExprVar(type: DataType, pos: Position, scope: PtSub): PtIdentifier { + val count = when(type) { + in ByteDatatypes -> { + countByteVars++ + countByteVars + } + in WordDatatypes -> { + countWordVars++ + countWordVars + } + DataType.FLOAT -> { + countFloatVars++ + countFloatVars + } + else -> throw FatalAstException("weird dt") + } + val name = "p8p_exprvar_${count}_${type.toString().lowercase()}" + var subVars = newVariables[scope] + if(subVars==null) { + subVars = mutableListOf() + newVariables[scope] = subVars + } + if(subVars.all { it.name!=name }) { + subVars.add(PtVariable(name, type, ZeropageWish.DONTCARE, null, null, pos)) + } + return PtIdentifier("${scope.scopedName}.$name", type, pos) + } + + fun transformExpr(expr: PtBinaryExpression): Pair> { + // depth first process the expression tree + val scope = expr.definingSub()!! + val assignments = mutableListOf() + + fun transformOperand(node: PtExpression): PtNode { + return when(node) { + is PtNumber, is PtIdentifier, is PtArray, is PtString, is PtMachineRegister -> node + is PtBinaryExpression -> { + val (replacement, subAssigns) = transformExpr(node) + assignments.addAll(subAssigns) + replacement + } + else -> { + val variable = getExprVar(node.type, node.position, scope) + val assign = PtAssignment(node.position) + val target = PtAssignTarget(variable.position) + target.add(variable) + assign.add(target) + assign.add(node) + assignments.add(assign) + variable + } + } + } + + val newLeft = transformOperand(expr.left) + val newRight = transformOperand(expr.right) + + // process the binexpr + + val resultVar = + if(expr.type == expr.left.type) { + getExprVar(expr.type, expr.position, scope) + } else { + if(expr.operator in ComparisonOperators && expr.type in ByteDatatypes) { + // this is very common and should be dealth with correctly; byte==0, word>42 + val varType = if(expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type + getExprVar(varType, expr.position, scope) + } + else if(expr.left.type in PassByReferenceDatatypes && expr.type==DataType.UBYTE) { + // this is common and should be dealth with correctly; for instance "name"=="john" + val varType = if (expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type + getExprVar(varType, expr.position, scope) + } else if(expr.left.type equalsSize expr.type) { + getExprVar(expr.type, expr.position, scope) + } else { + TODO("expression type differs from left operand type! got ${expr.left.type} expected ${expr.type} ${expr.position}") + } + } + + if(resultVar.name!=(newLeft as? PtIdentifier)?.name) { + // resultvar = left + val assign1 = PtAssignment(newLeft.position) + val target1 = PtAssignTarget(resultVar.position) + target1.add(resultVar) + assign1.add(target1) + assign1.add(newLeft) + assignments.add(assign1) + } + // resultvar {oper}= right + val operator = if(expr.operator in ComparisonOperators) expr.operator else expr.operator+'=' + val assign2 = PtAugmentedAssign(operator, newRight.position) + val target2 = PtAssignTarget(resultVar.position) + target2.add(resultVar.copy()) + assign2.add(target2) + assign2.add(newRight) + assignments.add(assign2) + return Pair(resultVar, assignments) + } + + fun isProperStatement(node: PtNode): Boolean { + return when(node) { + is PtAssignment -> true + is PtAugmentedAssign -> true + is PtBreakpoint -> true + is PtConditionalBranch -> true + is PtForLoop -> true + is PtIfElse -> true + is PtIncludeBinary -> true + is PtInlineAssembly -> true + is PtJump -> true + is PtAsmSub -> true + is PtLabel -> true + is PtSub -> true + is PtVariable -> true + is PtNop -> true + is PtPostIncrDecr -> true + is PtRepeatLoop -> true + is PtReturn -> true + is PtWhen -> true + is PtBuiltinFunctionCall -> node.void + is PtFunctionCall -> node.void + else -> false + } + } + + fun transform(node: PtNode, parent: PtNode) { + if(node is PtBinaryExpression) { + node.children.toTypedArray().forEach { + transform(it, node) + } + val (rep, assignments) = transformExpr(node) + var replacement = rep + if(!(rep.type equalsSize node.type)) { + if(rep.type in NumericDatatypes && node.type in ByteDatatypes) { + replacement = PtTypeCast(node.type, node.position) + replacement.add(rep) + } else + TODO("cast replacement type ${rep.type} -> ${node.type}") + } + var idx = parent.children.indexOf(node) + parent.children[idx] = replacement + replacement.parent = parent + // find the statement above which we should insert the assignments + var stmt = node + while(!isProperStatement(stmt)) + stmt = stmt.parent + idx = stmt.parent.children.indexOf(stmt) + assignments.reversed().forEach { + stmt.parent.add(idx, it as PtNode) + } + } else { + node.children.toTypedArray().forEach { child -> transform(child, node) } + } + } + + program.allBlocks().forEach { block -> + block.children.toTypedArray().forEach { + transform(it, block) + } + } + + // add the new variables + newVariables.forEach { (sub, vars) -> + vars.forEach { + sub.add(0, it) + } + } + + // extra check to see that all PtBinaryExpressions have been transformed + fun binExprCheck(node: PtNode) { + if(node is PtBinaryExpression) + throw IllegalArgumentException("still got binexpr $node ${node.position}") + node.children.forEach { binExprCheck(it) } + } + binExprCheck(program) +} + diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index e9b0ee8d5..c0ee4739c 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -958,7 +958,11 @@ internal class AstChecker(private val program: Program, } } - if(expr.operator !in ComparisonOperators) { + if(expr.operator in ComparisonOperators) { + if(leftDt!=rightDt && !(leftDt in ByteDatatypes && rightDt in ByteDatatypes)) { + throw FatalAstException("got comparison with different operand types: $leftDt ${expr.operator} $rightDt ${expr.position}") + } + } else { if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) { // str+str and str*number have already been const evaluated before we get here. errors.err("no computational or logical expressions with strings or arrays are possible", expr.position) diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index bcbc62e23..79dca48b1 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -36,6 +36,19 @@ internal class BeforeAsmAstChanger(val program: Program, return noModifications } + override fun after(expr: BinaryExpression, parent: Node): Iterable { + if(expr.operator in ComparisonOperators && expr.left.inferType(program) istype DataType.STR && expr.right.inferType(program) istype DataType.STR) { + // replace string comparison expressions with calls to string.compare() + val stringCompare = BuiltinFunctionCall( + IdentifierReference(listOf("prog8_lib_stringcompare"), expr.position), + mutableListOf(expr.left.copy(), expr.right.copy()), expr.position) + val zero = NumericLiteral.optimalInteger(0, expr.position) + val comparison = BinaryExpression(stringCompare, expr.operator, zero, expr.position) + return listOf(IAstModification.ReplaceNode(expr, comparison, parent)) + } + return noModifications + } + override fun after(decl: VarDecl, parent: Node): Iterable { if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes) throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl") diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index 5ed3c5578..7fa098bc1 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -79,7 +79,7 @@ if CONDITION==0 while CONDITION { STUFF } ==> _whileloop: - if CONDITION==0 goto _after + if INVERTED-CONDITION goto _after STUFF goto _whileloop _after: diff --git a/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt b/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt index d133fd0cc..4afe580b9 100644 --- a/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt @@ -109,6 +109,8 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val if(binExpr==null || binExpr.operator !in ComparisonOperators) return noModifications + return noModifications // TODO tijdelijk geen simplify + // Simplify the conditional expression, introduce simple assignments if required. // This is REQUIRED for correct code generation on 6502 because evaluating certain expressions // clobber the handful of temporary variables in the zeropage and leaving everything in one diff --git a/compiler/test/comparisons/test_compares.p8 b/compiler/test/comparisons/test_compares.p8 index e2ad47107..0e293eb17 100644 --- a/compiler/test/comparisons/test_compares.p8 +++ b/compiler/test/comparisons/test_compares.p8 @@ -1,12 +1,40 @@ %zeropage basicsafe %import textio +%import floats +%import string main { sub start() { + test_string() + txt.nl() + test_compare_variable() + txt.nl() + test_compare_literal() + } + + sub test_string() { + str name="john" + + if (string.compare(name, "aaa")==0) or (string.compare(name, "john")==0) or (string.compare(name, "bbb")==0) { + txt.print("name1 ok\n") + } + if (string.compare(name, "aaa")==0) or (string.compare(name, "zzz")==0) or (string.compare(name, "bbb")==0) { + txt.print("name2 fail!\n") + } + + if name=="aaa" or name=="john" or name=="bbb" + txt.print("name1b ok\n") + if name=="aaa" or name=="zzz" or name=="bbb" + txt.print("name2b fail!\n") + + } + + sub test_compare_literal() { byte b = -100 ubyte ub = 20 word w = -20000 uword uw = 2000 + float f = -100 txt.print("all 1: ") txt.print_ub(b == -100) @@ -34,7 +62,15 @@ main { txt.print_ub(uw <= 2000) txt.print_ub(uw > 1999) txt.print_ub(uw >= 2000) + txt.spc() + txt.print_ub(f == -100.0) + txt.print_ub(f != -99.0) + txt.print_ub(f < -99.0) + txt.print_ub(f <= -100.0) + txt.print_ub(f > -101.0) + txt.print_ub(f >= -100.0) txt.nl() + txt.print("all 0: ") txt.print_ub(b == -99) txt.print_ub(b != -100) @@ -61,6 +97,153 @@ main { txt.print_ub(uw <= 1999) txt.print_ub(uw > 2000) txt.print_ub(uw >= 2001) + txt.spc() + txt.print_ub(f == -99.0) + txt.print_ub(f != -100.0) + txt.print_ub(f < -100.0) + txt.print_ub(f <= -101.0) + txt.print_ub(f > -100.0) + txt.print_ub(f >= -99.0) + txt.nl() + + txt.print("all .: ") + if b == -100 txt.chrout('.') + if b != -99 txt.chrout('.') + if b < -99 txt.chrout('.') + if b <= -100 txt.chrout('.') + if b > -101 txt.chrout('.') + if b >= -100 txt.chrout('.') + if ub ==20 txt.chrout('.') + if ub !=19 txt.chrout('.') + if ub <21 txt.chrout('.') + if ub <=20 txt.chrout('.') + if ub>19 txt.chrout('.') + if ub>=20 txt.chrout('.') + txt.spc() + if w == -20000 txt.chrout('.') + if w != -19999 txt.chrout('.') + if w < -19999 txt.chrout('.') + if w <= -20000 txt.chrout('.') + if w > -20001 txt.chrout('.') + if w >= -20000 txt.chrout('.') + if uw == 2000 txt.chrout('.') + if uw != 2001 txt.chrout('.') + if uw < 2001 txt.chrout('.') + if uw <= 2000 txt.chrout('.') + if uw > 1999 txt.chrout('.') + if uw >= 2000 txt.chrout('.') + txt.spc() + if f == -100.0 txt.chrout('.') + if f != -99.0 txt.chrout('.') + if f < -99.0 txt.chrout('.') + if f <= -100.0 txt.chrout('.') + if f > -101.0 txt.chrout('.') + if f >= -100.0 txt.chrout('.') + txt.nl() + + txt.print(" no !: ") + if b == -99 txt.chrout('!') + if b != -100 txt.chrout('!') + if b < -100 txt.chrout('!') + if b <= -101 txt.chrout('!') + if b > -100 txt.chrout('!') + if b >= -99 txt.chrout('!') + if ub ==21 txt.chrout('!') + if ub !=20 txt.chrout('!') + if ub <20 txt.chrout('!') + if ub <=19 txt.chrout('!') + if ub>20 txt.chrout('!') + if ub>=21 txt.chrout('!') + txt.spc() + if w == -20001 txt.chrout('!') + if w != -20000 txt.chrout('!') + if w < -20000 txt.chrout('!') + if w <= -20001 txt.chrout('!') + if w > -20000 txt.chrout('!') + if w >= -19999 txt.chrout('!') + if uw == 1999 txt.chrout('!') + if uw != 2000 txt.chrout('!') + if uw < 2000 txt.chrout('!') + if uw <= 1999 txt.chrout('!') + if uw > 2000 txt.chrout('!') + if uw >= 2001 txt.chrout('!') + txt.spc() + if f == -99.0 txt.chrout('!') + if f != -100.0 txt.chrout('!') + if f < -100.0 txt.chrout('!') + if f <= -101.0 txt.chrout('!') + if f > -100.0 txt.chrout('!') + if f >= -99.0 txt.chrout('!') + txt.nl() + + txt.print("all .: ") + if b == -100 txt.chrout('.') else txt.chrout('!') + if b != -99 txt.chrout('.') else txt.chrout('!') + if b < -99 txt.chrout('.') else txt.chrout('!') + if b <= -100 txt.chrout('.') else txt.chrout('!') + if b > -101 txt.chrout('.') else txt.chrout('!') + if b >= -100 txt.chrout('.') else txt.chrout('!') + if ub ==20 txt.chrout('.') else txt.chrout('!') + if ub !=19 txt.chrout('.') else txt.chrout('!') + if ub <21 txt.chrout('.') else txt.chrout('!') + if ub <=20 txt.chrout('.') else txt.chrout('!') + if ub>19 txt.chrout('.') else txt.chrout('!') + if ub>=20 txt.chrout('.') else txt.chrout('!') + txt.spc() + if w == -20000 txt.chrout('.') else txt.chrout('!') + if w != -19999 txt.chrout('.') else txt.chrout('!') + if w < -19999 txt.chrout('.') else txt.chrout('!') + if w <= -20000 txt.chrout('.') else txt.chrout('!') + if w > -20001 txt.chrout('.') else txt.chrout('!') + if w >= -20000 txt.chrout('.') else txt.chrout('!') + if uw == 2000 txt.chrout('.') else txt.chrout('!') + if uw != 2001 txt.chrout('.') else txt.chrout('!') + if uw < 2001 txt.chrout('.') else txt.chrout('!') + if uw <= 2000 txt.chrout('.') else txt.chrout('!') + if uw > 1999 txt.chrout('.') else txt.chrout('!') + if uw >= 2000 txt.chrout('.') else txt.chrout('!') + txt.spc() + if f == -100.0 txt.chrout('.') else txt.chrout('!') + if f != -99.0 txt.chrout('.') else txt.chrout('!') + if f < -99.0 txt.chrout('.') else txt.chrout('!') + if f <= -100.0 txt.chrout('.') else txt.chrout('!') + if f > -101.0 txt.chrout('.') else txt.chrout('!') + if f >= -100.0 txt.chrout('.') else txt.chrout('!') + txt.nl() + + txt.print("all .: ") + if b == -99 txt.chrout('!') else txt.chrout('.') + if b != -100 txt.chrout('!') else txt.chrout('.') + if b < -100 txt.chrout('!') else txt.chrout('.') + if b <= -101 txt.chrout('!') else txt.chrout('.') + if b > -100 txt.chrout('!') else txt.chrout('.') + if b >= -99 txt.chrout('!') else txt.chrout('.') + if ub ==21 txt.chrout('!') else txt.chrout('.') + if ub !=20 txt.chrout('!') else txt.chrout('.') + if ub <20 txt.chrout('!') else txt.chrout('.') + if ub <=19 txt.chrout('!') else txt.chrout('.') + if ub>20 txt.chrout('!') else txt.chrout('.') + if ub>=21 txt.chrout('!') else txt.chrout('.') + txt.spc() + if w == -20001 txt.chrout('!') else txt.chrout('.') + if w != -20000 txt.chrout('!') else txt.chrout('.') + if w < -20000 txt.chrout('!') else txt.chrout('.') + if w <= -20001 txt.chrout('!') else txt.chrout('.') + if w > -20000 txt.chrout('!') else txt.chrout('.') + if w >= -19999 txt.chrout('!') else txt.chrout('.') + if uw == 1999 txt.chrout('!') else txt.chrout('.') + if uw != 2000 txt.chrout('!') else txt.chrout('.') + if uw < 2000 txt.chrout('!') else txt.chrout('.') + if uw <= 1999 txt.chrout('!') else txt.chrout('.') + if uw > 2000 txt.chrout('!') else txt.chrout('.') + if uw >= 2001 txt.chrout('!') else txt.chrout('.') + txt.spc() + if f == -99.0 txt.chrout('!') else txt.chrout('.') + if f != -100.0 txt.chrout('!') else txt.chrout('.') + if f < -100.0 txt.chrout('!') else txt.chrout('.') + if f <= -101.0 txt.chrout('!') else txt.chrout('.') + if f > -100.0 txt.chrout('!') else txt.chrout('.') + if f >= -99.0 txt.chrout('!') else txt.chrout('.') txt.nl() @@ -112,9 +295,311 @@ main { txt.print_uw(uw) txt.print(" 8000\n") -; float f -; while f<2.2 { -; f+=0.1 -; } + f = 0.0 + floats.print_f(f) + while f<2.2 { + f+=0.1 + floats.print_f(f) + } + floats.print_f(f) + txt.print(" 2.2\n") + } + + sub test_compare_variable() { + byte b = -100 + ubyte ub = 20 + word w = -20000 + uword uw = 2000 + float f = -100 + + byte minus_100 = -100 + byte minus_99 = -99 + byte minus_20 = -20 + byte minus_101 = -101 + ubyte nineteen = 19 + ubyte twenty = 20 + ubyte twenty1 = 21 + ubyte twohundred = 200 + float f_minus_100 = -100.0 + float f_minus_101 = -101.0 + float f_minus_99 = -99.0 + float twodottwo = 2.2 + word minus8000 = -8000 + word eightthousand = 8000 + word w_min_20000 = -20000 + word w_min_19999 = -19999 + word w_min_20001 = -20001 + uword twothousand = 2000 + uword twothousand1 = 2001 + uword nineteen99 = 1999 + + + txt.print("all 1: ") + txt.print_ub(b == minus_100) + txt.print_ub(b != minus_99) + txt.print_ub(b < minus_99) + txt.print_ub(b <= minus_100) + txt.print_ub(b > minus_101) + txt.print_ub(b >= minus_100) + txt.print_ub(ub ==twenty) + txt.print_ub(ub !=nineteen) + txt.print_ub(ub nineteen) + txt.print_ub(ub>=twenty) + txt.spc() + txt.print_ub(w == w_min_20000) + txt.print_ub(w != w_min_19999) + txt.print_ub(w < w_min_19999) + txt.print_ub(w <= w_min_20000) + txt.print_ub(w > w_min_20001) + txt.print_ub(w >= w_min_20000) + txt.print_ub(uw == twothousand) + txt.print_ub(uw != twothousand1) + txt.print_ub(uw < twothousand1) + txt.print_ub(uw <= twothousand) + txt.print_ub(uw > nineteen99) + txt.print_ub(uw >= twothousand) + txt.spc() + txt.print_ub(f == f_minus_100) + txt.print_ub(f != f_minus_99) + txt.print_ub(f < f_minus_99) + txt.print_ub(f <= f_minus_100) + txt.print_ub(f > f_minus_101) + txt.print_ub(f >= f_minus_100) + txt.nl() + + txt.print("all 0: ") + txt.print_ub(b == minus_99) + txt.print_ub(b != minus_100) + txt.print_ub(b < minus_100) + txt.print_ub(b <= minus_101) + txt.print_ub(b > minus_100) + txt.print_ub(b >= minus_99) + txt.print_ub(ub ==twenty1) + txt.print_ub(ub !=twenty) + txt.print_ub(ub twenty) + txt.print_ub(ub>=twenty1) + txt.spc() + txt.print_ub(w == w_min_20001) + txt.print_ub(w != w_min_20000) + txt.print_ub(w < w_min_20000) + txt.print_ub(w <= w_min_20001) + txt.print_ub(w > w_min_20000) + txt.print_ub(w >= w_min_19999) + txt.print_ub(uw == nineteen99) + txt.print_ub(uw != twothousand) + txt.print_ub(uw < twothousand) + txt.print_ub(uw <= nineteen99) + txt.print_ub(uw > twothousand) + txt.print_ub(uw >= twothousand1) + txt.spc() + txt.print_ub(f == f_minus_99) + txt.print_ub(f != f_minus_100) + txt.print_ub(f < f_minus_100) + txt.print_ub(f <= f_minus_101) + txt.print_ub(f > f_minus_100) + txt.print_ub(f >= f_minus_99) + txt.nl() + + txt.print("all .: ") + if b == minus_100 txt.chrout('.') + if b != minus_99 txt.chrout('.') + if b < minus_99 txt.chrout('.') + if b <= minus_100 txt.chrout('.') + if b > minus_101 txt.chrout('.') + if b >= minus_100 txt.chrout('.') + if ub == twenty txt.chrout('.') + if ub != nineteen txt.chrout('.') + if ub < twenty1 txt.chrout('.') + if ub <= twenty txt.chrout('.') + if ub> nineteen txt.chrout('.') + if ub>= twenty txt.chrout('.') + txt.spc() + if w == w_min_20000 txt.chrout('.') + if w != w_min_19999 txt.chrout('.') + if w < w_min_19999 txt.chrout('.') + if w <= w_min_20000 txt.chrout('.') + if w > w_min_20001 txt.chrout('.') + if w >= w_min_20000 txt.chrout('.') + if uw == twothousand txt.chrout('.') + if uw != twothousand1 txt.chrout('.') + if uw < twothousand1 txt.chrout('.') + if uw <= twothousand txt.chrout('.') + if uw > nineteen99 txt.chrout('.') + if uw >= twothousand txt.chrout('.') + txt.spc() + if f == f_minus_100 txt.chrout('.') + if f != f_minus_99 txt.chrout('.') + if f < f_minus_99 txt.chrout('.') + if f <= f_minus_100 txt.chrout('.') + if f > f_minus_101 txt.chrout('.') + if f >= f_minus_100 txt.chrout('.') + txt.nl() + + txt.print(" no !: ") + if b == minus_99 txt.chrout('!') + if b != minus_100 txt.chrout('!') + if b < minus_100 txt.chrout('!') + if b <= minus_101 txt.chrout('!') + if b > minus_100 txt.chrout('!') + if b >= minus_99 txt.chrout('!') + if ub == twenty1 txt.chrout('!') + if ub != twenty txt.chrout('!') + if ub < twenty txt.chrout('!') + if ub <= nineteen txt.chrout('!') + if ub> twenty txt.chrout('!') + if ub>= twenty1 txt.chrout('!') + txt.spc() + if w == w_min_20001 txt.chrout('!') + if w != w_min_20000 txt.chrout('!') + if w < w_min_20000 txt.chrout('!') + if w <= w_min_20001 txt.chrout('!') + if w > w_min_20000 txt.chrout('!') + if w >= w_min_19999 txt.chrout('!') + if uw == nineteen99 txt.chrout('!') + if uw != twothousand txt.chrout('!') + if uw < twothousand txt.chrout('!') + if uw <= nineteen99 txt.chrout('!') + if uw > twothousand txt.chrout('!') + if uw >= twothousand1 txt.chrout('!') + txt.spc() + if f == f_minus_99 txt.chrout('!') + if f != f_minus_100 txt.chrout('!') + if f < f_minus_100 txt.chrout('!') + if f <= f_minus_101 txt.chrout('!') + if f > f_minus_100 txt.chrout('!') + if f >= f_minus_99 txt.chrout('!') + txt.nl() + + txt.print("all .: ") + if b == minus_100 txt.chrout('.') else txt.chrout('!') + if b != minus_99 txt.chrout('.') else txt.chrout('!') + if b < minus_99 txt.chrout('.') else txt.chrout('!') + if b <= minus_100 txt.chrout('.') else txt.chrout('!') + if b > minus_101 txt.chrout('.') else txt.chrout('!') + if b >= minus_100 txt.chrout('.') else txt.chrout('!') + if ub == twenty txt.chrout('.') else txt.chrout('!') + if ub != nineteen txt.chrout('.') else txt.chrout('!') + if ub < twenty1 txt.chrout('.') else txt.chrout('!') + if ub <= twenty txt.chrout('.') else txt.chrout('!') + if ub> nineteen txt.chrout('.') else txt.chrout('!') + if ub>=twenty txt.chrout('.') else txt.chrout('!') + txt.spc() + if w == w_min_20000 txt.chrout('.') else txt.chrout('!') + if w != w_min_19999 txt.chrout('.') else txt.chrout('!') + if w < w_min_19999 txt.chrout('.') else txt.chrout('!') + if w <= w_min_20000 txt.chrout('.') else txt.chrout('!') + if w > w_min_20001 txt.chrout('.') else txt.chrout('!') + if w >= w_min_20000 txt.chrout('.') else txt.chrout('!') + if uw == twothousand txt.chrout('.') else txt.chrout('!') + if uw != twothousand1 txt.chrout('.') else txt.chrout('!') + if uw < twothousand1 txt.chrout('.') else txt.chrout('!') + if uw <= twothousand txt.chrout('.') else txt.chrout('!') + if uw > nineteen99 txt.chrout('.') else txt.chrout('!') + if uw >= twothousand txt.chrout('.') else txt.chrout('!') + txt.spc() + if f == f_minus_100 txt.chrout('.') else txt.chrout('!') + if f != f_minus_99 txt.chrout('.') else txt.chrout('!') + if f < f_minus_99 txt.chrout('.') else txt.chrout('!') + if f <= f_minus_100 txt.chrout('.') else txt.chrout('!') + if f > f_minus_101 txt.chrout('.') else txt.chrout('!') + if f >= f_minus_100 txt.chrout('.') else txt.chrout('!') + txt.nl() + + txt.print("all .: ") + if b == minus_99 txt.chrout('!') else txt.chrout('.') + if b != minus_100 txt.chrout('!') else txt.chrout('.') + if b < minus_100 txt.chrout('!') else txt.chrout('.') + if b <= minus_101 txt.chrout('!') else txt.chrout('.') + if b > minus_100 txt.chrout('!') else txt.chrout('.') + if b >= minus_99 txt.chrout('!') else txt.chrout('.') + if ub == twenty1 txt.chrout('!') else txt.chrout('.') + if ub !=twenty txt.chrout('!') else txt.chrout('.') + if ub twenty txt.chrout('!') else txt.chrout('.') + if ub>= twenty1 txt.chrout('!') else txt.chrout('.') + txt.spc() + if w == w_min_20001 txt.chrout('!') else txt.chrout('.') + if w != w_min_20000 txt.chrout('!') else txt.chrout('.') + if w < w_min_20000 txt.chrout('!') else txt.chrout('.') + if w <= w_min_20001 txt.chrout('!') else txt.chrout('.') + if w > w_min_20000 txt.chrout('!') else txt.chrout('.') + if w >= w_min_19999 txt.chrout('!') else txt.chrout('.') + if uw == nineteen99 txt.chrout('!') else txt.chrout('.') + if uw != twothousand txt.chrout('!') else txt.chrout('.') + if uw < twothousand txt.chrout('!') else txt.chrout('.') + if uw <= nineteen99 txt.chrout('!') else txt.chrout('.') + if uw > twothousand txt.chrout('!') else txt.chrout('.') + if uw >= twothousand1 txt.chrout('!') else txt.chrout('.') + txt.spc() + if f == f_minus_99 txt.chrout('!') else txt.chrout('.') + if f != f_minus_100 txt.chrout('!') else txt.chrout('.') + if f < f_minus_100 txt.chrout('!') else txt.chrout('.') + if f <= f_minus_101 txt.chrout('!') else txt.chrout('.') + if f > f_minus_100 txt.chrout('!') else txt.chrout('.') + if f >= f_minus_99 txt.chrout('!') else txt.chrout('.') + txt.nl() + + + b = minus_100 + while b <= minus_20 + b++ + txt.print_b(b) + txt.print(" -19\n") + b = minus_100 + while b < -minus_20 + b++ + txt.print_b(b) + txt.print(" -20\n") + + ub = 20 + while ub <= twohundred + ub++ + txt.print_ub(ub) + txt.print(" 201\n") + ub = 20 + while ub < twohundred + ub++ + txt.print_ub(ub) + txt.print(" 200\n") + + w = w_min_20000 + while w <= minus8000 { + w++ + } + txt.print_w(w) + txt.print(" -7999\n") + w = w_min_20000 + while w < minus8000 { + w++ + } + txt.print_w(w) + txt.print(" -8000\n") + + uw = 2000 + while uw <= eightthousand { + uw++ + } + txt.print_uw(uw) + txt.print(" 8001\n") + uw = 2000 + while uw < eightthousand { + uw++ + } + txt.print_uw(uw) + txt.print(" 8000\n") + + f = 0.0 + floats.print_f(f) + while f