diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen6502.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen6502.kt index f36200561..edf3a08ed 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen6502.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen6502.kt @@ -279,7 +279,7 @@ class AsmGen6502(internal val program: Program, statements.asSequence().filterIsInstance().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") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index b365bbb8c..237b31c7c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -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") diff --git a/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt b/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt index aa53d7b80..bd624300b 100644 --- a/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt +++ b/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt @@ -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 - } - } } \ No newline at end of file diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index dc591f57f..fdacbf572 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -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 diff --git a/compiler/test/TestSubroutines.kt b/compiler/test/TestSubroutines.kt index 86071384d..1b100021f 100644 --- a/compiler/test/TestSubroutines.kt +++ b/compiler/test/TestSubroutines.kt @@ -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().single { it.name=="asmfunc"} val func = mainBlock.statements.filterIsInstance().single { it.name=="func"} + val emptysub = mainBlock.statements.filterIsInstance().single { it.name=="emptysub"} asmfunc.isAsmSubroutine shouldBe true - asmfunc.statements.single() shouldBe instanceOf() + asmfunc.statements.single() shouldBe instanceOf() + (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() func.statements.size shouldBe 5 func.statements[4] shouldBe instanceOf() @@ -129,6 +135,9 @@ class TestSubroutines: FunSpec({ call.args.single() shouldBe instanceOf() } (call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing" + + emptysub.statements.size shouldBe 1 + emptysub.statements.single() shouldBe instanceOf() } test("ubyte[] array parameters") { diff --git a/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt b/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt index f1086486e..5c866492f 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt @@ -25,10 +25,12 @@ abstract class Zeropage(protected val options: CompilationOptions) { val free = mutableListOf() // 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 diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 020a7a0fb..025bd382d 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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. diff --git a/examples/test.p8 b/examples/test.p8 index 96545c4b6..a3c4d7edd 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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") + } }