diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 96811fd00..08fe02d71 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -1164,12 +1164,13 @@ $repeatLabel""") } } - internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair? { + internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression, allowNegativeIndex: Boolean=false): Pair? { if (pointerOffsetExpr !is PtBinaryExpression) return null val operator = pointerOffsetExpr.operator val left = pointerOffsetExpr.left val right = pointerOffsetExpr.right - if (operator != "+") return null + if (operator != "+" && (operator != "-" || !allowNegativeIndex)) + return null val leftDt = left.type val rightDt = right.type if(leftDt.isUnsignedWord && rightDt.isUnsignedByte) @@ -1212,70 +1213,166 @@ $repeatLabel""") } if(addressExpr.operator=="+") { - val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr) - if(ptrAndIndex!=null) { + val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, false) + if (ptrAndIndex == null) return false + + if(write) { + + // WRITING TO pointer + offset + + val addrOf = ptrAndIndex.first as? PtAddressOf + val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt() + if(addrOf!=null && constOffset!=null) { + if(addrOf.isFromArrayElement) { + TODO("address-of array element $addrOf") + } else { + out(" sta ${asmSymbolName(addrOf.identifier)}+${constOffset}") + return true + } + } + val pointervar = ptrAndIndex.first as? PtIdentifier - val target = if(pointervar==null) null else symbolTable.lookup(pointervar.name)!!.astNode - when(target) { - is PtLabel -> { + if(pointervar!=null && isZpVar(pointervar)) { + val saveA = evalBytevalueWillClobberA(ptrAndIndex.second) + if(saveA) out(" pha") + assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) + if(saveA) out(" pla") + out(" sta (${asmSymbolName(pointervar)}),y") + } else { + // copy the pointer var to zp first + val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second) + if(saveA) out(" pha") + if(ptrAndIndex.second.isSimple()) { + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) - out(" lda ${asmSymbolName(pointervar!!)},y") - return true + if(saveA) out(" pla") + out(" sta (P8ZP_SCRATCH_W2),y") + } else { + pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second) + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) + restoreRegisterStack(CpuRegister.Y, true) + if(saveA) out(" pla") + out(" sta (P8ZP_SCRATCH_W2),y") } - is IPtVariable, null -> { - if(write) { - if(pointervar!=null && isZpVar(pointervar)) { - val saveA = evalBytevalueWillClobberA(ptrAndIndex.second) - if(saveA) - out(" pha") - assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) - if(saveA) - out(" pla") - out(" sta (${asmSymbolName(pointervar)}),y") - } else { - // copy the pointer var to zp first - val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second) - if(saveA) - out(" pha") - if(ptrAndIndex.second.isSimple()) { - assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) - assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) - if(saveA) - out(" pla") - out(" sta (P8ZP_SCRATCH_W2),y") - } else { - pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second) - assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) - restoreRegisterStack(CpuRegister.Y, true) - if(saveA) - out(" pla") - out(" sta (P8ZP_SCRATCH_W2),y") - } - } + } + return true + } + + // READING FROM pointer + offset + + val addrOf = ptrAndIndex.first as? PtAddressOf + val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt() + if(addrOf!=null && constOffset!=null) { + if(addrOf.isFromArrayElement) { + TODO("address-of array element $addrOf") + } else { + out(" lda ${asmSymbolName(addrOf.identifier)}+${constOffset}") + return true + } + } + + val pointervar = ptrAndIndex.first as? PtIdentifier + val targetVariable = if(pointervar==null) null else symbolTable.lookup(pointervar.name)!!.astNode + when(targetVariable) { + is PtLabel -> { + assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) + out(" lda ${asmSymbolName(pointervar!!)},y") + return true + } + is IPtVariable, null -> { + if(pointervar!=null && isZpVar(pointervar)) { + assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) + out(" lda (${asmSymbolName(pointervar)}),y") + } else { + // copy the pointer var to zp first + if(ptrAndIndex.second.isSimple()) { + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) + assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) + out(" lda (P8ZP_SCRATCH_W2),y") } else { - if(pointervar!=null && isZpVar(pointervar)) { - assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) - out(" lda (${asmSymbolName(pointervar)}),y") - } else { - // copy the pointer var to zp first - if(ptrAndIndex.second.isSimple()) { - assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) - assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) - out(" lda (P8ZP_SCRATCH_W2),y") - } else { - pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second) - assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) - restoreRegisterStack(CpuRegister.Y, false) - out(" lda (P8ZP_SCRATCH_W2),y") - } - } + pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second) + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) + restoreRegisterStack(CpuRegister.Y, false) + out(" lda (P8ZP_SCRATCH_W2),y") } + } + return true + } + else -> throw AssemblyError("invalid pointervar $pointervar") + } + } + + else if(addressExpr.operator=="-") { + val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, true) + if (ptrAndIndex == null) return false + + if(write) { + + // WRITING TO pointer - offset + + val addrOf = ptrAndIndex.first as? PtAddressOf + val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt() + if(addrOf!=null && constOffset!=null) { + if(addrOf.isFromArrayElement) { + TODO("address-of array element $addrOf") + } else { + out(" sta ${asmSymbolName(addrOf.identifier)}-${constOffset}") return true } - else -> throw AssemblyError("invalid pointervar $pointervar") + } + + if(constOffset!=null) { + println("MEMWRITE POINTER - $constOffset ${addressExpr.position}") // TODO +/* + val pointervar = ptrAndIndex.first as? PtIdentifier + if(pointervar!=null && isZpVar(pointervar)) { + val saveA = evalBytevalueWillClobberA(ptrAndIndex.second) + if(saveA) out(" pha") + assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) + if(saveA) out(" pla") + out(" sta (${asmSymbolName(pointervar)}),y") + } else { + // copy the pointer var to zp first + val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second) + if(saveA) out(" pha") + if(ptrAndIndex.second.isSimple()) { + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) + assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) + if(saveA) out(" pla") + out(" sta (P8ZP_SCRATCH_W2),y") + } else { + pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second) + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) + restoreRegisterStack(CpuRegister.Y, true) + if(saveA) out(" pla") + out(" sta (P8ZP_SCRATCH_W2),y") + } + } + return true +*/ + } + } else { + + // READING FROM pointer - offset + + val addrOf = ptrAndIndex.first as? PtAddressOf + val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt() + if(addrOf!=null && constOffset!=null) { + if(addrOf.isFromArrayElement) { + TODO("address-of array element $addrOf") + } else { + out(" lda ${asmSymbolName(addrOf.identifier)}-${constOffset}") + return true + } + } + + if(constOffset!=null) { + println("MEMREAD POINTER - $constOffset ${addressExpr.position}") // TODO + // TODO optimize more cases } } } + return false } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 2905282e2..3b9a510f9 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -306,38 +306,29 @@ internal class AssignmentAsmGen( } } } - SourceStorageKind.MEMORY -> { - val value = assign.source.memory!! - when (value.address) { - is PtNumber -> { - val address = (value.address as PtNumber).number.toUInt() - assignMemoryByte(assign.target, address, null) - } - is PtIdentifier -> { - assignMemoryByte(assign.target, null, value.address as PtIdentifier) - } - is PtBinaryExpression -> { - val addrExpr = value.address as PtBinaryExpression - if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) { - assignRegisterByte(assign.target, CpuRegister.A, false, true) - } else { - assignByteFromAddressExpression(value.address, assign.target) - } - } - else -> assignByteFromAddressExpression(value.address, assign.target) - } - } - SourceStorageKind.EXPRESSION -> { - assignExpression(assign, scope) - } - SourceStorageKind.REGISTER -> { - asmgen.assignRegister(assign.source.register!!, assign.target) - } + SourceStorageKind.MEMORY -> assignByteFromAddressExpression(assign.source.memory!!.address, assign.target) + SourceStorageKind.EXPRESSION -> assignExpression(assign, scope) + SourceStorageKind.REGISTER -> asmgen.assignRegister(assign.source.register!!, assign.target) } } private fun assignByteFromAddressExpression(address: PtExpression, target: AsmAssignTarget) { - if(address is PtBinaryExpression) { + + if (address is PtNumber) { + val address = address.number.toUInt() + assignMemoryByte(target, address, null) + return + } + else if (address is PtIdentifier) { + assignMemoryByte(target, null, address) + return + } + else if (address is PtBinaryExpression) { + if(asmgen.tryOptimizedPointerAccessWithA(address, false)) { + assignRegisterByte(target, CpuRegister.A, false, true) + return + } + if(address.operator=="+" && address.right.type.isUnsignedWord) { if (address.left is PtIdentifier) { // use (zp),Y instead of explicitly calculating the full zp pointer value @@ -376,9 +367,11 @@ internal class AssignmentAsmGen( } } // else if(address.operator=="-") { -// // does this ever occur? we could optimize it too, but it seems like a pathological case +// // TODO does this ever occur? we could optimize it too, but it seems like a pathological case // } } + + // fallback assignmen through temporary pointer var assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false) assignRegisterByte(target, CpuRegister.A, false, true) @@ -4010,18 +4003,6 @@ $endLabel""") asmgen.storeAIntoPointerVar(addressExpr) } addressExpr is PtBinaryExpression -> { - if(addressExpr.operator=="+" || addressExpr.operator=="-") { - val addrOf = addressExpr.left as? PtAddressOf - val offset = (addressExpr.right as? PtNumber)?.number?.toInt() - if(addrOf!=null && offset!=null) { - if(addrOf.isFromArrayElement) { - TODO("address-of array element $addrOf") - } else { - asmgen.out(" sta ${asmgen.asmSymbolName(addrOf.identifier)}${addressExpr.operator}${offset}") - return - } - } - } if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true)) storeViaExprEval() } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index b71ffcd5f..15b27778d 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -169,10 +169,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, else -> { if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value)) return + // slower method to calculate and use the pointer to access the memory with: asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false) asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.saveRegisterStack(CpuRegister.Y, true) - asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A_65c02") + else + asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A") when(value.kind) { SourceStorageKind.LITERALBOOLEAN -> { inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", false) @@ -212,7 +216,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } asmgen.restoreRegisterStack(CpuRegister.Y, false) asmgen.restoreRegisterStack(CpuRegister.A, false) - asmgen.out(" jsr prog8_lib.write_byte_X_to_address_in_AY") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" jsr prog8_lib.write_byte_X_to_address_in_AY_65c02") + else + asmgen.out(" jsr prog8_lib.write_byte_X_to_address_in_AY") } } } diff --git a/compiler/res/prog8lib/prog8_lib.asm b/compiler/res/prog8lib/prog8_lib.asm index a8e17c32c..ee5b94624 100644 --- a/compiler/res/prog8lib/prog8_lib.asm +++ b/compiler/res/prog8lib/prog8_lib.asm @@ -31,6 +31,13 @@ read_byte_from_address_in_AY_into_A .proc rts .pend +read_byte_from_address_in_AY_into_A_65c02 .proc + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + lda (P8ZP_SCRATCH_W2) + rts + .pend + write_byte_X_to_address_in_AY .proc sta P8ZP_SCRATCH_W2 @@ -41,6 +48,14 @@ write_byte_X_to_address_in_AY .proc rts .pend +write_byte_X_to_address_in_AY_65c02 .proc + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + txa + sta (P8ZP_SCRATCH_W2) + rts + .pend + reg_less_uw .proc ; AY < P8ZP_SCRATCH_W2? diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index 923068c2c..859dfceee 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -265,7 +265,7 @@ _after: override fun after(memread: DirectMemoryRead, parent: Node): Iterable { // for word variables: // @(&var) --> lsb(var) - // @(&var+1) --> msb(var) + // @(&var+1) --> msb(var) NOTE: ONLY WHEN VAR IS AN ACTUAL WORD VARIABLE (POINTER) val addrOf = memread.addressExpression as? AddressOf if(addrOf?.arrayIndex!=null) @@ -279,8 +279,11 @@ _after: val addressOf = expr.left as? AddressOf val offset = (expr.right as? NumericLiteral)?.number?.toInt() if(addressOf!=null && offset==1) { - val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(addressOf.identifier), memread.position) - return listOf(IAstModification.ReplaceNode(memread, msb, parent)) + val variable = addressOf.identifier.targetVarDecl(program) + if(variable!=null && variable.datatype.isWord) { + val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(addressOf.identifier), memread.position) + return listOf(IAstModification.ReplaceNode(memread, msb, parent)) + } } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e71a0428d..7b41e39f6 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,9 @@ TODO ==== +- optimize @(cell_ptr-offset) to use DEC pointer_msb ; LDY #255 ; INC pointer_msb instead. See tryOptimizedPointerAccessWithA() + + - add paypal donation button as well? - announce prog8 on the 6502.org site? @@ -83,7 +86,6 @@ Optimizations - if magicwall_enabled and (jiffy_counter & 3 == 1) sounds.magicwall() -> generates shortcut jump to another jump, why not immediately after the if - explode(x, y+1) pushes x on the stack and pops it, could simply load it in reverse order and not use the stack.normal - return mkword(attrs[cx16.r2L], object[cx16.r2L]) same as the explode() above -- @(cell_ptr-1) = objects.amoeba uses temp zp pointer, also when cell_ptr is zp already? - x = y + z more efficient if rewritten to x=y; x+=z ? - return peekw(table+64+pos*2) .... or rather .. return -> can this be optimized by using a temporary variable and chop up the expression? likewise cx16.r0 = (gfx_lores.WIDTH-bmx.width)/2 + (gfx_lores.HEIGHT-bmx.height)/2*gfx_lores.WIDTH a lot of register juggling diff --git a/examples/test.p8 b/examples/test.p8 index 3af58b0fc..9981afd02 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,23 +1,46 @@ %import textio %option no_sysinit -%zeropage basicsafe +%zeropage kernalsafe main { sub start() { - word @shared dx - uword @shared udx - dx++ - udx++ - dx = -5000 - if abs(dx) < 9999 - txt.print("yes1") - else - txt.print("no2") + ubyte[] array = [11,22,33,44,55,66,77,88,99] + uword @shared ptr = &array[5] + ubyte @shared offset + + + cx16.r0L = @(&start + 1) + cx16.r1L = @(&start - 1) + @(&start+1) = 99 + @(&start-1) = 99 + +; @(ptr+1) = cx16.r0L +; @(ptr+2) = cx16.r0L +; @(ptr+offset) = cx16.r0L +; @(ptr-1) = cx16.r0L +; @(ptr-2) = cx16.r0L +; @(ptr-offset) = cx16.r0L + + +; cx16.r0L = @(ptr+1) +; cx16.r1L = @(ptr+2) +; cx16.r2L = @(ptr+offset) +; cx16.r3L = @(ptr-1) +; cx16.r4L = @(ptr-2) +; cx16.r5L = @(ptr-offset) + + + +; %asm {{ +; dec p8v_ptr+1 +; ldy #255 +; lda (p8v_ptr),y +; inc p8v_ptr+1 +; sta cx16.r0L +; }} + + repeat { + } - dx = -15000 - if abs(dx) < 9999 - txt.print("yes2") - else - txt.print("no2") } }