diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt index 491b7a23e..4a91ad7dd 100644 --- a/compiler/src/prog8/ast/AstToplevel.kt +++ b/compiler/src/prog8/ast/AstToplevel.kt @@ -263,7 +263,10 @@ class Module(override val name: String, val importedBy = mutableListOf() val imports = mutableSetOf() - var loadAddress: Int = 0 // can be set with the %address directive + val loadAddress: Int by lazy { + val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0 + address + } override fun linkParents(parent: Node) { this.parent = parent diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt index b4430441d..39f484a3b 100644 --- a/compiler/src/prog8/ast/base/Extensions.kt +++ b/compiler/src/prog8/ast/base/Extensions.kt @@ -3,6 +3,7 @@ package prog8.ast.base import prog8.ast.Module import prog8.ast.Program import prog8.ast.processing.* +import prog8.ast.statements.Directive import prog8.compiler.CompilationOptions import prog8.compiler.BeforeAsmGenerationAstChanger @@ -68,3 +69,37 @@ internal fun Program.variousCleanups() { process.visit(this) process.applyModifications() } + +internal fun Program.moveMainAndStartToFirst() { + // the module containing the program entrypoint is moved to the first in the sequence. + // the "main" block containing the entrypoint is moved to the top in there, + // and finally the entrypoint subroutine "start" itself is moved to the top in that block. + + val directives = modules[0].statements.filterIsInstance() + val start = this.entrypoint() + if(start!=null) { + val mod = start.definingModule() + val block = start.definingBlock() + if(!modules.remove(mod)) + throw FatalAstException("module wrong") + modules.add(0, mod) + mod.remove(block) + var afterDirective = mod.statements.indexOfFirst { it !is Directive } + if(afterDirective<0) + mod.statements.add(block) + else + mod.statements.add(afterDirective, block) + block.remove(start) + afterDirective = block.statements.indexOfFirst { it !is Directive } + if(afterDirective<0) + block.statements.add(start) + else + block.statements.add(afterDirective, start) + + // overwrite the directives in the module containing the entrypoint + for(directive in directives) { + modules[0].statements.removeAll { it is Directive && it.directive == directive.directive } + modules[0].statements.add(0, directive) + } + } +} diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 08a4e31b4..fee816ea4 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -120,8 +120,6 @@ private fun determineCompilationOptions(program: Program): CompilationOptions { as? Directive)?.args?.single()?.name?.toUpperCase() val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive)?.args?.single()?.name?.toUpperCase() - mainModule.loadAddress = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%address" } - as? Directive)?.args?.single()?.int ?: 0 val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } as? Directive)?.args?.single()?.name?.toUpperCase() val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet() @@ -211,6 +209,7 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO programAst.checkRecursion(errors) // check if there are recursive subroutine calls errors.handle() programAst.verifyFunctionArgTypes() + programAst.moveMainAndStartToFirst() } private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path, diff --git a/examples/diskdir-sys50000.p8 b/examples/diskdir-sys50000.p8 index 7cadb2023..1746fc84e 100644 --- a/examples/diskdir-sys50000.p8 +++ b/examples/diskdir-sys50000.p8 @@ -12,56 +12,4 @@ ; The only difference with diskdir.p8 is the directives that make this load at 50000. -main { - sub start() { - txt.print("directory of disk drive #8:\n\n") - diskdir(8) - } - - sub diskdir(ubyte drivenumber) { - c64.SETNAM(1, "$") - c64.SETLFS(1, drivenumber, 0) - void c64.OPEN() ; open 1,8,0,"$" - if_cs - goto io_error - void c64.CHKIN(1) ; use #1 as input channel - if_cs - goto io_error - - repeat 4 { - void c64.CHRIN() ; skip the 4 prologue bytes - } - - ; while not key pressed / EOF encountered, read data. - ubyte status = c64.READST() - while not status { - txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN())) - txt.chrout(' ') - ubyte @zp char - do { - char = c64.CHRIN() - txt.chrout(char) - } until char==0 - txt.chrout('\n') - repeat 2 { - void c64.CHRIN() ; skip 2 bytes - } - status = c64.READST() - - c64.STOP() - if_nz - break - } - -io_error: - status = c64.READST() - c64.CLOSE(1) - c64.CLRCHN() ; restore default i/o devices - - if status and status != 64 { ; 64=end of file - txt.print("\ni/o error, status: ") - txt.print_ub(status) - txt.chrout('\n') - } - } -} +%import diskdir diff --git a/examples/diskdir.p8 b/examples/diskdir.p8 index 05a4630a3..ab1b9d34e 100644 --- a/examples/diskdir.p8 +++ b/examples/diskdir.p8 @@ -40,15 +40,15 @@ main { void c64.CHRIN() ; skip 2 bytes void c64.CHRIN() status = c64.READST() - c64.STOP() + void c64.STOP() if_nz break } io_error: status = c64.READST() - c64.CLOSE(1) c64.CLRCHN() ; restore default i/o devices + c64.CLOSE(1) if status and status != 64 { ; 64=end of file txt.print("\ni/o error, status: ") diff --git a/examples/test.p8 b/examples/test.p8 index 1830c7247..d0c4366ad 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -8,6 +8,18 @@ main { str planet_name = "12345678" sub start() { + uword[] warray = [1,2,3,4,5] + + uword sums + ubyte ii + uword ww + uword wptr = &warray + &uword wmap = $c000 + + wmap += ii + wmap <<= ii + wmap >>= ii + txt.print(planet_name) txt.chrout('\n')