mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
fix more 6502-only struct pointer-field bugs; add indexed in-place support
fixes #237
This commit is contained in:
1 parent
e811ecdbde
commit
af06fdfffc
7 files changed
+157
-55
No files matched your search
@@ -1498,7 +1498,7 @@ $repeatLabel""")
|
||||
}
|
||||
else if(swap.target1.pointerDeref!=null && swap.target2.pointerDeref!=null) {
|
||||
val v1 = swap.target1.pointerDeref!!
|
||||
val v2 = swap.target1.pointerDeref!!
|
||||
val v2 = swap.target2.pointerDeref!!
|
||||
if(v1.derefLast && v1.chain.isEmpty() && v2.derefLast && v2.chain.isEmpty() && isZpVar(v1.startpointer) && isZpVar(v2.startpointer)) {
|
||||
// optimized case where v1 and v2 are both already zeropage pointer variables
|
||||
val name1 = asmVariableName(v1.startpointer)
|
||||
|
||||
@@ -33,8 +33,8 @@ import prog8.codegen.cpu6502.assignment.*
|
||||
return when (fcall.name) {
|
||||
"msw" -> funcMsw(fcall, firstReturnRegister ?: RegisterOrPair.AY)
|
||||
"lsw" -> funcLsw(fcall, firstReturnRegister ?: RegisterOrPair.AY)
|
||||
"msb" -> funcMsb(fcall, firstReturnRegister ?: RegisterOrPair.A)
|
||||
"msb__long" -> funcMsbLong(fcall, firstReturnRegister ?: RegisterOrPair.A)
|
||||
"msb" -> funcMsb(fcall, false, firstReturnRegister ?: RegisterOrPair.A)
|
||||
"msb__long" -> funcMsb(fcall, true, firstReturnRegister ?: RegisterOrPair.A)
|
||||
"lsb" -> funcLsb(fcall, false, firstReturnRegister ?: RegisterOrPair.A)
|
||||
"lsb__long" -> funcLsb(fcall,true, firstReturnRegister ?: RegisterOrPair.A)
|
||||
"mkword" -> funcMkword(fcall, firstReturnRegister ?: RegisterOrPair.AY)
|
||||
@@ -2426,55 +2426,36 @@ import prog8.codegen.cpu6502.assignment.*
|
||||
return arrayOf(RegisterOrPair.AY)
|
||||
}
|
||||
|
||||
private fun funcMsbLong(fcall: PtFunctionCall, resultReg: RegisterOrPair): Array<RegisterOrPair> {
|
||||
private fun funcMsb(fcall: PtFunctionCall, fromLong: Boolean, resultReg: RegisterOrPair): Array<RegisterOrPair> {
|
||||
val arg = fcall.args.single()
|
||||
if (!arg.type.isLong)
|
||||
throw AssemblyError("msb__long requires long argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
when(resultReg) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda $sourceName+3")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+3")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+3")
|
||||
else -> throw AssemblyError("invalid register for msb long: $resultReg ${arg.position}")
|
||||
}
|
||||
} else if(arg is PtArrayIndexer) {
|
||||
TODO("msb of long array element ${fcall.position}")
|
||||
if(fromLong) {
|
||||
if (!arg.type.isLong)
|
||||
throw AssemblyError("msb__long requires long argument")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15, arg.type.isSigned)
|
||||
when(resultReg) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda cx16.r14+3")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx cx16.r14+3")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy cx16.r14+3")
|
||||
else -> throw AssemblyError("invalid register for msb long: $resultReg ${arg.position}")
|
||||
}
|
||||
if (!arg.type.isWord && !arg.type.isPointer)
|
||||
throw AssemblyError("msb requires word argument")
|
||||
}
|
||||
|
||||
return arrayOf(resultReg)
|
||||
}
|
||||
|
||||
private fun funcMsb(fcall: PtFunctionCall, resultReg: RegisterOrPair): Array<RegisterOrPair> {
|
||||
val arg = fcall.args.single()
|
||||
if (!arg.type.isWord)
|
||||
throw AssemblyError("msb requires word argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
|
||||
val offset = if(fromLong) 3 else 1
|
||||
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
when(resultReg) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||
else -> throw AssemblyError("invalid register for msb: $resultReg ${arg.position}")
|
||||
RegisterOrPair.A -> asmgen.out(" lda $sourceName+$offset")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+$offset")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+$offset")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+$offset | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+$offset | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+$offset | ldy #0")
|
||||
else -> throw AssemblyError("invalid register for msb${if(fromLong) " long" else ""}: $resultReg ${arg.position}")
|
||||
}
|
||||
} else {
|
||||
if(arg is PtArrayIndexer) {
|
||||
if(fromLong)
|
||||
TODO("msb of long array element ${fcall.position}")
|
||||
|
||||
// just read the msb byte out of the word array
|
||||
if(arg.splitWords) {
|
||||
if(arg.variable==null)
|
||||
@@ -2496,15 +2477,15 @@ import prog8.codegen.cpu6502.assignment.*
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVar,y | ldx #0")
|
||||
}
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVar,y | ldy #0")
|
||||
}
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
|
||||
asmgen.out(" ldy $arrayVar,x | ldx #0")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid register for msb: $resultReg ${arg.position}")
|
||||
}
|
||||
} else {
|
||||
@@ -2540,6 +2521,14 @@ import prog8.codegen.cpu6502.assignment.*
|
||||
else -> throw AssemblyError("invalid register for msb: $resultReg ${arg.position}")
|
||||
}
|
||||
}
|
||||
} else if(fromLong) {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15, arg.type.isSigned)
|
||||
when(resultReg) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda cx16.r14+$offset")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx cx16.r14+$offset")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy cx16.r14+$offset")
|
||||
else -> throw AssemblyError("invalid register for msb long: $resultReg ${arg.position}")
|
||||
}
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
|
||||
asmgen.out(" tya")
|
||||
@@ -2554,7 +2543,7 @@ import prog8.codegen.cpu6502.assignment.*
|
||||
if(fromLong) {
|
||||
if (!arg.type.isLong) throw AssemblyError("lsb__long requires long")
|
||||
} else {
|
||||
if (!arg.type.isWord) throw AssemblyError("lsb requires word")
|
||||
if (!arg.type.isWord && !arg.type.isPointer) throw AssemblyError("lsb requires word")
|
||||
}
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||
|
||||
@@ -377,7 +377,7 @@ internal class AssignmentAsmGen(
|
||||
throw AssemblyError("weird array element type for pointer indexed read $elementDt")
|
||||
}
|
||||
} else {
|
||||
val (zpPtrVar, _) = pointergen.deref(pointerDeref, addOffsetToPointer = true)
|
||||
val (zpPtrVar, _) = pointergen.deref(pointerDeref, addOffsetToPointer = true, loadPointerFieldValue = true)
|
||||
|
||||
when {
|
||||
elementDt.isByteOrBool -> {
|
||||
|
||||
@@ -62,6 +62,63 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceModificationIndexedPointerField(target: AsmAssignTarget, operator: String, value: AsmAssignSource) {
|
||||
val array = target.array!!
|
||||
val elementDt = target.datatype
|
||||
if(!elementDt.isByteOrBool && !elementDt.isWord)
|
||||
throw AssemblyError("inplace modification indexed pointer field for type $elementDt not supported")
|
||||
val indexedTarget = IndexedPtrTarget(target)
|
||||
val ptr = ptrgen.indexedEffectiveAddress(indexedTarget)
|
||||
|
||||
val elementTemp = asmgen.createTempVarReused(elementDt.base, false, array)
|
||||
if(elementDt.isByteOrBool) {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptr),y
|
||||
sta $elementTemp""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptr),y
|
||||
sta $elementTemp
|
||||
iny
|
||||
lda ($ptr),y
|
||||
sta $elementTemp+1""")
|
||||
}
|
||||
|
||||
// save the computed address on the hardware stack so later code can't clobber it
|
||||
asmgen.out("""
|
||||
lda $ptr
|
||||
pha
|
||||
lda $ptr+1
|
||||
pha""")
|
||||
|
||||
// reuse the existing variable-target logic to modify the temp
|
||||
val tempTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, elementDt, null, target.position, variableAsmName = elementTemp)
|
||||
inplaceModification(tempTarget, operator, value)
|
||||
|
||||
// restore the address and store the temp back
|
||||
asmgen.out("""
|
||||
pla
|
||||
sta $ptr+1
|
||||
pla
|
||||
sta $ptr""")
|
||||
if(elementDt.isByteOrBool) {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda $elementTemp
|
||||
sta ($ptr),y""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda $elementTemp
|
||||
sta ($ptr),y
|
||||
iny
|
||||
lda $elementTemp+1
|
||||
sta ($ptr),y""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceModification(target: AsmAssignTarget, operator: String, value: AsmAssignSource) {
|
||||
|
||||
// the asm-gen code can deal with situations where you want to assign a byte into a word.
|
||||
@@ -247,7 +304,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
TODO("inplace modification array indexed pointer deref ${target.position}")
|
||||
inplaceModificationIndexedPointerField(target, operator, value)
|
||||
return
|
||||
}
|
||||
val targetArrayVar = target.array.variable!!
|
||||
|
||||
@@ -228,10 +228,11 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
|
||||
|
||||
internal fun deref(pointer: PtPointerDeref, addOffsetToPointer: Boolean=false): Pair<String, UByte> {
|
||||
internal fun deref(pointer: PtPointerDeref, addOffsetToPointer: Boolean=false, loadPointerFieldValue: Boolean=false): Pair<String, UByte> {
|
||||
// walk the pointer deref chain and leaves the final pointer value in a ZP var
|
||||
// this will often be the temp var P8ZP_SCRATCH_PTR but can also be the original pointer variable if it is already in zeropage and there is nothing to add to it
|
||||
// returns the ZP var to use as a pointer, and a Y register offset (which can be zero)
|
||||
// loadPointerFieldValue: for addOffsetToPointer=true, also load the value if the final field is a pointer-typed field (rather than returning its address).
|
||||
|
||||
fun addFieldOffsetToScratchPointer(fieldoffset: UInt) {
|
||||
if(fieldoffset==0u)
|
||||
@@ -276,8 +277,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
if(addOffsetToPointer) {
|
||||
asmgen.assignExpressionToVariable(pointer.startpointer, "P8ZP_SCRATCH_PTR", DataType.UWORD)
|
||||
addFieldOffsetToScratchPointer(field.second.toUInt())
|
||||
// if the target field is a pointer (or array indexed through a pointer), use its value as the base address
|
||||
if(pointer.derefLast || field.first.isPointer)
|
||||
if(pointer.derefLast || (loadPointerFieldValue && field.first.isPointer))
|
||||
updateScratchPointer()
|
||||
return "P8ZP_SCRATCH_PTR" to 0u
|
||||
} else {
|
||||
@@ -327,8 +327,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
} else {
|
||||
if(addOffsetToPointer) {
|
||||
addFieldOffsetToScratchPointer(fieldinfo.second.toUInt())
|
||||
// if the target field is a pointer, use its value as the base address
|
||||
if(fieldinfo.first.isPointer)
|
||||
if(loadPointerFieldValue && fieldinfo.first.isPointer)
|
||||
updateScratchPointer()
|
||||
return "P8ZP_SCRATCH_PTR" to 0u
|
||||
} else
|
||||
@@ -361,7 +360,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
// save scaled index to W2, compute base (field-corrected) into P8ZP_SCRATCH_PTR, then add
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta P8ZP_SCRATCH_W2 | lda P8ZP_SCRATCH_W1+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
val (basePtr, _) = deref(target.pointer, addOffsetToPointer = true)
|
||||
val (basePtr, _) = deref(target.pointer, addOffsetToPointer = true, loadPointerFieldValue = true)
|
||||
if(basePtr != "P8ZP_SCRATCH_PTR") {
|
||||
// deref returned the original zp pointer variable; copy it so we don't mutate it
|
||||
asmgen.out("""
|
||||
@@ -454,7 +453,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
inplaceByteInc(target, amount)
|
||||
} else {
|
||||
if (target.dt.isByte) inplaceByteAdd(target, value)
|
||||
else if (target.dt.isWord) inplaceWordAdd(target, value)
|
||||
else if (target.dt.isWord || target.dt.isPointer) inplaceWordAdd(target, value)
|
||||
else if (target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
|
||||
else if (target.dt.isLong) inplaceLongAdd(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
@@ -466,7 +465,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
inplaceByteDec(target, amount)
|
||||
} else {
|
||||
if (target.dt.isByte) inplaceByteSub(target, value)
|
||||
else if (target.dt.isWord) inplaceWordSub(target, value)
|
||||
else if (target.dt.isWord || target.dt.isPointer) inplaceWordSub(target, value)
|
||||
else if (target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
|
||||
else if (target.dt.isLong) inplaceLongSub(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
|
||||
@@ -253,6 +253,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
|
||||
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg)
|
||||
chunks = inplaceInstrs
|
||||
} else if(array?.pointerderef != null) {
|
||||
chunks = fallbackAssign(augAssign)
|
||||
} else {
|
||||
chunks = when (augAssign.operator) {
|
||||
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
|
||||
@@ -117,6 +117,61 @@ class TestExecution6502 : FunSpec({
|
||||
machine.assertMemory(4500+5, 1234 shr 8)
|
||||
}
|
||||
|
||||
test("more struct pointer field operations") {
|
||||
val src = $$"""
|
||||
%option no_sysinit
|
||||
%launcher none
|
||||
%address $1000
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
struct Node {
|
||||
bool flag
|
||||
^^uword uptr
|
||||
}
|
||||
^^Node @shared n = [true, 4000]
|
||||
^^Node @shared m = [true, 5000]
|
||||
^^uword @shared normalptr = 4000
|
||||
&ubyte poweroff = $f203
|
||||
&uword n_after_swap = $0300
|
||||
&uword m_after_swap = $0302
|
||||
&uword n_after_add = $0304
|
||||
&uword lsb_result = $0306
|
||||
&uword msb_result = $0308
|
||||
&uword idxinc_result = $030A
|
||||
|
||||
sub start() {
|
||||
swap(n.uptr, m.uptr)
|
||||
n_after_swap = n.uptr
|
||||
m_after_swap = m.uptr
|
||||
n.uptr += 2
|
||||
n_after_add = n.uptr
|
||||
n.uptr[3] = 100
|
||||
n.uptr[3]++
|
||||
idxinc_result = n.uptr[3]
|
||||
lsb_result = lsb(normalptr)
|
||||
msb_result = msb(normalptr)
|
||||
poweroff = 1
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val compileResult = compileText(Cx16Target(), false, src, outputDir)
|
||||
val machine = compileResult!!.simulate()
|
||||
machine.assertMemory(0x0300, 5000 and 0xff)
|
||||
machine.assertMemory(0x0301, 5000 shr 8)
|
||||
machine.assertMemory(0x0302, 4000 and 0xff)
|
||||
machine.assertMemory(0x0303, 4000 shr 8)
|
||||
machine.assertMemory(0x0304, 5004 and 0xff)
|
||||
machine.assertMemory(0x0305, 5004 shr 8)
|
||||
machine.assertMemory(0x0306, 160)
|
||||
machine.assertMemory(0x0308, 15)
|
||||
machine.assertMemory(0x030A, 101)
|
||||
machine.assertMemory(0x030B, 0)
|
||||
machine.assertMemory(5010, 101)
|
||||
machine.assertMemory(5011, 0)
|
||||
}
|
||||
|
||||
test("serial output") {
|
||||
val src = $$"""
|
||||
%encoding iso
|
||||
|
||||
Reference in new issue
Block a user