avoid fallback JSR peek for common case pointer+offset

This commit is contained in:
Irmen de Jong
2025-10-11 01:08:15 +02:00
parent 7e26ecb0b6
commit 07bb5c36bd
7 changed files with 71 additions and 42 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -1067,6 +1067,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = asmgen.asmVariableName(pointer)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.out(" lda ($varname),y")
} else if(addrExpr.operator in arrayOf("+", "-") && addrExpr.left is PtIdentifier) {
readValueFromPointerPlusOrMinOffset(addrExpr.left as PtIdentifier, addrExpr.operator, addrExpr.right, BaseDataType.BOOL)
} else fallback()
}
else -> fallback()
@@ -1112,6 +1114,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda ($varname),y
tay
txa""")
} else if(addrExpr.operator in arrayOf("+", "-") && addrExpr.left is PtIdentifier) {
readValueFromPointerPlusOrMinOffset(addrExpr.left as PtIdentifier, addrExpr.operator, addrExpr.right, BaseDataType.UWORD)
} else fallback()
}
else -> fallback()
@@ -1129,6 +1133,37 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun readValueFromPointerPlusOrMinOffset(ptr: PtIdentifier, operator: String, offset: PtExpression, dt: BaseDataType) {
val varname = asmgen.asmVariableName(ptr)
asmgen.assignExpressionToRegister(offset, RegisterOrPair.AY)
if(operator=="+")
asmgen.out("""
clc
adc $varname
sta P8ZP_SCRATCH_W1
tya
adc $varname+1
sta P8ZP_SCRATCH_W1+1""")
else
asmgen.out("""
sec
sbc $varname
sta P8ZP_SCRATCH_W1
tya
sbc $varname+1
sta P8ZP_SCRATCH_W1+1""")
if (dt.isByteOrBool) {
if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
asmgen.out(" lda (P8ZP_SCRATCH_W1)")
} else {
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W1),y")
}
} else if(dt.isWord) {
asmgen.out(" jsr prog8_lib.func_peekw.from_scratchW1")
} else throw AssemblyError("unsupported type for peek $dt")
}
private fun funcPeekL(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
// TODO optimize for the simple cases
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)

View File

@@ -435,6 +435,7 @@ func_peek .proc
; -- read the byte value on the address in AY, into A
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
from_scratchW1
ldy #0
lda (P8ZP_SCRATCH_W1),y
rts
@@ -444,6 +445,7 @@ func_peekw .proc
; -- read the word value on the address in AY, into AY
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
from_scratchW1
ldy #0
lda (P8ZP_SCRATCH_W1),y
pha
@@ -458,6 +460,7 @@ func_peekl .proc
; -- read the ;pmg value on the address in AY, into R0:R1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
from_scratchW1
ldy #0
lda (P8ZP_SCRATCH_W1),y
sta cx16.r0

View File

@@ -297,6 +297,17 @@ _after:
val peek = FunctionCallExpression(IdentifierReference(listOf("peekbool"), arrayIndexedExpression.position), mutableListOf(address), arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, peek, parent))
}
} else if(arrayVar.datatype.sub==BaseDataType.LONG) {
// use peekl/pokel
if(parent is AssignTarget) {
val assignment = parent.parent as Assignment
val args = mutableListOf(address, assignment.value)
val poke = FunctionCallStatement(IdentifierReference(listOf("pokel"), arrayIndexedExpression.position), args, false, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(assignment, poke, assignment.parent))
} else {
val peek = FunctionCallExpression(IdentifierReference(listOf("peekl"), arrayIndexedExpression.position), mutableListOf(address), arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, peek, parent))
}
} else if(arrayVar.datatype.sub==BaseDataType.FLOAT) {
// use peekf/pokef
if(parent is AssignTarget) {

View File

@@ -1,12 +1,15 @@
TODO
====
- optimized translation for pokeX and peekX if address = pointer + uwordoffset.
STRUCTS and TYPED POINTERS
--------------------------
- implement the remaining TODO's in PointerAssignmentsGen.
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- optimize the float copying in assignIndexedPointer() (also word?)
- optimize the float copying in assignIndexedPointer() (also word and long?)
- optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment)
- implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
- support @nosplit pointer arrays?
@@ -24,6 +27,8 @@ Future Things and Ideas
This will break some existing programs that depend on value wrap arounds, but gives more intuitive constant number handling.
Can give descriptive error message for old syntax that still includes the type name?
- improve ANTLR grammar with better error handling (as suggested by Qwen AI)
- add documentation for more library modules instead of just linking to the source code
- add an Index to the documentation
- allow memory() to occur in array initializer
- when a complete block is removed because unused, suppress all info messages about everything in the block being removed
- fix the line, cols in Position, sometimes they count from 0 sometimes from 1

View File

@@ -36,7 +36,7 @@ irq {
; Here is the actual multiplexing routine.
; it's a raster irq just after the start of the sprite,
; that updates the Y position of all the sprits,
; and registers a new rater irq for that next row of sprites.
; and registers a new raster irq for that next row of sprites.
; If the bottom of the screen is reached, it resets the X position of the sprites as well,
; and moves the sprites back to the top of the screen.
sub multiplexer() -> bool {
@@ -51,12 +51,15 @@ irq {
first_sprite_X = 0
sprites.set_sprites_Y(sprites_Y)
while c64.RASTER != cx16.r2 {
; wait until raster line after sprite has been fully drawn (at least 24 lines down)
; wait until raster line after sprites have been fully drawn (at least 24 lines down)
}
sprites.set_sprites_X(first_sprite_X) ; we can now update the X positions without risk of sprite tearing
system_irq = true
} else {
; only set the new Y positions. But it's possible to change other attributes as well ofcourse (colors, x-position, data)
; but raster timing is critical for that if you want to avoid tearing and glitches. Can probably not be done here at this raster position...
sprites.set_sprites_Y(sprites_Y)
c64.SPXY[0]++
}
sys.set_rasterline(sprites_Y+1)

View File

@@ -3,45 +3,17 @@
main {
sub start() {
long @shared lv1 = -9999
; txt.print_uw(lsw(lv1))
; txt.spc()
; txt.print_w(lv1 as word)
; txt.spc()
; txt.print_uw(lv1 as uword)
; txt.spc()
; txt.print_b(lv1 as byte)
; txt.spc()
; txt.print_ub(lv1 as ubyte)
; txt.spc()
; txt.print_w(msw(lv1 << 8) as word)
; txt.spc()
; txt.print_w(lsw(lv1 >> 8) as word)
^^bool flags
^^word words
^^long longs
txt.nl()
txt.nl()
lv1 = -9876543
conv.str_ub0(123)
txt.print(conv.str_l(lv1))
txt.spc()
txt.print(conv.string_out)
txt.nl()
lv1 = 123456
txt.print(conv.str_l(lv1))
txt.spc()
txt.print(conv.string_out)
txt.nl()
lv1 = -2147483647
txt.print(conv.str_l(lv1))
txt.spc()
txt.print(conv.string_out)
txt.nl()
lv1 = 2147483647
txt.print(conv.str_l(lv1))
txt.spc()
txt.print(conv.string_out)
txt.nl()
; TODO optimized translation of the peekX and pokeX calls:
if flags[cx16.r0]
flags[cx16.r0] = true
if words[cx16.r0]!=0
words[cx16.r0] = 9990
if longs[cx16.r0]!=0
longs[cx16.r0] = 999999
}
}