diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 0f4b9cb1b..7ae7c257b 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -996,6 +996,8 @@ internal class AssignmentAsmGen(private val program: PtProgram, private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { val signed = expr.left.type == DataType.BYTE || expr.right.type == DataType.BYTE + // TODO no need to use a temporary variable if the right expression is a literal number or a variable name (or register name) + when(expr.operator) { "==" -> { asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index 51fdbdd2a..074f6c2ab 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -320,44 +320,69 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, && tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y) - if(target.array.splitWords) { - asmgen.out(" lda ${target.array.variable.name}_lsb,y | sta P8ZP_SCRATCH_W1") - asmgen.out(" lda ${target.array.variable.name}_msb,y | sta P8ZP_SCRATCH_W1+1") - } else { - asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1") - asmgen.out(" lda ${target.array.variable.name}+1,y | sta P8ZP_SCRATCH_W1+1") - } asmgen.saveRegisterStack(CpuRegister.Y, false) + if(target.array.splitWords) { + asmgen.out(" lda ${target.array.variable.name}_lsb,y") + asmgen.out(" ldx ${target.array.variable.name}_msb,y") + } else { + asmgen.out(" lda ${target.array.variable.name},y") + asmgen.out(" ldx ${target.array.variable.name}+1,y") + } when(value.kind) { SourceStorageKind.LITERALNUMBER -> { - // TODO optimize the stuff below to not use temp variables?? - inplacemodificationWordWithLiteralval("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt()) + val number = value.number!!.number.toInt() + if(!inplacemodificationRegisterAXwithLiteralval(operator, number)) { + asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") + inplacemodificationWordWithLiteralval("P8ZP_SCRATCH_W1", target.datatype, operator, number) + asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") + } } SourceStorageKind.VARIABLE -> { - // TODO optimize the stuff below to not use temp variables?? - inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype) + if(!inplacemodificationRegisterAXwithVariable( + operator, + value.asmVarname, + value.datatype + )) { + asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") + inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype) + asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") + } } SourceStorageKind.REGISTER -> { - // TODO optimize the stuff below to not use temp variables?? - inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, regName(value), value.datatype) + if(!inplacemodificationRegisterAXwithVariable( + operator, + regName(value), + value.datatype + )) { + asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") + inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, regName(value), value.datatype) + asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") + } + } + SourceStorageKind.MEMORY -> { + asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") + inplacemodificationWordWithMemread("P8ZP_SCRATCH_W1", target.datatype, operator, value.memory!!) + asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") + } + SourceStorageKind.ARRAY -> { + asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") + inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!) + asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") } - SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread("P8ZP_SCRATCH_W1", target.datatype, operator, value.memory!!) - SourceStorageKind.ARRAY -> inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!) SourceStorageKind.EXPRESSION -> { + asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") if(value.expression is PtTypeCast) inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression) else inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!) + asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") } } - asmgen.restoreRegisterStack(CpuRegister.Y, false) - if(target.array.splitWords) { - asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name}_lsb,y") - asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}_msb,y") - } else { - asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name},y") - asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}+1,y") - } + asmgen.restoreRegisterStack(CpuRegister.Y, true) + if(target.array.splitWords) + asmgen.out(" sta ${target.array.variable.name}_lsb,y | txa | sta ${target.array.variable.name}_msb,y") + else + asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y") } DataType.FLOAT -> { asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A) @@ -412,6 +437,110 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } } + private fun inplacemodificationRegisterAXwithVariable(operator: String, variable: String, varDt: DataType): Boolean { + // note: we only optimize addition and subtraction, and these are the same for unsigned or signed. + when(operator) { + "+" -> { + return if(varDt in WordDatatypes) { + asmgen.out(""" + clc + adc $variable + pha + txa + adc $variable+1 + tax + pla""") + true + } else { + asmgen.out(""" + ldy $variable + bpl + + dex ; sign extend ++ clc + adc $variable + bcc + + inx ++""") + true + } + } + "-" -> { + return if(varDt in WordDatatypes) { + asmgen.out(""" + sec + sbc $variable + pha + txa + sbc $variable+1 + tax + pla""") + true + } else { + asmgen.out(""" + ldy $variable + bpl + + inx ; sign extend ++ sec + sbc $variable + bcs + + dex ++""") + true + } + } + else -> return false // TODO optimize more operators, such as the bitwise logical ones? Might need to know if signed + } + } + + private fun inplacemodificationRegisterAXwithLiteralval(operator: String, number: Int): Boolean { + // note: we only optimize addition and subtraction, and these are the same for unsigned or signed. + when(operator) { + "+" -> { + return if(number in -128..255) { + asmgen.out(""" + clc + adc #$number + bcc + + inx ++""") + true + } else { + asmgen.out(""" + clc + adc #<$number + pha + txa + adc #>$number + tax + pla""") + true + } + } + "-" -> { + return if(number in -128..255) { + asmgen.out(""" + sec + sbc #$number + bcs + + dex ++""") + true + } else { + asmgen.out(""" + sec + sbc #<$number + pha + txa + sbc #>$number + tax + pla""") + true + } + } + else -> return false // TODO optimize more operators, such as the bitwise logical ones? Might need to know if signed + } + } + private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean { if (target.datatype == value.type) { val childDt = value.value.type @@ -579,7 +708,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) { - // TODO optimize the stuff below to not use temp variables?? val tmpVar = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG" asmgen.assignExpressionToVariable(value, tmpVar, value.type) asmgen.out(" lda $name") @@ -1932,21 +2060,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, sta $name+1""") } "*" -> { - // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation + // value is (u) byte value, sign extend that and proceed with regular 16 bit operation // TODO use an optimized word * byte multiplication routine? asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.signExtendAYlsb(valueDt) multiplyVarByWordInAY() } "/" -> { - // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation + // value is (u) byte value, sign extend that and proceed with regular 16 bit operation // TODO use an optimized word / byte divmod routine? asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.signExtendAYlsb(valueDt) divideVarByWordInAY() } "%" -> { - // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation + // value is (u) byte value, sign extend that and proceed with regular 16 bit operation // TODO use an optimized word / byte divmod routine? asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.signExtendAYlsb(valueDt) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index f0c3f0c01..217204f5e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,8 @@ TODO ==== -- try to reduce the number of uses of temp variables for example in array[idx] -= amount see AugmentableAssignmentAsmGen -- investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... / - investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... +- try to reduce the number of temp variables in comparison expressions like if a>4 or a<2 .... See assignOptimizedComparisonBytes(). +- investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... - IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction - IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified! diff --git a/examples/test.p8 b/examples/test.p8 index b339ed33e..551d21b62 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,16 +3,16 @@ main { sub start() { - uword[4] array = [1,2,3,4] - ubyte index = 2 + ubyte a = 1 - cx16.r0 = 99 - array[index] += 12345 - array[index] += cx16.r0 - array[index] += index - txt.print_uw(array[index]) ; prints 12449 + if a>4 or a<2 { + a++ + } - ; code size = $0249 + if a>=2 and a<4 { + a++ + } + txt.print_ub(a) ; 3 } }