From f5ebf79e7120450b86b6b760a1c229ae3f4cbe8a Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 26 Nov 2021 22:11:52 +0100 Subject: [PATCH] make sure X register is also saved if needed when GoSub is used --- .../compiler/target/cpu6502/codegen/AsmGen.kt | 11 +++- .../cpu6502/codegen/FunctionCallAsmGen.kt | 22 +++++++ .../astprocessing/StatementReorderer.kt | 57 ++++++++++++------- examples/test.p8 | 43 +++----------- 4 files changed, 75 insertions(+), 58 deletions(-) diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index bd3858608..e05534413 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -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)) } diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt index 616c7f270..510738db5 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt @@ -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! diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index e33c61401..cb8efdb96 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -362,30 +362,43 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { 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 { + 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 { + 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 + } + } } diff --git a/examples/test.p8 b/examples/test.p8 index e9e68381f..921804e47 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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 }} }