make sure X register is also saved if needed when GoSub is used

This commit is contained in:
Irmen de Jong 2021-11-26 22:11:52 +01:00
parent 66d5490702
commit f5ebf79e71
4 changed files with 75 additions and 58 deletions

View File

@ -854,9 +854,15 @@ class AsmGen(private val program: Program,
internal fun saveXbeforeCall(functionCall: IFunctionCall) =
functioncallAsmGen.saveXbeforeCall(functionCall)
internal fun saveXbeforeCall(gosub: GoSub) =
functioncallAsmGen.saveXbeforeCall(gosub)
internal fun restoreXafterCall(functionCall: IFunctionCall) =
functioncallAsmGen.restoreXafterCall(functionCall)
internal fun restoreXafterCall(gosub: GoSub) =
functioncallAsmGen.restoreXafterCall(gosub)
internal fun translateNormalAssignment(assign: AsmAssignment) =
assignmentAsmGen.translateNormalAssignment(assign)
@ -1380,8 +1386,11 @@ $label nop""")
}
private fun translate(jump: Jump) {
if(jump.isGosub)
if(jump.isGosub) {
saveXbeforeCall(jump as GoSub)
out(" jsr ${getJumpTarget(jump)}")
restoreXafterCall(jump as GoSub)
}
else
jmp(getJumpTarget(jump))
}

View File

@ -34,6 +34,17 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
}
internal fun saveXbeforeCall(gosub: GoSub) {
val sub = gosub.identifier?.targetSubroutine(program)
if(sub?.shouldSaveX()==true) {
val regSaveOnStack = sub.asmAddress==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, gosub.definingSubroutine!!)
}
}
internal fun restoreXafterCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) {
@ -45,6 +56,17 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
}
internal fun restoreXafterCall(gosub: GoSub) {
val sub = gosub.identifier?.targetSubroutine(program)
if(sub?.shouldSaveX()==true) {
val regSaveOnStack = sub.asmAddress==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)
}
}
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
// Output only the code to set up the parameters and perform the actual call
// NOTE: does NOT output the code to deal with the result values!

View File

@ -362,30 +362,43 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val function = functionCallStatement.target.targetStatement(program)!!
checkUnusedReturnValues(functionCallStatement, function, program, errors)
if(function is Subroutine) {
if(function.isAsmSubroutine)
return noModifications // TODO new logic for passing arguments to asmsub
// regular subroutine call: replace the call with assigning the params directly + actual call with a GoSub
require(function.asmParameterRegisters.isEmpty())
val assignParams =
function.parameters.zip(functionCallStatement.args).map {
var argumentValue = it.second
val paramIdentifier = IdentifierReference(function.scopedName + it.first.name, argumentValue.position)
val argDt = argumentValue.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
if(argDt in ArrayDatatypes) {
// pass the address of the array instead
argumentValue = AddressOf(argumentValue as IdentifierReference, argumentValue.position)
}
Assignment(AssignTarget(paramIdentifier, null, null, argumentValue.position), argumentValue, argumentValue.position)
}
return assignParams.map { IAstModification.InsertBefore(functionCallStatement, it, parent as IStatementContainer) } +
IAstModification.ReplaceNode(
functionCallStatement as Node,
GoSub(null, functionCallStatement.target, null, (functionCallStatement as Node).position),
parent)
if(function.inline)
return noModifications
return if(function.isAsmSubroutine)
replaceCallAsmSubStatementWithGosub(function, functionCallStatement, parent)
else
replaceCallSubStatementWithGosub(function, functionCallStatement, parent)
}
return noModifications
}
private fun replaceCallSubStatementWithGosub(function: Subroutine, call: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val assignParams =
function.parameters.zip(call.args).map {
var argumentValue = it.second
val paramIdentifier = IdentifierReference(function.scopedName + it.first.name, argumentValue.position)
val argDt = argumentValue.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
if(argDt in ArrayDatatypes) {
// pass the address of the array instead
argumentValue = AddressOf(argumentValue as IdentifierReference, argumentValue.position)
}
Assignment(AssignTarget(paramIdentifier, null, null, argumentValue.position), argumentValue, argumentValue.position)
}
return assignParams.map { IAstModification.InsertBefore(call, it, parent as IStatementContainer) } +
IAstModification.ReplaceNode(
call,
GoSub(null, call.target, null, call.position),
parent)
}
private fun replaceCallAsmSubStatementWithGosub(function: Subroutine, call: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(function.parameters.isEmpty()) {
// 0 params -> just GoSub
return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent))
} else {
// TODO new logic for passing arguments to asmsub: >0 params -> not sure yet how to do this....
return noModifications
}
}
}

View File

@ -5,48 +5,21 @@ main {
sub start() {
uword xx
ubyte yy = 33
xx=xx-routine(2)
; concat_string(random_name())
ubyte @shared xx=20
ubyte @shared yy=10
; ubyte xx=20
; ubyte yy=10
;
; routine(33)
; txt.setcc(xx+1, yy+3, 81, 7)
; txt.setcc(xx+2, yy+2, 81, 7)
; txt.setcc(xx+3, yy+1, 81, 7)
;
; ; TODO test new param load with subroutine call in expression:
; ; yy=routine(33)
;
; main.routine.r1arg = 20
; ; main.routine2.r2arg = 20 ; TODO asmgen
;
; xx = main.routine.r1arg
; xx++
; ;xx = main.routine2.r2arg ; TODO asmgen
; ;xx++
routine2()
routine2()
routine2()
repeat {
xx++
}
}
sub random_name() -> str {
ubyte ii
str name = " " ; 8 chars max
return name
}
ubyte qqq
sub routine(ubyte r1arg) -> ubyte {
return qqq
return main.start.yy
}
asmsub routine2(ubyte r2arg @ A) {
asmsub routine2() -> ubyte @A {
%asm {{
adc #20
rts
}}
}