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),
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
"rsave" to FSignature(false, emptyList(), null),
"rsavex" 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),
"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
}
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) {
when (register) {
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) {
when (register) {
CpuRegister.A -> {
@ -586,12 +542,6 @@ class AsmGen6502Internal (
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) =
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?) =
assignmentAsmGen.translateNormalAssignment(assign, scope)
@ -3095,9 +3045,6 @@ $repeatLabel""")
* it's more consistent to only define these attributes on a Subroutine node.
*/
internal class SubroutineExtraAsmInfo {
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false

View File

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

View File

@ -6,15 +6,6 @@ import prog8.code.ast.PtSub
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 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 fun translateFunctionCallStatement(stmt: PtFunctionCall) {
saveXbeforeCall(stmt)
translateFunctionCall(stmt)
restoreXafterCall(stmt)
// 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) =
(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)

View File

@ -13,7 +13,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.array
val scope = stmt.definingISub()
when {
targetIdent!=null -> {
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}_lsb+$constIndex""")
} else {
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
if(incr)
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 +
dec ${asmArrayvarname}_msb,x
+ dec ${asmArrayvarname}_lsb,x""")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
return
}
@ -113,7 +110,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
}
else
{
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
when(elementDt) {
in ByteDatatypes -> {
@ -141,7 +137,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
}
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")
}
}
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)
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2)

View File

@ -189,12 +189,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
is PtFunctionCall -> {
val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value)
val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null }
when (returnValue.second) {
DataType.STR -> {
asmgen.restoreXafterCall(value)
when(assign.target.datatype) {
DataType.UWORD -> {
// assign the address of the string result value
@ -208,7 +206,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
DataType.FLOAT -> {
// float result from function sits in FAC1
asmgen.restoreXafterCall(value)
assignFAC1float(assign.target)
}
else -> {
@ -244,8 +241,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
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 -> {
// use subroutine
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)
asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.restoreRegisterStack(CpuRegister.A, false)
val stringVal = (variable as PtVariable).value as PtString
asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" jsr prog8_lib.containment_bytearray")
@ -1234,9 +1229,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
DataType.ARRAY_B, DataType.ARRAY_UB -> {
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)
asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray")
return
@ -1458,27 +1453,19 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when(valueDt) {
DataType.UBYTE -> {
assignExpressionToRegister(value, RegisterOrPair.Y, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.FREADUY")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
DataType.BYTE -> {
assignExpressionToRegister(value, RegisterOrPair.A, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.FREADSA")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
DataType.UWORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, false)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.GIVUAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
DataType.WORD -> {
assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.saveRegisterLocal(CpuRegister.X, origTypeCastExpression.definingISub()!!)
asmgen.out(" jsr floats.GIVAYFAY")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
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")
}
else {
asmgen.saveRegisterLocal(CpuRegister.X, wordtarget.scope!!)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X)
asmgen.out(" lda $sourceName")
asmgen.signExtendAYlsb(DataType.BYTE)
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
TargetStorageKind.REGISTER -> {

View File

@ -105,17 +105,41 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
DataType.FLOAT -> {
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, value.number!!.number, target.scope!!)
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, value.asmVarname, target.scope!!)
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, regName(value), target.scope!!)
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
target.asmVarname,
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.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 -> {
if(value.expression is PtTypeCast) {
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 {
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}")
@ -236,17 +260,41 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
DataType.FLOAT -> {
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(targetVarName, operator, value.number!!.number, target.scope!!)
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(targetVarName, operator, value.asmVarname, target.scope!!)
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(targetVarName, operator, regName(value), target.scope!!)
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
targetVarName,
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.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 -> {
if(value.expression is PtTypeCast) {
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 {
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}")
@ -264,7 +312,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
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) {
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)
@ -280,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
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")
}
in WordDatatypes -> {
@ -336,18 +384,42 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$tempvar
jsr floats.copy_float""") // copy from array into float temp var, clobbers A,Y
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(tempvar, operator, value.number!!.number, target.scope!!)
SourceStorageKind.VARIABLE -> inplaceModification_float_variable_to_variable(tempvar, operator, value.asmVarname, target.scope!!)
SourceStorageKind.REGISTER -> inplaceModification_float_variable_to_variable(tempvar, operator, regName(value), target.scope!!)
SourceStorageKind.LITERALNUMBER -> inplaceModification_float_litval_to_variable(
tempvar,
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.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 -> {
if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
inplaceModification_float_value_to_variable(tempvar, operator, value.expression, target.scope!!)
inplaceModification_float_value_to_variable(
tempvar,
operator,
value.expression
)
} 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}")
@ -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.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
"+" -> {
asmgen.out("""
@ -2327,11 +2398,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$name
jsr floats.MOVMF
""")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, otherName: String, scope: IPtSubroutine) {
asmgen.saveRegisterLocal(CpuRegister.X, scope)
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, otherName: String) {
when (operator) {
"+" -> {
asmgen.out("""
@ -2443,12 +2512,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$name
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)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
"+" -> {
if (value == 0.0)
@ -2566,6 +2633,5 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy #>$name
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)
"push" -> funcPush(call)
"pushw" -> funcPushw(call)
"rsave",
"rsavex",
"rrestore",
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> funcCallfar(call)
"msb" -> funcMsb(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
experimentation in new code generation targets.
rsave, rsavex
rsave
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,
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
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* restored,
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.
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
------------------

View File

@ -1,11 +1,11 @@
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 inplaceModification TODO in AugmentableAssignmentAsmGen
- (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: reduce the number of branch instructions (gradually), replace with CMP(I) + status branch instruction