tweak pointer access, fix and optimize reading memory from a label as pointer

This commit is contained in:
Irmen de Jong 2025-01-05 03:53:28 +01:00
parent 478e2b4ebd
commit 0ffebc25d0
7 changed files with 246 additions and 118 deletions

View File

@ -1164,12 +1164,13 @@ $repeatLabel""")
}
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression, allowNegativeIndex: Boolean=false): Pair<PtExpression, PtExpression>? {
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
}

View File

@ -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()
}

View File

@ -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")
}
}
}

View File

@ -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?

View File

@ -265,7 +265,7 @@ _after:
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// 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))
}
}
}

View File

@ -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 <complex expression> -> 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

View File

@ -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")
}
}