fix rts in empty asmsub

This commit is contained in:
Irmen de Jong 2022-02-06 04:29:36 +01:00
parent 77de99b383
commit 8ae3bad6f7
8 changed files with 66 additions and 40 deletions

View File

@ -279,7 +279,7 @@ class AsmGen6502(internal val program: Program,
statements.asSequence().filterIsInstance<VarDecl>().forEach {
if(it.type==VarDeclType.VAR && it.datatype in NumericDatatypes)
it.value=null // TODO why is this done?
it.value=null // make sure every var has no init value anymore (could be set due to 'noreinit' option) because initialization is done via explicit assignment
}
out("\n; subroutines in this block")

View File

@ -96,8 +96,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
// (this condition has been enforced by an ast check earlier)
asmgen.out(" \t; inlined routine follows: ${sub.name}")
val assembly = sub.statements.single() as InlineAssembly
asmgen.translate(assembly)
sub.statements.forEach { asmgen.translate(it as InlineAssembly) }
asmgen.out(" \t; inlined routine end: ${sub.name}")
} else {
asmgen.out(" jsr $subAsmName")

View File

@ -20,33 +20,34 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x22u..0xffu)
synchronized(this) {
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x22u..0xffu)
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x22u..0x7fu)
free.addAll(0xa9u..0xffu)
}
ZeropageType.BASICSAFE -> {
free.addAll(0x22u..0x7fu)
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x22u..0x7fu)
free.addAll(0xa9u..0xffu)
removeReservedFromFreePool()
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = (2+reg*2).toUInt() to (DataType.UWORD to 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = (2+reg*2).toUInt() to (DataType.WORD to 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = (2+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = (3+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = (2+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = (3+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sH .. cx16.r15sH
}
ZeropageType.BASICSAFE -> {
free.addAll(0x22u..0x7fu)
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
removeReservedFromFreePool()
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = (2+reg*2).toUInt() to (DataType.UWORD to 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = (2+reg*2).toUInt() to (DataType.WORD to 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = (2+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = (3+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = (2+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = (3+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sH .. cx16.r15sH
}
}
}

View File

@ -146,12 +146,13 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
// and if an assembly block doesn't contain a rts/rti, and some other situations.
val returnStmt = Return(null, subroutine.position)
if (!subroutine.isAsmSubroutine && !subroutine.inline) {
if(subroutine.statements.isEmpty() ||
(subroutine.amountOfRtsInAsm() == 0
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
&& subroutine.statements.last() !is Subroutine)) {
&& subroutine.statements.last() !is Subroutine
&& subroutine.statements.last() !is Return)) {
val returnStmt = Return(null, subroutine.position)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
}
@ -167,14 +168,17 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
&& prevStmt !is Subroutine
&& prevStmt !is Return
) {
val returnStmt = Return(null, subroutine.position)
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
}
}
if (subroutine.inline && subroutine.isAsmSubroutine && subroutine.amountOfRtsInAsm() == 0) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above)
mods += IAstModification.InsertLast(InlineAssembly(" rts\n", Position.DUMMY), subroutine)
if (!subroutine.inline) {
if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && subroutine.amountOfRtsInAsm() == 0) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above)
mods += IAstModification.InsertLast(InlineAssembly(" rts\n", Position.DUMMY), subroutine)
}
}
return mods

View File

@ -85,6 +85,7 @@ class TestSubroutines: FunSpec({
func("text")
func(text)
func($2000)
emptysub()
}
asmsub asmfunc(str thing @AY) {
@ -94,6 +95,9 @@ class TestSubroutines: FunSpec({
uword t2 = thing as uword
asmfunc(thing)
}
sub emptysub() {
}
}
"""
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
@ -101,14 +105,16 @@ class TestSubroutines: FunSpec({
val mainBlock = module.statements.single() as Block
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
val emptysub = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="emptysub"}
asmfunc.isAsmSubroutine shouldBe true
asmfunc.statements.single() shouldBe instanceOf<Return>()
asmfunc.statements.single() shouldBe instanceOf<InlineAssembly>()
(asmfunc.statements.single() as InlineAssembly).assembly.trim() shouldBe "rts"
asmfunc.amountOfRtsInAsm() shouldBe 1
func.isAsmSubroutine shouldBe false
withClue("str param should have been changed to uword") {
asmfunc.parameters.single().type shouldBe DataType.UWORD
func.parameters.single().type shouldBe DataType.UWORD
}
asmfunc.statements.last() shouldBe instanceOf<Return>()
func.statements.size shouldBe 5
func.statements[4] shouldBe instanceOf<Return>()
@ -129,6 +135,9 @@ class TestSubroutines: FunSpec({
call.args.single() shouldBe instanceOf<IdentifierReference>()
}
(call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing"
emptysub.statements.size shouldBe 1
emptysub.statements.single() shouldBe instanceOf<Return>()
}
test("ubyte[] array parameters") {

View File

@ -25,10 +25,12 @@ abstract class Zeropage(protected val options: CompilationOptions) {
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
fun removeReservedFromFreePool() {
for (reserved in options.zpReserved)
reserve(reserved)
synchronized(this) {
for (reserved in options.zpReserved)
reserve(reserved)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
}
}
fun availableBytes() = if(options.zeropage== ZeropageType.DONTUSE) 0 else free.size

View File

@ -23,8 +23,8 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Ast modifications done in AsmGen, that should be done BEFORE calling asmgen (so that it doesn't have to modify the Ast any longer):
- block2asm: after vardecls2asm it clears the vardecl.value of all variables (why?)
- block2asm: removes init-assignments to no longer output the initialization assignments as regular statements (is done separately in block initialization routine)
- block2asm: after vardecls2asm it clears the vardecl.value of all variables
- Maybe don't rely on vardecls at all any longer but figure out the variable allocations (including ZP allocations) beforehand
and pass that via a new datastructure to asmgen? So that asmgen is no longer tasked with doing the allocations.
This could perhaps make it easer for the codegen as well to deal with sections, if any, in the future.

View File

@ -17,7 +17,18 @@ main {
txt.nl()
txt.print_ub(startval1)
txt.nl()
derp()
derp()
foobar()
startval1++
mainglobal1++
}
asmsub derp() {
}
sub foobar() {
txt.print("foobar\n")
}
}