mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
started pointer access optimization
This commit is contained in:
parent
c328e9018c
commit
4ed7fb771c
@ -558,44 +558,6 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
fun storeByteIntoPointer(pointervar: IdentifierReference, ldaInstructionArg: String?) {
|
||||
val sourceName = asmVariableName(pointervar)
|
||||
val vardecl = pointervar.targetVarDecl(program.namespace)!!
|
||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
|
||||
if (isZpVar(scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
if (ldaInstructionArg != null)
|
||||
out(" lda $ldaInstructionArg")
|
||||
out(" sta ($sourceName)")
|
||||
} else {
|
||||
out("""
|
||||
ldy $sourceName
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy $sourceName+1
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
${if (ldaInstructionArg == null) "" else "lda $ldaInstructionArg"}
|
||||
sta (P8ZP_SCRATCH_W2)""")
|
||||
}
|
||||
} else {
|
||||
if (isZpVar(scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
if (ldaInstructionArg != null)
|
||||
out(" lda $ldaInstructionArg")
|
||||
out(" ldy #0 | sta ($sourceName),y")
|
||||
} else {
|
||||
out("""
|
||||
ldy $sourceName
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy $sourceName+1
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
${if (ldaInstructionArg == null) "" else "lda $ldaInstructionArg"}
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W2),y""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||
|
||||
internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
|
||||
@ -1369,4 +1331,80 @@ $label nop""")
|
||||
val vardecl = variable.targetVarDecl(program.namespace)!!
|
||||
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
|
||||
}
|
||||
|
||||
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {
|
||||
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
|
||||
val leftDt = pointerOffsetExpr.left.inferType(program)
|
||||
val rightDt = pointerOffsetExpr.left.inferType(program)
|
||||
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UBYTE))
|
||||
return Pair(pointerOffsetExpr.left, pointerOffsetExpr.right)
|
||||
if(leftDt.istype(DataType.UBYTE) && rightDt.istype(DataType.UWORD))
|
||||
return Pair(pointerOffsetExpr.right, pointerOffsetExpr.left)
|
||||
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UWORD)) {
|
||||
// could be that the index was a constant numeric byte but converted to word, check that
|
||||
val constIdx = pointerOffsetExpr.right.constValue(program)
|
||||
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
|
||||
return Pair(pointerOffsetExpr.left, NumericLiteralValue(DataType.UBYTE, constIdx.number, constIdx.position))
|
||||
}
|
||||
// could be that the index was type casted into uword, check that
|
||||
val rightTc = pointerOffsetExpr.right as? TypecastExpression
|
||||
if(rightTc!=null && rightTc.expression.inferType(program).istype(DataType.UBYTE))
|
||||
return Pair(pointerOffsetExpr.left, rightTc.expression)
|
||||
val leftTc = pointerOffsetExpr.left as? TypecastExpression
|
||||
if(leftTc!=null && leftTc.expression.inferType(program).istype(DataType.UBYTE))
|
||||
return Pair(pointerOffsetExpr.right, leftTc.expression)
|
||||
}
|
||||
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean {
|
||||
// optimize pointer,indexregister if possible
|
||||
if(expr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
|
||||
if(ptrAndIndex!=null) {
|
||||
val pointervar = ptrAndIndex.first as? IdentifierReference
|
||||
if(write) {
|
||||
when(ptrAndIndex.second) {
|
||||
is NumericLiteralValue, is IdentifierReference -> {
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
out(" sta (${asmSymbolName(pointervar)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// same as above but we need to save the A register
|
||||
out(" pha")
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
out(" pla")
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
out(" sta (${asmSymbolName(pointervar)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
out(" lda (${asmSymbolName(pointervar)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
out(" lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -197,8 +197,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val number = (what.addressExpression as NumericLiteralValue).number
|
||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||
if(ptrAndIndex!=null) {
|
||||
TODO("memread via pointer+indexregister ${ptrAndIndex.first},${ptrAndIndex.second}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||
}
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
@ -240,11 +245,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val number = (what.addressExpression as NumericLiteralValue).number
|
||||
asmgen.out(" ror ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff ; modified""")
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||
if(ptrAndIndex!=null) {
|
||||
TODO("memread via pointer+indexregister ${ptrAndIndex.first},${ptrAndIndex.second}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff ; modified""")
|
||||
}
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
@ -286,8 +296,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val number = (what.addressExpression as NumericLiteralValue).number
|
||||
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||
if(ptrAndIndex!=null) {
|
||||
TODO("memread via pointer+indexregister ${ptrAndIndex.first},${ptrAndIndex.second}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||
}
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
@ -329,11 +344,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val number = (what.addressExpression as NumericLiteralValue).number
|
||||
asmgen.out(" rol ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff ; modified""")
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||
if(ptrAndIndex!=null) {
|
||||
TODO("memread via pointer+indexregister ${ptrAndIndex.first},${ptrAndIndex.second}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff ; modified""")
|
||||
}
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
@ -564,6 +584,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
// all other types of swap() calls are done via a temporary variable
|
||||
// TODO optimize swapping of pointer+indexregister
|
||||
|
||||
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
||||
return when (expr) {
|
||||
|
@ -1462,51 +1462,19 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
|
||||
if (pushResultOnEstack) {
|
||||
asmgen.out(" dex | lda (P8ZP_SCRATCH_W2) | sta P8ESTACK_LO+1,x")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
||||
}
|
||||
} else {
|
||||
if (pushResultOnEstack) {
|
||||
asmgen.out(" dex | ldy #0 | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x")
|
||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y | dex | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
|
||||
// try to optimize simple cases like assignment from ptr+1, ptr+2...
|
||||
if(expr.operator=="+") {
|
||||
// we can assume const operand has been moved to the right.
|
||||
val constOperand = expr.right.constValue(program)
|
||||
if(constOperand!=null) {
|
||||
val intIndex = constOperand.number.toInt()
|
||||
if(intIndex in 1..255) {
|
||||
val idref = expr.left as? IdentifierReference
|
||||
if(idref!=null && asmgen.isZpVar(idref)) {
|
||||
// pointer var is already in zp, we can indirect index immediately
|
||||
if (pushResultOnEstack) {
|
||||
asmgen.out(" dex | ldy #${intIndex} | lda (${asmgen.asmSymbolName(idref)}),y | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
asmgen.out(" ldy #${intIndex} | lda (${asmgen.asmSymbolName(idref)}),y")
|
||||
}
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
asmgen.assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
if (pushResultOnEstack) {
|
||||
asmgen.out(" dex | ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
asmgen.out(" ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
when(expr.addressExpression) {
|
||||
is NumericLiteralValue -> {
|
||||
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
|
||||
@ -1521,8 +1489,12 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
if(!tryOptimizedPointerAccess(expr.addressExpression as BinaryExpression))
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(expr.addressExpression as BinaryExpression, false)) {
|
||||
if(pushResultOnEstack)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
assignViaExprEval()
|
||||
}
|
||||
}
|
||||
else -> assignViaExprEval()
|
||||
}
|
||||
|
@ -121,31 +121,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
}
|
||||
|
||||
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
|
||||
// try to optimize simple cases like assignment from ptr+1, ptr+2...
|
||||
if(expr.operator=="+") {
|
||||
// we can assume const operand has been moved to the right.
|
||||
val constOperand = expr.right.constValue(program)
|
||||
if(constOperand!=null) {
|
||||
val intIndex = constOperand.number.toInt()
|
||||
if(intIndex in 1..255) {
|
||||
val idref = expr.left as? IdentifierReference
|
||||
if(idref!=null && asmgen.isZpVar(idref)) {
|
||||
// pointer var is already in zp, we can indirect index immediately
|
||||
asmgen.out(" ldy #${intIndex} | lda (${asmgen.asmSymbolName(idref)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
|
||||
asmgen.out(" ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
val value = assign.source.memory!!
|
||||
when (value.addressExpression) {
|
||||
is NumericLiteralValue -> {
|
||||
@ -156,8 +131,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
if(!tryOptimizedPointerAccess(value.addressExpression as BinaryExpression))
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) {
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
} else {
|
||||
assignViaExprEval(value.addressExpression)
|
||||
}
|
||||
}
|
||||
else -> assignViaExprEval(value.addressExpression)
|
||||
}
|
||||
@ -331,14 +309,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
if(targetDt in WordDatatypes) {
|
||||
if (value.addressExpression is NumericLiteralValue) {
|
||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||
assignMemoryByteIntoWord(target, address, null)
|
||||
return
|
||||
}
|
||||
else if (value.addressExpression is IdentifierReference) {
|
||||
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference)
|
||||
return
|
||||
when (value.addressExpression) {
|
||||
is NumericLiteralValue -> {
|
||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||
assignMemoryByteIntoWord(target, address, null)
|
||||
return
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference)
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
TODO("memread from expression ${value.addressExpression}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -749,8 +732,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
asmgen.out(" inx")
|
||||
storeByteViaRegisterAInMemoryAddress("P8ESTACK_LO,x", target.memory!!)
|
||||
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(target.constArrayIndexValue!=null) {
|
||||
@ -1178,7 +1161,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
|
||||
asmgen.out(" lda $sourceName")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@ -1338,7 +1322,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" st${register.name.toLowerCase()} ${target.asmVarname}")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
storeRegisterInMemoryAddress(register, target.memory!!)
|
||||
TODO("assignRegisterByte via storeByteViaRegisterAInMemoryAddress()")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@ -1608,7 +1592,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" stz ${target.asmVarname} ")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
|
||||
asmgen.out(" lda #${byte.toHex()}")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@ -1647,7 +1632,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname} ")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
|
||||
asmgen.out(" lda #${byte.toHex()}")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@ -1831,7 +1817,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
storeByteViaRegisterAInMemoryAddress(address.toHex(), target.memory!!)
|
||||
asmgen.out(" lda ${address.toHex()}")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
throw AssemblyError("no asm gen for assign memory byte at $address to array ${target.asmVarname}")
|
||||
@ -1869,7 +1856,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
val sourceName = asmgen.asmVariableName(identifier)
|
||||
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
|
||||
asmgen.out(" lda $sourceName")
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${target.asmVarname} ")
|
||||
@ -1965,117 +1953,73 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
|
||||
private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) {
|
||||
val addressExpr = memoryAddress.addressExpression
|
||||
val addressLv = addressExpr as? NumericLiteralValue
|
||||
|
||||
fun storeViaExprEval() {
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" lda $ldaInstructionArg | ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
|
||||
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
|
||||
// try to optimize simple cases like storing value to ptr+1, ptr+2...
|
||||
if(expr.operator=="+") {
|
||||
// we can assume const operand has been moved to the right.
|
||||
val constOperand = expr.right.constValue(program)
|
||||
if(constOperand!=null) {
|
||||
val intIndex = constOperand.number.toInt()
|
||||
if(intIndex in 1..255) {
|
||||
val idref = expr.left as? IdentifierReference
|
||||
if(idref!=null && asmgen.isZpVar(idref)) {
|
||||
// pointer var is already in zp, we can indirect index immediately
|
||||
asmgen.out(" lda $ldaInstructionArg | ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.out(" lda $ldaInstructionArg | ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
return true
|
||||
}
|
||||
when(addressExpr) {
|
||||
is NumericLiteralValue, is IdentifierReference -> {
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
else -> {
|
||||
// same as above but we need to save the A register
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.out(" pla")
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun storeAIntoPointerVar(pointervar: IdentifierReference) {
|
||||
val sourceName = asmgen.asmVariableName(pointervar)
|
||||
val vardecl = pointervar.targetVarDecl(program.namespace)!!
|
||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
|
||||
if (asmgen.isZpVar(scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.out(" sta ($sourceName)")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy $sourceName
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy $sourceName+1
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
sta (P8ZP_SCRATCH_W2)""")
|
||||
}
|
||||
} else {
|
||||
if (asmgen.isZpVar(scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.out(" ldy #0 | sta ($sourceName),y")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy $sourceName
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy $sourceName+1
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W2),y""")
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
when {
|
||||
addressLv != null -> {
|
||||
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
|
||||
asmgen.out(" sta ${addressLv.number.toHex()}")
|
||||
}
|
||||
addressExpr is IdentifierReference -> {
|
||||
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
|
||||
storeAIntoPointerVar(addressExpr)
|
||||
}
|
||||
addressExpr is BinaryExpression -> {
|
||||
if(!tryOptimizedPointerAccess(addressExpr))
|
||||
storeViaExprEval()
|
||||
}
|
||||
else -> storeViaExprEval()
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
|
||||
// this is optimized for register A.
|
||||
val addressExpr = memoryAddress.addressExpression
|
||||
val addressLv = addressExpr as? NumericLiteralValue
|
||||
val registerName = register.name.toLowerCase()
|
||||
|
||||
fun storeViaExprEval() {
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
|
||||
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
|
||||
// try to optimize simple cases like storing value to ptr+1, ptr+2...
|
||||
if(expr.operator=="+") {
|
||||
// we can assume const operand has been moved to the right.
|
||||
val constOperand = expr.right.constValue(program)
|
||||
if(constOperand!=null) {
|
||||
val intIndex = constOperand.number.toInt()
|
||||
if(intIndex in 1..255) {
|
||||
val idref = expr.left as? IdentifierReference
|
||||
if(idref!=null && asmgen.isZpVar(idref)) {
|
||||
// pointer var is already in zp, we can indirect index immediately
|
||||
when(register) {
|
||||
CpuRegister.A -> asmgen.out(" ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
|
||||
CpuRegister.X -> asmgen.out(" txa | ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
|
||||
CpuRegister.Y -> asmgen.out(" tya | ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
|
||||
}
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out(" ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
when {
|
||||
addressLv != null -> {
|
||||
asmgen.out(" st$registerName ${addressLv.number.toHex()}")
|
||||
}
|
||||
addressExpr is IdentifierReference -> {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
asmgen.storeByteIntoPointer(addressExpr, null)
|
||||
}
|
||||
addressExpr is BinaryExpression -> {
|
||||
if(!tryOptimizedPointerAccess(addressExpr))
|
||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
|
||||
storeViaExprEval()
|
||||
}
|
||||
else -> storeViaExprEval()
|
||||
|
@ -2,15 +2,18 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- why is fibonacci example (and others) generating larger code now? fix?
|
||||
- optimize pointer access @(ptr+ix) if ix<=255 (i.e. if it is of ubyte type) to use indexing register
|
||||
- add any2(), all2(), max2(), min2(), reverse2(), sum2(), sort2() that take (array, startindex, length) arguments
|
||||
- allow uwordpointer[index] syntax -> transform into @(uwordpointer+index) allow index to be >255!
|
||||
- optimize for loop iterations better to allow proper inx, cpx #value, bne loop instructions
|
||||
|
||||
- can we get rid of the --longOptionName command line options and only keep the short versions? https://github.com/Kotlin/kotlinx-cli/issues/50
|
||||
- add a f_seek() routine for the Cx16 that uses its seek dos api?
|
||||
- add a compiler option to generate a symbol listing at the end
|
||||
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
|
||||
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
||||
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
||||
- c64: use VIC banking to move up the graphics bitmap memory location. Move it to $e000 under the kernal rom?
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable
|
||||
- some support for recursive subroutines?
|
||||
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
|
||||
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables
|
||||
|
@ -7,40 +7,29 @@ main {
|
||||
|
||||
|
||||
sub start() {
|
||||
str name = "abcdef"
|
||||
uword screen=$0400
|
||||
ubyte[256] xbuf = 1
|
||||
ubyte[256] ybuf = 3
|
||||
|
||||
uword ptr = &name
|
||||
|
||||
ubyte ix = 0
|
||||
ubyte cc
|
||||
|
||||
cc = @(ptr)
|
||||
txt.chrout(cc)
|
||||
txt.nl()
|
||||
cc = @(ptr+1)
|
||||
txt.chrout(cc)
|
||||
txt.nl()
|
||||
cc = @(ptr+2)
|
||||
txt.chrout(cc)
|
||||
txt.nl()
|
||||
txt.nl()
|
||||
; cc = @(screen+2)
|
||||
; cc++
|
||||
; @(screen+2) = cc
|
||||
|
||||
cc=0
|
||||
txt.chrout(@(ptr)+cc)
|
||||
txt.chrout(@(ptr+1)+cc)
|
||||
txt.chrout(@(ptr+2)+cc)
|
||||
txt.nl()
|
||||
@(screen + ix + cc*$0008) = cc
|
||||
|
||||
@(ptr) = '1'
|
||||
@(ptr+1) = '2'
|
||||
@(ptr+2) = '3'
|
||||
txt.print(name)
|
||||
txt.nl()
|
||||
|
||||
cc=0
|
||||
@(ptr+cc) = 'a'
|
||||
@(ptr+cc+1) = 'b'
|
||||
@(ptr+cc+2) = 'c'
|
||||
txt.print(name)
|
||||
txt.nl()
|
||||
; cc = @(screen+ix)
|
||||
; cc++
|
||||
; @(screen+ix) = cc
|
||||
; for ii in 24 downto 0 {
|
||||
; for i in 39 downto 0 {
|
||||
; @(screen+i) = xbuf[i] + ybuf[ii]
|
||||
; }
|
||||
; screen+=40
|
||||
; }
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user