clean up X register save/store in compiler code, remove temp vars for register saving

This commit is contained in:
Irmen de Jong 2023-07-15 13:04:14 +02:00
parent b791fae9ce
commit e2bb0de24d
13 changed files with 114 additions and 237 deletions

View File

@ -124,9 +124,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null), "push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null), "pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
"rsave" to FSignature(false, emptyList(), null), "rsave" to FSignature(false, emptyList(), null),
"rsavex" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null), "rrestore" to FSignature(false, emptyList(), null),
"rrestorex" to FSignature(false, emptyList(), null),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), "memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD), "callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
) )

View File

@ -393,32 +393,6 @@ class AsmGen6502Internal (
return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars -> reference zp symbols directly return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars -> reference zp symbols directly
} }
internal fun saveRegisterLocal(register: CpuRegister, scope: IPtSubroutine) {
if (isTargetCpu(CpuType.CPU65c02)) {
// just use the cpu's stack for all registers, shorter code
when (register) {
CpuRegister.A -> out(" pha")
CpuRegister.X -> out(" phx")
CpuRegister.Y -> out(" phy")
}
} else {
when (register) {
CpuRegister.A -> {
// just use the stack, only for A
out(" pha")
}
CpuRegister.X -> {
out(" stx prog8_regsaveX")
subroutineExtra(scope).usedRegsaveX = true
}
CpuRegister.Y -> {
out(" sty prog8_regsaveY")
subroutineExtra(scope).usedRegsaveY = true
}
}
}
}
internal fun saveRegisterStack(register: CpuRegister, keepA: Boolean) { internal fun saveRegisterStack(register: CpuRegister, keepA: Boolean) {
when (register) { when (register) {
CpuRegister.A -> out(" pha") CpuRegister.A -> out(" pha")
@ -443,24 +417,6 @@ class AsmGen6502Internal (
} }
} }
internal fun restoreRegisterLocal(register: CpuRegister) {
if (isTargetCpu(CpuType.CPU65c02)) {
when (register) {
// this just used the stack, for all registers. Shorter code.
CpuRegister.A -> out(" pla")
CpuRegister.X -> out(" plx")
CpuRegister.Y -> out(" ply")
}
} else {
when (register) {
CpuRegister.A -> out(" pla") // this just used the stack but only for A
CpuRegister.X -> out(" ldx prog8_regsaveX")
CpuRegister.Y -> out(" ldy prog8_regsaveY")
}
}
}
internal fun restoreRegisterStack(register: CpuRegister, keepA: Boolean) { internal fun restoreRegisterStack(register: CpuRegister, keepA: Boolean) {
when (register) { when (register) {
CpuRegister.A -> { CpuRegister.A -> {
@ -586,12 +542,6 @@ class AsmGen6502Internal (
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) = internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) =
functioncallAsmGen.translateFunctionCall(functionCallExpr) functioncallAsmGen.translateFunctionCall(functionCallExpr)
internal fun saveXbeforeCall(functionCall: PtFunctionCall) =
functioncallAsmGen.saveXbeforeCall(functionCall)
internal fun restoreXafterCall(functionCall: PtFunctionCall) =
functioncallAsmGen.restoreXafterCall(functionCall)
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) = internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) =
assignmentAsmGen.translateNormalAssignment(assign, scope) assignmentAsmGen.translateNormalAssignment(assign, scope)
@ -3095,9 +3045,6 @@ $repeatLabel""")
* it's more consistent to only define these attributes on a Subroutine node. * it's more consistent to only define these attributes on a Subroutine node.
*/ */
internal class SubroutineExtraAsmInfo { internal class SubroutineExtraAsmInfo {
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar1 = false var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false var usedFloatEvalResultVar2 = false

View File

@ -68,9 +68,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub()) asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
} }
"rsave" -> funcRsave() "rsave" -> funcRsave()
"rsavex" -> funcRsaveX()
"rrestore" -> funcRrestore() "rrestore" -> funcRrestore()
"rrestorex" -> funcRrestoreX()
"cmp" -> funcCmp(fcall) "cmp" -> funcCmp(fcall)
"callfar" -> funcCallFar(fcall) "callfar" -> funcCallFar(fcall)
"prog8_lib_stringcompare" -> funcStringCompare(fcall) "prog8_lib_stringcompare" -> funcStringCompare(fcall)
@ -137,13 +135,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda P8ZP_SCRATCH_REG""") lda P8ZP_SCRATCH_REG""")
} }
private fun funcRsaveX() {
if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" phx")
else
asmgen.out(" txa | pha")
}
private fun funcRrestore() { private fun funcRrestore() {
if (asmgen.isTargetCpu(CpuType.CPU65c02)) if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(""" asmgen.out("""
@ -161,13 +152,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
plp""") plp""")
} }
private fun funcRrestoreX() {
if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" plx")
else
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1")
}
private fun funcCallFar(fcall: PtBuiltinFunctionCall) { private fun funcCallFar(fcall: PtBuiltinFunctionCall) {
if(asmgen.options.compTarget.name != "cx16") if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callfar only works on cx16 target at this time") throw AssemblyError("callfar only works on cx16 target at this time")
@ -445,16 +429,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else { } else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) { if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.restoreRegisterStack(CpuRegister.X, false)
asmgen.out(""" asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff,x ; modified""") + ror ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X)
} else { } else {
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
@ -550,16 +531,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else { } else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) { if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!) asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X) asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X) asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.restoreRegisterStack(CpuRegister.X, false)
asmgen.out(""" asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff,x ; modified""") + rol ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X)
} else { } else {
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
@ -677,7 +655,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = asmgen.asmVariableName(addrExpr) val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) { if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.isTargetCpu(CpuType.CPU65c02)) { if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out(""" asmgen.out("""
@ -693,7 +670,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
iny iny
sta ($varname),y""") sta ($varname),y""")
} }
asmgen.restoreRegisterLocal(CpuRegister.X)
return return
} }
} }
@ -703,18 +679,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) { if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
// can do ZP,Y indexing // can do ZP,Y indexing
val varname = asmgen.asmVariableName(pointer) val varname = asmgen.asmVariableName(pointer)
val scope = fcall.definingISub()!!
asmgen.saveRegisterLocal(CpuRegister.X, scope)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y) asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.saveRegisterLocal(CpuRegister.Y, scope) asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
asmgen.restoreRegisterLocal(CpuRegister.Y) asmgen.restoreRegisterStack(CpuRegister.Y, true)
asmgen.out(""" asmgen.out("""
sta ($varname),y sta ($varname),y
txa txa
iny iny
sta ($varname),y""") sta ($varname),y""")
asmgen.restoreRegisterLocal(CpuRegister.X)
return return
} }
} }

View File

@ -6,15 +6,6 @@ import prog8.code.ast.PtSub
import prog8.code.core.* import prog8.code.core.*
internal fun IPtSubroutine.regXasResult(): Boolean =
(this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal fun IPtSubroutine.shouldSaveX(): Boolean =
this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam()))
internal fun PtAsmSub.regXasParam(): Boolean =
parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean) internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
internal fun PtAsmSub.shouldKeepA(): KeepAresult { internal fun PtAsmSub.shouldKeepA(): KeepAresult {

View File

@ -11,42 +11,10 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) { internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) { internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
saveXbeforeCall(stmt)
translateFunctionCall(stmt) translateFunctionCall(stmt)
restoreXafterCall(stmt)
// just ignore any result values from the function call. // just ignore any result values from the function call.
} }
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
} else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
}
}
internal fun restoreXafterCall(stmt: PtFunctionCall) {
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
} else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
internal fun optimizeIntArgsViaRegisters(sub: PtSub) = internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes) (sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) || (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)

View File

@ -13,7 +13,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
val targetIdent = stmt.target.identifier val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memory val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.array val targetArrayIdx = stmt.target.array
val scope = stmt.definingISub()
when { when {
targetIdent!=null -> { targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent) val what = asmgen.asmVariableName(targetIdent)
@ -76,7 +75,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
dec ${asmArrayvarname}_msb+$constIndex dec ${asmArrayvarname}_msb+$constIndex
+ dec ${asmArrayvarname}_lsb+$constIndex""") + dec ${asmArrayvarname}_lsb+$constIndex""")
} else { } else {
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
if(incr) if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+") asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
@ -86,7 +84,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
bne + bne +
dec ${asmArrayvarname}_msb,x dec ${asmArrayvarname}_msb,x
+ dec ${asmArrayvarname}_lsb,x""") + dec ${asmArrayvarname}_lsb,x""")
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
return return
} }
@ -113,7 +110,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
} }
else else
{ {
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
when(elementDt) { when(elementDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
@ -141,7 +137,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
} }
else -> throw AssemblyError("weird array elt dt") else -> throw AssemblyError("weird array elt dt")
} }
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
} }
} }

View File

@ -371,12 +371,6 @@ internal class ProgramAndVarsGen(
else -> throw AssemblyError("weird dt for extravar $dt") else -> throw AssemblyError("weird dt for extravar $dt")
} }
} }
if(asmGenInfo.usedRegsaveA) // will probably never occur
asmgen.out("prog8_regsaveA .byte ?")
if(asmGenInfo.usedRegsaveX)
asmgen.out("prog8_regsaveX .byte ?")
if(asmGenInfo.usedRegsaveY)
asmgen.out("prog8_regsaveY .byte ?")
if(asmGenInfo.usedFloatEvalResultVar1) if(asmGenInfo.usedFloatEvalResultVar1)
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}") asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2) if(asmGenInfo.usedFloatEvalResultVar2)

View File

@ -189,12 +189,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
is PtFunctionCall -> { is PtFunctionCall -> {
val symbol = asmgen.symbolTable.lookup(value.name) val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value) asmgen.translateFunctionCall(value)
val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null } val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null }
when (returnValue.second) { when (returnValue.second) {
DataType.STR -> { DataType.STR -> {
asmgen.restoreXafterCall(value)
when(assign.target.datatype) { when(assign.target.datatype) {
DataType.UWORD -> { DataType.UWORD -> {
// assign the address of the string result value // assign the address of the string result value
@ -208,7 +206,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
DataType.FLOAT -> { DataType.FLOAT -> {
// float result from function sits in FAC1 // float result from function sits in FAC1
asmgen.restoreXafterCall(value)
assignFAC1float(assign.target) assignFAC1float(assign.target)
} }
else -> { else -> {
@ -244,8 +241,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
throw AssemblyError("should be just one register byte result value") throw AssemblyError("should be just one register byte result value")
} }
} }
// we've processed the result value in the X register by now, so it's now finally safe to restore it
asmgen.restoreXafterCall(value)
} }
} }
} }
@ -1221,9 +1216,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.STR -> { DataType.STR -> {
// use subroutine // use subroutine
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!) asmgen.saveRegisterStack(CpuRegister.A, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterStack(CpuRegister.A, false)
val stringVal = (variable as PtVariable).value as PtString val stringVal = (variable as PtVariable).value as PtString
asmgen.out(" ldy #${stringVal.value.length}") asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
@ -1234,9 +1229,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingISub()!!) asmgen.saveRegisterStack(CpuRegister.A, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
return return
@ -1458,27 +1453,19 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when(valueDt) { when(valueDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
assignExpressionToRegister(value, RegisterOrPair.Y, false) assignExpressionToRegister(value, RegisterOrPair.Y, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.FREADUY") asmgen.out(" jsr floats.FREADUY")
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
DataType.BYTE -> { DataType.BYTE -> {
assignExpressionToRegister(value, RegisterOrPair.A, true) assignExpressionToRegister(value, RegisterOrPair.A, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.FREADSA") asmgen.out(" jsr floats.FREADSA")
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
DataType.UWORD -> { DataType.UWORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, false) assignExpressionToRegister(value, RegisterOrPair.AY, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.GIVUAYFAY") asmgen.out(" jsr floats.GIVUAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
DataType.WORD -> { DataType.WORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, true) assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.GIVAYFAY") asmgen.out(" jsr floats.GIVAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
else -> throw AssemblyError("invalid dt") else -> throw AssemblyError("invalid dt")
} }
@ -2154,12 +2141,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1") asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
} }
else { else {
asmgen.saveRegisterLocal(CpuRegister.X, wordtarget.scope!!)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X)
asmgen.out(" lda $sourceName") asmgen.out(" lda $sourceName")
asmgen.signExtendAYlsb(DataType.BYTE) asmgen.signExtendAYlsb(DataType.BYTE)
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x") asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {

View File

@ -105,17 +105,41 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
DataType.FLOAT -> { DataType.FLOAT -> {
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, value.number!!.number, target.scope!!) SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, value.asmVarname, target.scope!!) target.asmVarname,
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, regName(value), target.scope!!) operator,
value.number!!.number
)
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(
target.asmVarname,
operator,
value.asmVarname
)
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(
target.asmVarname,
operator,
regName(value)
)
SourceStorageKind.MEMORY -> TODO("memread into float") SourceStorageKind.MEMORY -> TODO("memread into float")
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value.array!!, target.scope!!) SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(
target.asmVarname,
operator,
value.array!!
)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplaceModification_float_value_to_variable(target.asmVarname, operator, value.expression, target.scope!!) inplaceModification_float_value_to_variable(
target.asmVarname,
operator,
value.expression
)
} else { } else {
inplaceModification_float_value_to_variable(target.asmVarname, operator, value.expression!!, target.scope!!) inplaceModification_float_value_to_variable(
target.asmVarname,
operator,
value.expression!!
)
} }
} }
else -> throw AssemblyError("weird source type ${value.kind}") else -> throw AssemblyError("weird source type ${value.kind}")
@ -236,17 +260,41 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
DataType.FLOAT -> { DataType.FLOAT -> {
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(targetVarName, operator, value.number!!.number, target.scope!!) SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(targetVarName, operator, value.asmVarname, target.scope!!) targetVarName,
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(targetVarName, operator, regName(value), target.scope!!) operator,
value.number!!.number
)
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(
targetVarName,
operator,
value.asmVarname
)
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(
targetVarName,
operator,
regName(value)
)
SourceStorageKind.MEMORY -> TODO("memread into float array") SourceStorageKind.MEMORY -> TODO("memread into float array")
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(targetVarName, operator, value.array!!, target.scope!!) SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(
targetVarName,
operator,
value.array!!
)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplaceModification_float_value_to_variable(targetVarName, operator, value.expression, target.scope!!) inplaceModification_float_value_to_variable(
targetVarName,
operator,
value.expression
)
} else { } else {
inplaceModification_float_value_to_variable(targetVarName, operator, value.expression!!, target.scope!!) inplaceModification_float_value_to_variable(
targetVarName,
operator,
value.expression!!
)
} }
} }
else -> throw AssemblyError("weird source type ${value.kind}") else -> throw AssemblyError("weird source type ${value.kind}")
@ -264,7 +312,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_B1") asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_B1")
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!) asmgen.saveRegisterStack(CpuRegister.Y, false)
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.number!!.number.toInt()) SourceStorageKind.LITERALNUMBER -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.asmVarname) SourceStorageKind.VARIABLE -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.asmVarname)
@ -280,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
else -> throw AssemblyError("weird source type ${value.kind}") else -> throw AssemblyError("weird source type ${value.kind}")
} }
asmgen.restoreRegisterLocal(CpuRegister.Y) asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" lda P8ZP_SCRATCH_B1 | sta ${target.array.variable.name},y") asmgen.out(" lda P8ZP_SCRATCH_B1 | sta ${target.array.variable.name},y")
} }
in WordDatatypes -> { in WordDatatypes -> {
@ -336,18 +384,42 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$tempvar ldy #>$tempvar
jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(tempvar, operator, value.number!!.number, target.scope!!) SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(tempvar, operator, value.asmVarname, target.scope!!) tempvar,
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(tempvar, operator, regName(value), target.scope!!) operator,
value.number!!.number
)
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(
tempvar,
operator,
value.asmVarname
)
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(
tempvar,
operator,
regName(value)
)
SourceStorageKind.MEMORY -> TODO("memread into float") SourceStorageKind.MEMORY -> TODO("memread into float")
SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(tempvar, operator, value.array!!, target.scope!!) SourceStorageKind.ARRAY -> inplaceModification_float_value_to_variable(
tempvar,
operator,
value.array!!
)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return return
inplaceModification_float_value_to_variable(tempvar, operator, value.expression, target.scope!!) inplaceModification_float_value_to_variable(
tempvar,
operator,
value.expression
)
} else { } else {
inplaceModification_float_value_to_variable(tempvar, operator, value.expression!!, target.scope!!) inplaceModification_float_value_to_variable(
tempvar,
operator,
value.expression!!
)
} }
} }
else -> throw AssemblyError("weird source type ${value.kind}") else -> throw AssemblyError("weird source type ${value.kind}")
@ -2283,9 +2355,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
} }
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression, scope: IPtSubroutine) { private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1) asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
"+" -> { "+" -> {
asmgen.out(""" asmgen.out("""
@ -2327,11 +2398,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$name ldy #>$name
jsr floats.MOVMF jsr floats.MOVMF
""") """)
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, otherName: String, scope: IPtSubroutine) { private fun inplaceModification_float_variable_to_variable(name: String, operator: String, otherName: String) {
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
"+" -> { "+" -> {
asmgen.out(""" asmgen.out("""
@ -2443,12 +2512,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$name ldy #>$name
jsr floats.MOVMF jsr floats.MOVMF
""") """)
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: IPtSubroutine) { private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double) {
val constValueName = allocator.getFloatAsmConst(value) val constValueName = allocator.getFloatAsmConst(value)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
"+" -> { "+" -> {
if (value == 0.0) if (value == 0.0)
@ -2566,6 +2633,5 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$name ldy #>$name
jsr floats.MOVMF jsr floats.MOVMF
""") """)
asmgen.restoreRegisterLocal(CpuRegister.X)
} }
} }

View File

@ -24,10 +24,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"popw" -> funcPopw(call) "popw" -> funcPopw(call)
"push" -> funcPush(call) "push" -> funcPush(call)
"pushw" -> funcPushw(call) "pushw" -> funcPushw(call)
"rsave", "rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"rsavex",
"rrestore",
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> funcCallfar(call) "callfar" -> funcCallfar(call)
"msb" -> funcMsb(call) "msb" -> funcMsb(call)
"lsb" -> funcLsb(call) "lsb" -> funcLsb(call)

View File

@ -944,15 +944,12 @@ syscall (callnr), syscall1 (callnr, arg), syscall2 (callnr, arg1, arg2), syscall
specific memory locations. So these builtin function calls are not useful yet except for specific memory locations. So these builtin function calls are not useful yet except for
experimentation in new code generation targets. experimentation in new code generation targets.
rsave, rsavex rsave
Saves all registers including status (or only X) on the stack Saves all registers including status (or only X) on the stack
It's not needed to rsave()/rsavex() before an asm subroutine that clobbers the X register
(which is used by prog8 as the internal evaluation stack pointer);
the compiler will take care of this situation automatically.
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* saved, Note: the 16 bit 'virtual' registers of the Commander X16 are *not* saved,
but you can use ``cx16.save_virtual_registers()`` for that. but you can use ``cx16.save_virtual_registers()`` for that.
rrestore, rrestorex rrestore
Restore all registers including status (or only X) back from the cpu hardware stack Restore all registers including status (or only X) back from the cpu hardware stack
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* restored, Note: the 16 bit 'virtual' registers of the Commander X16 are *not* restored,
but you can use ``cx16.restore_virtual_registers()`` for that. but you can use ``cx16.restore_virtual_registers()`` for that.

View File

@ -118,40 +118,6 @@ doing something with that returnvalue. This can be on purpose if you're simply n
Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case. Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case.
The 6502 CPU's X-register: off-limits
-------------------------------------
Prog8 uses the cpu's X-register as a pointer in its internal expression evaluation stack.
When only writing code in Prog8, this is taken care of behind the scenes for you by the compiler.
However when you are including or linking with assembly routines or Kernal/ROM calls that *do*
use the X register (either clobbering it internally, or using it as a parameter, or return value register),
those calls will destroy Prog8's stack pointer and this will result in invalid calculations.
You should avoid using the X register in your assembly code, or take preparations.
If you make sure that the value of the X register is preserved before calling a routine
that uses it, and restored when the routine is done, you'll be ok.
Routines that return a value in the X register can be called from Prog8 but the return value is
inaccessible unless you write a short piece of inline assembly code to deal with it yourself, such as::
ubyte returnvalue
%asm {{
stx P8ZP_SCRATCH_REG ; use 'phx/plx' if using 65c02 cpu
ldx #10
jsr routine_using_x
stx returnvalue
ldx P8ZP_SCRATCH_REG
}}
; now use 'returnvalue' variable
Prog8 also provides some help to deal with this:
- you should use a ``clobbers(X)`` specification for asmsub routines that modify the X register; the compiler will preserve it for you automatically when such a routine is called
- the ``rsavex()`` and ``rrestorex()`` builtin functions can preserve and restore the X register
- the ``rsave()`` and ``rrestore()`` builtin functions can preserve and restore *all* registers (but this is very slow and overkill if you only need to save X)
Compiler Internals Compiler Internals
------------------ ------------------

View File

@ -1,11 +1,11 @@
TODO TODO
==== ====
- (branch): clean up all X register special handling - (branch): clean up all X register special handling in p8 code inline asm
- (branch): clean up docs about eval stack and X register
- (branch): fix optimizeCmpSequence in AsmOptimizer - (branch): fix optimizeCmpSequence in AsmOptimizer
- (branch): fix inplaceModification TODO in AugmentableAssignmentAsmGen - (branch): fix inplaceModification TODO in AugmentableAssignmentAsmGen
- (branch): fix up cx16/keyboardhandler.p8 X register shenanigans - (branch): fix up cx16/keyboardhandler.p8 X register shenanigans
- (branch): clean up docs about eval stack and X register
- IR: instructions that do type conversion (SZ etc, CONCAT, SGN) should put the result in a DIFFERENT register. - IR: instructions that do type conversion (SZ etc, CONCAT, SGN) should put the result in a DIFFERENT register.
- IR: reduce the number of branch instructions (gradually), replace with CMP(I) + status branch instruction - IR: reduce the number of branch instructions (gradually), replace with CMP(I) + status branch instruction