mirror of
https://github.com/irmen/prog8.git
synced 2025-01-26 19:30:59 +00:00
clean up X register save/store in compiler code, remove temp vars for register saving
This commit is contained in:
parent
b791fae9ce
commit
e2bb0de24d
@ -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),
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 -> {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
------------------
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user