implement some more in place pointer operators

This commit is contained in:
Irmen de Jong
2025-09-16 21:57:44 +02:00
parent ddf6e84a1a
commit efd73fd10d
3 changed files with 380 additions and 43 deletions
@@ -282,22 +282,26 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
"%" -> TODO("inplace ptr %")
"<<" -> {
if(target.dt.isByte) TODO("inplaceByteShiftLeft(target, value) ${target.position}")
if(target.dt.isByte) inplaceByteShiftLeft(target, value)
else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
else throw AssemblyError("weird dt ${target.position}")
}
">>" -> {
if(target.dt.isByte) TODO("inplaceByteShiftRight(target, value) ${target.position}")
if(target.dt.isByte) inplaceByteShiftRight(target, value)
else if(target.dt.isWord) inplaceWordShiftRight(target, value)
else throw AssemblyError("weird dt ${target.position}")
}
"&", "and" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
TODO("inplace ptr &")
if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
else if(target.dt.isWord) inplaceWordAnd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"|", "or" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
TODO("inplace ptr |")
if(target.dt.isByteOrBool) inplaceByteOr(target, value)
else if(target.dt.isWord) inplaceWordOr(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"^", "xor" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
@@ -305,12 +309,6 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
else if(target.dt.isWord) inplaceWordXor(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"==" -> TODO("inplace ptr ==")
"!=" -> TODO("inplace ptr !=")
"<" -> TODO("inplace ptr <")
"<=" -> TODO("inplace ptr <=")
">" -> TODO("inplace ptr >")
">=" -> TODO("inplace ptr >=")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -693,7 +691,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
val (zpPtrVar, offset) = deref(target.pointer)
if(target.dt.isSigned)
TODO("signed word shift rigth ${target.position} $value")
TODO("signed word shift right ${target.position} $value")
fun shift1unsigned() {
asmgen.out("""
@@ -720,21 +718,92 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord)
require(value.datatype.isByte)
val varname = value.asmVarname
TODO("<< variable")
asmgen.out(" ldx $varname")
asmgen.out("-")
shift1unsigned()
asmgen.out(" dex | bne -")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
TODO("<< expression")
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("-")
shift1unsigned()
asmgen.out(" dex | bne -")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isWord)
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR"))
require(register.isWord())
TODO("<< register")
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
asmgen.out("-")
shift1unsigned()
asmgen.out(" dex | bne -")
}
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceByteShiftRight(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
if(target.dt.isSigned)
TODO("signed byte shift right ${target.position} $value")
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(number==1) {
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
lsr a
sta ($zpPtrVar),y""")
} else if(number>1) {
asmgen.out("""
ldx #$number
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isByte)
val varname = value.asmVarname
asmgen.out("""
ldx $varname
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, null, target.position, register = RegisterOrPair.X))
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
}
else -> throw AssemblyError("weird source value $value")
}
@@ -768,21 +837,90 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord)
require(value.datatype.isByte)
val varname = value.asmVarname
TODO("<< variable")
asmgen.out(" ldx $varname")
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
TODO("<< expression")
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isWord)
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR"))
require(register.isWord())
TODO("<< register")
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
}
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceByteShiftLeft(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(number==1) {
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
asl a
sta ($zpPtrVar),y""")
} else if(number>1) {
asmgen.out("""
ldx #$number
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isByte)
val varname = value.asmVarname
asmgen.out("""
ldx $varname
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
asmgen.out("-")
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
}
else -> throw AssemblyError("weird source value $value")
}
@@ -1230,6 +1368,180 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
}
private fun inplaceByteOr(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out("""
lda ($zpPtrVar)
ora #$number
sta ($zpPtrVar)""")
else
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora #$number
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
val varname = value.asmVarname
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out("""
lda ($zpPtrVar)
ora $varname
sta ($zpPtrVar)""")
else
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora $varname
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
asmgen.out("""
ldy #$offset
ora ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register | byte")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceWordOr(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora #<$number
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #>$number
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora $varname
sta ($zpPtrVar),y
lda ($zpPtrVar),y
ora $varname+1
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
asmgen.out("""
ldy #$offset
ora ($zpPtrVar),y
sta ($zpPtrVar),y
iny
txa
ora ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register | word")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceByteAnd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out("""
lda ($zpPtrVar)
and #$number
sta ($zpPtrVar)""")
else
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and #$number
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
val varname = value.asmVarname
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out("""
lda ($zpPtrVar)
and $varname
sta ($zpPtrVar)""")
else
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and $varname
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
asmgen.out("""
ldy #$offset
and ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register & byte")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceWordAnd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and #<$number
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #>$number
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and $varname
sta ($zpPtrVar),y
lda ($zpPtrVar),y
and $varname+1
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
asmgen.out("""
ldy #$offset
and ($zpPtrVar),y
sta ($zpPtrVar),y
iny
txa
and ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register & word")
else -> throw AssemblyError("weird source value $value")
}
}
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
+21
View File
@@ -90,6 +90,7 @@ Future Things and Ideas
7 }
8 modifications.forEach { it.perform() }
9 }
- improve ANTLR grammar with better error handling (according to Qwen AI)
- allow memory() to occur in array initializer
- %breakpoint after an assignment is parsed as part of the expression (x % breakpoint), that should not happen
- when a complete block is removed because unused, suppress all info messages about everything in the block being removed
@@ -120,6 +121,26 @@ Future Things and Ideas
IR/VM
-----
- is it possible to use LOADFIELD/STOREFIELD instructions more?
- make multiple classes of registers and maybe also categorize by life time , to prepare for better register allocation in the future
SYSCALL_ARGS, // Reserved for syscall arguments (r99000-99099, r99100-99199)
FUNCTION_PARAMS, // For passing function parameters
FUNCTION_RETURNS, // For function return values
TEMPORARY, // Short-lived temporary values
LOCAL_VARIABLES, // Local variables within functions
GLOBAL_VARIABLES, // Global/static variables
HARDWARE_MAPPED, // Mapped to CPU hardware registers
LOOP_INDICES, // Used as loop counters
ADDRESS_CALCULATION // Used for pointer arithmetic
Categorizing registers by lifetime can significantly improve allocation:
- Short-lived: Temporary registers used in expressions
- Medium-lived: Local variables within a function
Registers could be categorized by how frequently they're accessed:
- Hot Registers: Frequently accessed (should be allocated to faster physical registers)
- Warm Registers: Moderately accessed
- Cold Registers: Rarely accessed (can be spilled to memory if needed)
We already have type-based pools
- byte, word, float registers
- pointer dt's are all reduced to just an uword (in the irTypeString method) - is this okay or could it be beneficial to reintroduce the actual pointer type information? See commit 88b074c208450c58aa32469745afa03e4c5f564a
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
+18 -14
View File
@@ -13,20 +13,24 @@ main {
}
sub start() {
^^Node[] @shared nodeswithtype = [
^^Node: [1,"one", 1000, true, 1.111],
^^Node: [],
]
^^Node test = []
^^Node derp2 = ^^Foobar: []
^^Node[] @shared nodeswithout = [
[2,"two", 2000, false, 2.222],
[1,2,3,true,5],
[]
]
^^Node @shared nptrwithtype = ^^Node : [1, "one", 1000, false, 3.333]
^^Node @shared nptrwithouttype = [1, "one", 1000, false, 3.333]
test.id ++
test.array += 1000
test.id <<= 2
test.id <<= cx16.r0L
test.id >>= 3
test.id >>= cx16.r0L
test.id &= 1
; test.id *= 5 ; TODO implement this
; test.id /= 5 ; TODO implement this
test.array ^= 1000
test.array |= 1000
test.array &= 1000
test.array >>= 3
test.array >>= cx16.r0L
test.array <<= 2
test.array <<= cx16.r0L
test.array *= 5
}
}