mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
make sure X register is also saved if needed when GoSub is used
This commit is contained in:
parent
66d5490702
commit
f5ebf79e71
@ -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))
|
||||
}
|
||||
|
@ -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!
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user