From 8d6220ce51e9f27414832188cbc5a75460856e63 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 21 Aug 2020 00:17:28 +0200 Subject: [PATCH] added most essential of the new in-place assignment code --- compiler/res/prog8lib/prog8lib.asm | 19 +- .../src/prog8/ast/statements/AstStatements.kt | 14 +- .../compiler/target/c64/AssemblyProgram.kt | 4 +- .../target/c64/codegen/AssignmentAsmGen.kt | 19 +- .../codegen/AugmentableAssignmentAsmGen.kt | 452 +++++++++++++++++- examples/test.p8 | 83 +++- 6 files changed, 543 insertions(+), 48 deletions(-) diff --git a/compiler/res/prog8lib/prog8lib.asm b/compiler/res/prog8lib/prog8lib.asm index 8ffee348c..22f0c3895 100644 --- a/compiler/res/prog8lib/prog8lib.asm +++ b/compiler/res/prog8lib/prog8lib.asm @@ -40,9 +40,22 @@ read_byte_from_address_on_stack .proc ; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged) lda c64.ESTACK_LO+1,x ldy c64.ESTACK_HI+1,x - sta (+) +1 - sty (+) +2 -+ lda $ffff ; modified + sta c64.SCRATCH_ZPWORD2 + sty c64.SCRATCH_ZPWORD2+1 + ldy #0 + lda (c64.SCRATCH_ZPWORD2),y + rts + .pend + + +write_byte_to_address_on_stack .proc + ; -- write the byte in A to the memory address on the top of the stack (stack remains unchanged) + ldy c64.ESTACK_LO+1,x + sty c64.SCRATCH_ZPWORD2 + ldy c64.ESTACK_HI+1,x + sty c64.SCRATCH_ZPWORD2+1 + ldy #0 + lda (c64.SCRATCH_ZPWORD2),y rts .pend diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 6fc170ec0..7c375b0bb 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -336,24 +336,28 @@ open class Assignment(var target: AssignTarget, var value: Expression, override get() { val binExpr = value as? BinaryExpression if(binExpr!=null) { - if(binExpr.left isSameAs target) - return true // A = A 5 + if(binExpr.right !is BinaryExpression && binExpr.left isSameAs target) + return true // A = A v if(binExpr.operator in associativeOperators) { - if (binExpr.right isSameAs target) + if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target) return true // A = v A val leftBinExpr = binExpr.left as? BinaryExpression if(leftBinExpr?.operator == binExpr.operator) { + // one of these? // A = (A x) y // A = (x A) y - return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target + // A = (x y) A + return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target || binExpr.right isSameAs target } val rightBinExpr = binExpr.right as? BinaryExpression if(rightBinExpr?.operator == binExpr.operator) { + // one of these? // A = y (A x) // A = y (x y) - return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target + // A = A (x y) + return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target || binExpr.left isSameAs target } } } diff --git a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt index d4659d52a..00be276ae 100644 --- a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt +++ b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt @@ -14,9 +14,9 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro private val viceMonListFile = outputDir.resolve("$name.vice-mon-list") override fun assemble(options: CompilationOptions) { - // add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps + // add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently) val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", - "-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch", + "-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror", "--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor") val outFile = when (options.output) { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 7b6c53d71..bddda813a 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -72,9 +72,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference) } else -> { - asmgen.translateExpression(read.addressExpression) - asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | inx") - assignFromRegister(assign.target, CpuRegister.A) + TODO("assign from memread $assign") // see inplaceModification() ? +// asmgen.translateExpression(read.addressExpression) +// asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | inx") +// assignFromRegister(assign.target, CpuRegister.A) } } } @@ -375,24 +376,24 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen val pointerVarName = asmgen.asmIdentifierName(addressExpr) asmgen.out(""" lda $pointerVarName - sta ${C64Zeropage.SCRATCH_W1} + sta ${C64Zeropage.SCRATCH_W2} lda $pointerVarName+1 - sta ${C64Zeropage.SCRATCH_W1+1} + sta ${C64Zeropage.SCRATCH_W2+1} lda $ldaInstructionArg ldy #0 - sta (${C64Zeropage.SCRATCH_W1}),y""") + sta (${C64Zeropage.SCRATCH_W2}),y""") } else -> { asmgen.translateExpression(addressExpr) asmgen.out(""" inx lda $ESTACK_LO_HEX,x - sta ${C64Zeropage.SCRATCH_W1} + sta ${C64Zeropage.SCRATCH_W2} lda $ESTACK_HI_HEX,x - sta ${C64Zeropage.SCRATCH_W1+1} + sta ${C64Zeropage.SCRATCH_W2+1} lda $ldaInstructionArg ldy #0 - sta (${C64Zeropage.SCRATCH_W1}),y""") + sta (${C64Zeropage.SCRATCH_W2}),y""") } } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt index c13a9f77d..fdb06b540 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt @@ -1,13 +1,14 @@ package prog8.compiler.target.c64.codegen import prog8.ast.Program -import prog8.ast.base.DataType -import prog8.ast.base.IterableDatatypes +import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.statements.AssignTarget import prog8.ast.statements.Assignment import prog8.compiler.AssemblyError import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage +import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX +import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX import prog8.compiler.toHex internal class AugmentableAssignmentAsmGen(private val program: Program, @@ -35,9 +36,423 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceBinary(target: AssignTarget, binaryExpression: BinaryExpression, assign: Assignment) { - println("TODO optimize binexpr assignment ${binaryExpression.position}") - assignmentAsmGen.translateOtherAssignment(assign) // TODO get rid of this fallback + private fun inplaceBinary(target: AssignTarget, binExpr: BinaryExpression, assign: Assignment) { + + if (binExpr.right !is BinaryExpression && binExpr.left isSameAs target) { + // A = A 5 + return inplaceModification(target, binExpr.operator, binExpr.right, assign) + } + + if (binExpr.operator in associativeOperators) + { + val leftBinExpr = binExpr.left as? BinaryExpression + if (leftBinExpr!=null && binExpr.right isSameAs target) { + // A = 5 A + return inplaceModification(target, binExpr.operator, binExpr.left, assign) + } + + if (leftBinExpr?.operator == binExpr.operator) { + // TODO better optimize the chained asm to avoid intermediate stores/loads? + when { + binExpr.right isSameAs target -> { + // A = (x y) A + inplaceModification(target, binExpr.operator, leftBinExpr.left, assign) + inplaceModification(target, binExpr.operator, leftBinExpr.right, assign) + return + } + leftBinExpr.left isSameAs target -> { + // A = (A x) y + inplaceModification(target, binExpr.operator, leftBinExpr.right, assign) + inplaceModification(target, binExpr.operator, binExpr.right, assign) + return + } + leftBinExpr.right isSameAs target -> { + // A = (x A) y + inplaceModification(target, binExpr.operator, leftBinExpr.left, assign) + inplaceModification(target, binExpr.operator, binExpr.right, assign) + return + } + } + } + val rightBinExpr = binExpr.right as? BinaryExpression + if (rightBinExpr?.operator == binExpr.operator) { + when { + binExpr.left isSameAs target -> { + // A = A (x y) + inplaceModification(target, binExpr.operator, rightBinExpr.left, assign) + inplaceModification(target, binExpr.operator, rightBinExpr.right, assign) + return + } + rightBinExpr.left isSameAs target -> { + // A = y (A x) + inplaceModification(target, binExpr.operator, binExpr.left, assign) + inplaceModification(target, binExpr.operator, rightBinExpr.right, assign) + return + } + rightBinExpr.right isSameAs target -> { + // A = y (x y) + inplaceModification(target, binExpr.operator, binExpr.left, assign) + inplaceModification(target, binExpr.operator, rightBinExpr.left, assign) + return + } + } + } + } + + throw FatalAstException("assignment should be augmentable $assign\nleft=${binExpr.left}\nright=${binExpr.right}") + } + + private fun inplaceModification(target: AssignTarget, operator: String, value: Expression, origAssign: Assignment) { + val arrayIdx = target.arrayindexed + val identifier = target.identifier + val memory = target.memoryAddress + val valueLv = (value as? NumericLiteralValue)?.number?.toDouble() + val ident = value as? IdentifierReference + + when { + identifier!=null -> { + val name = asmgen.asmIdentifierName(identifier) + val dt = identifier.inferType(program).typeOrElse(DataType.STRUCT) + when (dt) { + in ByteDatatypes -> { + when { + valueLv!=null -> inplaceModification_byte_litval_to_variable(name, operator, valueLv) + ident!=null -> inplaceModification_byte_variable_to_variable(name, operator, ident) + // TODO more specialized code for types such as memory read etc. + else -> inplaceModification_byte_value_to_variable(name, operator, value) + } + } + in WordDatatypes -> { + when { + valueLv!=null -> inplaceModification_word_litval_to_variable(name, operator, valueLv) + ident!=null -> inplaceModification_word_variable_to_variable(name, operator, ident) + // TODO more specialized code for types such as memory read etc. + else -> inplaceModification_word_value_to_variable(name, operator, value) + } + } + else -> { + // TODO fix this one it is used in several examples. + println("TODO 1c optimize simple inplace assignment [$dt] $name $operator= $value") + assignmentAsmGen.translateOtherAssignment(origAssign) // TODO get rid of this fallback + } + } + } + memory!=null -> { + when (memory.addressExpression) { + is NumericLiteralValue -> { + val addr = (memory.addressExpression as NumericLiteralValue).number.toInt() + // re-use code to assign a variable, instead this time, use a direct memory address + when { + valueLv!=null -> inplaceModification_byte_litval_to_variable(addr.toHex(), operator, valueLv) + ident!=null -> inplaceModification_byte_variable_to_variable(addr.toHex(), operator, ident) + // TODO more specialized code for types such as memory read etc. + else -> inplaceModification_byte_value_to_variable(addr.toHex(), operator, value) + } + } + is IdentifierReference -> { + val name = asmgen.asmIdentifierName(memory.addressExpression as IdentifierReference) + when { + valueLv!=null -> inplaceModification_byte_litval_to_memory(name, operator, valueLv) + ident!=null -> inplaceModification_byte_variable_to_memory(name, operator, ident) + // TODO more specialized code for types such as memory read etc. + else -> inplaceModification_byte_value_to_memory(name, operator, value, origAssign) + } + } + else -> { + asmgen.translateExpression(memory.addressExpression) + asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta ${C64Zeropage.SCRATCH_B1}") + // the original memory byte's value is now in the scratch B1 location. + when { + valueLv!=null -> inplaceModification_byte_litval_to_variable(C64Zeropage.SCRATCH_B1.toHex(), operator, valueLv) + ident!=null -> inplaceModification_byte_variable_to_variable(C64Zeropage.SCRATCH_B1.toHex(), operator, ident) + // TODO more specialized code for types such as memory read etc. + else -> inplaceModification_byte_value_to_variable(C64Zeropage.SCRATCH_B1.toHex(), operator, value) + } + asmgen.out(" lda ${C64Zeropage.SCRATCH_B1} | jsr prog8_lib.write_byte_to_address_on_stack | inx") + } + } + } + arrayIdx!=null -> { + println("TODO 3 optimize simple inplace array assignment $arrayIdx $operator= $value") + assignmentAsmGen.translateOtherAssignment(origAssign) // TODO get rid of this fallback + } + } + } + + private fun inplaceModification_byte_value_to_memory(pointername: String, operator: String, value: Expression, origAssign: Assignment) { + assignmentAsmGen.translateOtherAssignment(origAssign) // TODO get rid of this fallback + TODO("Not yet implemented") + } + + private fun inplaceModification_byte_variable_to_memory(pointername: String, operator: String, ident: IdentifierReference) { + val otherName = asmgen.asmIdentifierName(ident) + fun loadByteFromPointerInA() { + asmgen.out(""" + lda $pointername + ldy $pointername+1 + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1+1} + ldy #0 + lda (${C64Zeropage.SCRATCH_W1}),y""") + } + when(operator) { + // note: ** (power) operator requires floats. + "+" -> { + loadByteFromPointerInA() + asmgen.out(" clc | adc $otherName | sta (${C64Zeropage.SCRATCH_W1}),y") + } + "-" -> { + loadByteFromPointerInA() + asmgen.out(" sec | sbc $otherName | sta (${C64Zeropage.SCRATCH_W1}),y") + } + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("byte remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> TODO("byte asl") + ">>" -> TODO("byte lsr") + "&" -> { + loadByteFromPointerInA() + asmgen.out(" and $otherName | (${C64Zeropage.SCRATCH_W1}),y") + } + "^" -> { + loadByteFromPointerInA() + asmgen.out(" xor $otherName | (${C64Zeropage.SCRATCH_W1}),y") + } + "|" -> { + loadByteFromPointerInA() + asmgen.out(" ora $otherName | (${C64Zeropage.SCRATCH_W1}),y") + } + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_byte_litval_to_memory(pointername: String, operator: String, value: Double) { + fun loadByteFromPointerInA() { + asmgen.out(""" + lda $pointername + ldy $pointername+1 + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1+1} + ldy #0 + lda (${C64Zeropage.SCRATCH_W1}),y""") + } + when(operator) { + // note: ** (power) operator requires floats. + "+" -> { + loadByteFromPointerInA() + asmgen.out(" clc | adc #$value | sta (${C64Zeropage.SCRATCH_W1}),y") + } + "-" -> { + loadByteFromPointerInA() + asmgen.out(" sec | sbc #$value | sta (${C64Zeropage.SCRATCH_W1}),y") + } + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("byte remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> { + if(value>1) { + loadByteFromPointerInA() + repeat(value.toInt()) { asmgen.out(" asl a") } + asmgen.out(" sta (${C64Zeropage.SCRATCH_W1}),y") + } + } + ">>" -> { + if(value>1) { + loadByteFromPointerInA() + repeat(value.toInt()) { asmgen.out(" lsr a") } + asmgen.out(" sta (${C64Zeropage.SCRATCH_W1}),y") + } + } + "&" -> { + loadByteFromPointerInA() + asmgen.out(" and #$value | (${C64Zeropage.SCRATCH_W1}),y") + } + "^" -> { + loadByteFromPointerInA() + asmgen.out(" xor #$value | (${C64Zeropage.SCRATCH_W1}),y") + } + "|" -> { + loadByteFromPointerInA() + asmgen.out(" ora #$value | (${C64Zeropage.SCRATCH_W1}),y") + } + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_word_litval_to_variable(name: String, operator: String, value: Double) { + when(operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc #<$value | sta $name | lda $name+1 | adc #>$value | sta $name+1") + "-" -> asmgen.out(" lda $name | sec | sbc #<$value | sta $name | lda $name+1 | sbc #>$value | sta $name+1") + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("word remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> { + if(value>1) { + asmgen.out(" lda $name") + TODO("word asl") + asmgen.out(" sta $name") + } + } + ">>" -> { + if(value>1) { + asmgen.out(" lda $name") + TODO("word lsr") + asmgen.out(" sta $name") + } + } + "&" -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1") + "^" -> asmgen.out(" lda $name | xor #<$value | sta $name | lda $name+1 | xor #>$value | sta $name+1") + "|" -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_word_variable_to_variable(name: String, operator: String, ident: IdentifierReference) { + val otherName = asmgen.asmIdentifierName(ident) + when(operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1") + "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1") + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("word remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> TODO() + ">>" -> TODO() + "&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1") + "^" -> asmgen.out(" lda $name | xor $otherName | sta $name | lda $name+1 | xor $otherName+1 | sta $name+1") + "|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_word_value_to_variable(name: String, operator: String, value: Expression) { + // this should be the last resort for code generation for this, + // because the value is evaluated onto the eval stack (=slow). + asmgen.translateExpression(value) + when(operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | adc $ESTACK_HI_PLUS1_HEX,x | sta $name+1") + "-" -> asmgen.out(" lda $name | sec | sbc $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | sbc $ESTACK_HI_PLUS1_HEX,x | sta $name+1") + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("word remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> TODO() + ">>" -> TODO() + "&" -> asmgen.out(" lda $name | and $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | and $ESTACK_HI_PLUS1_HEX,x | sta $name+1") + "^" -> asmgen.out(" lda $name | xor $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | xor $ESTACK_HI_PLUS1_HEX,x | sta $name+1") + "|" -> asmgen.out(" lda $name | ora $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | ora $ESTACK_HI_PLUS1_HEX,x | sta $name+1") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + asmgen.out(" inx") + } + + private fun inplaceModification_byte_value_to_variable(name: String, operator: String, value: Expression) { + // this should be the last resort for code generation for this, + // because the value is evaluated onto the eval stack (=slow). + asmgen.translateExpression(value) + when(operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc $ESTACK_LO_PLUS1_HEX,x | sta $name") + "-" -> asmgen.out(" lda $name | sec | sbc $ESTACK_LO_PLUS1_HEX,x | sta $name") + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("byte remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> TODO() + ">>" -> TODO() + "&" -> asmgen.out(" lda $name | and $ESTACK_LO_PLUS1_HEX,x | sta $name") + "^" -> asmgen.out(" lda $name | xor $ESTACK_LO_PLUS1_HEX,x | sta $name") + "|" -> asmgen.out(" lda $name | ora $ESTACK_LO_PLUS1_HEX,x | sta $name") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + asmgen.out(" inx") + } + + private fun inplaceModification_byte_variable_to_variable(name: String, operator: String, ident: IdentifierReference) { + val otherName = asmgen.asmIdentifierName(ident) + when(operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name") + "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name") + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("byte remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> TODO() + ">>" -> TODO() + "&" -> asmgen.out(" lda $name | and $otherName | sta $name") + "^" -> asmgen.out(" lda $name | xor $otherName | sta $name") + "|" -> asmgen.out(" lda $name | ora $otherName | sta $name") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_byte_litval_to_variable(name: String, operator: String, value: Double) { + when(operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name") + "-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name") + "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("byte remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> { + if(value>1) { + asmgen.out(" lda $name") + repeat(value.toInt()) { asmgen.out(" asl a") } + asmgen.out(" sta $name") + } + } + ">>" -> { + if(value>1) { + asmgen.out(" lda $name") + repeat(value.toInt()) { asmgen.out(" lsr a") } + asmgen.out(" sta $name") + } + } + "&" -> asmgen.out(" lda $name | and #$value | sta $name") + "^" -> asmgen.out(" lda $name | xor #$value | sta $name") + "|" -> asmgen.out(" lda $name | ora #$value | sta $name") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } } private fun inplaceCast(target: AssignTarget, cast: TypecastExpression, assign: Assignment) { @@ -87,7 +502,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, when(dt) { DataType.UBYTE -> { when { - identifier!=null -> { + identifier != null -> { val name = asmgen.asmIdentifierName(identifier) asmgen.out(""" lda $name @@ -96,7 +511,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, + eor #1 sta $name""") } - memory!=null -> { + memory != null -> { when (memory.addressExpression) { is NumericLiteralValue -> { val addr = (memory.addressExpression as NumericLiteralValue).number.toHex() @@ -113,7 +528,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, lda $name sta ${C64Zeropage.SCRATCH_W1} lda $name+1 - sta ${C64Zeropage.SCRATCH_W1+1} + sta ${C64Zeropage.SCRATCH_W1 + 1} ldy #0 lda (${C64Zeropage.SCRATCH_W1}),y beq + @@ -121,10 +536,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, + eor #1 sta (${C64Zeropage.SCRATCH_W1}),y""") } - else -> throw AssemblyError("weird address value") + else -> { + TODO("$memory") + } } } - arrayIdx!=null -> { + arrayIdx != null -> { TODO("in-place not of ubyte array") } } @@ -159,14 +576,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, when(dt) { DataType.UBYTE -> { when { - identifier!=null -> { + identifier != null -> { val name = asmgen.asmIdentifierName(identifier) asmgen.out(""" lda $name eor #255 sta $name""") } - memory!=null -> { + memory != null -> { when (memory.addressExpression) { is NumericLiteralValue -> { val addr = (memory.addressExpression as NumericLiteralValue).number.toHex() @@ -181,15 +598,18 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, lda $name sta ${C64Zeropage.SCRATCH_W1} lda $name+1 - sta ${C64Zeropage.SCRATCH_W1+1} + sta ${C64Zeropage.SCRATCH_W1 + 1} ldy #0 lda (${C64Zeropage.SCRATCH_W1}),y eor #255 sta (${C64Zeropage.SCRATCH_W1}),y""") } - else -> throw AssemblyError("weird address value") - } } - arrayIdx!=null -> { + else -> { + TODO("$memory") + } + } + } + arrayIdx != null -> { TODO("in-place invert ubyte array") } } diff --git a/examples/test.p8 b/examples/test.p8 index def5f62be..5cfef7ac8 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,22 +7,79 @@ main { sub start() { - @($d020) += @($d020) ; TODO fix compiler hang + ;ubyte @(addr)=0 + uword addr = $02 + uword addr2 = $03 + ubyte B = 22 + ; all optimized: + @(addr) = B + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr2-1) = @(addr2-1) +33 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') - ubyte A + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = 33 + @(addr) + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') - A = 44+A + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = (@(addr) + 33) + B + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') -; ubyte A = 10 -; @($c00a) = $4a -; @($c000+A) ++ ; TODO implement this -; -; c64scr.print_ubhex(@($c00a), true) -; c64.CHROUT('\n') -; @($c000+A) -- ; TODO implement this -; -; c64scr.print_ubhex(@($c00a), true) -; c64.CHROUT('\n') + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = (33 + @(addr)) + B + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = (@(addr) + B) + 33 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = (B + @(addr)) + 33 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = B+ (@(addr) + 33) + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = B+(33 + @(addr)) + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = 33+(@(addr) + B) + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + + @(addr) = 11 + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') + @(addr) = 33+(B + @(addr)) + c64scr.print_ub(@(addr)) + c64.CHROUT('\n') } }