diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 55ced418f..461d9ed78 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -709,7 +709,7 @@ internal class AstChecker(private val program: Program, err("this directive may only occur in a block or at module level") if(directive.args.isEmpty()) err("missing option directive argument(s)") - else if(directive.args.map{it.name in setOf("enable_floats", "force_output")}.any { !it }) + else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit")}.any { !it }) err("invalid option directive argument(s)") } "%target" -> { diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index b95a194a3..b1c6f00dd 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -27,7 +27,8 @@ data class CompilationOptions(val output: OutputType, val launcher: LauncherType, val zeropage: ZeropageType, val zpReserved: List, - val floats: Boolean) + val floats: Boolean, + val noSysInit: Boolean) class CompilerException(message: String?) : Exception(message) diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index bc9948615..756d0727c 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -125,6 +125,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions { 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() val floatsEnabled = allOptions.any { it.name == "enable_floats" } + val noSysInit = allOptions.any { it.name == "no_sysinit" } var zpType: ZeropageType = if (zpoption == null) if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE @@ -160,7 +161,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions { return CompilationOptions( if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType), if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType), - zpType, zpReserved, floatsEnabled + zpType, zpReserved, floatsEnabled, noSysInit ) } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 8673e3c0c..c7e216b8f 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -126,13 +126,13 @@ internal class AsmGen(private val program: Program, out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'") out("+\t.word 0") out("_prog8_entrypoint\t; assembly code starts here\n") - if(!CompilationTarget.instance.initProcName.isNullOrEmpty()) + if(!options.noSysInit && !CompilationTarget.instance.initProcName.isNullOrEmpty()) out(" jsr ${CompilationTarget.instance.initProcName}") } options.output == OutputType.PRG -> { out("; ---- program without basic sys call ----") out("* = ${program.actualLoadAddress.toHex()}\n") - if(!CompilationTarget.instance.initProcName.isNullOrEmpty()) + if(!options.noSysInit && !CompilationTarget.instance.initProcName.isNullOrEmpty()) out(" jsr ${CompilationTarget.instance.initProcName}") } options.output == OutputType.RAW -> { diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 14c37d123..33234e69f 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -129,7 +129,7 @@ class TestC64Zeropage { @Test fun testNames() { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false)) zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors) @@ -142,37 +142,37 @@ class TestC64Zeropage { @Test fun testZpFloatEnable() { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) assertFailsWith { zp.allocate("", DataType.FLOAT, null, errors) } - val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false)) assertFailsWith { zp2.allocate("", DataType.FLOAT, null, errors) } - val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true)) + val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false)) zp3.allocate("", DataType.FLOAT, null, errors) } @Test fun testZpModesWithFloats() { - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false)) - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false)) - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false)) - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false)) assertFailsWith { - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false)) } assertFailsWith { - C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true)) + C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false)) } } @Test fun testZpDontuse() { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false)) println(zp.free) assertEquals(0, zp.available()) assertFailsWith { @@ -182,19 +182,19 @@ class TestC64Zeropage { @Test fun testFreeSpaces() { - val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) + val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) assertEquals(16, zp1.available()) - val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false)) assertEquals(89, zp2.available()) - val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false)) + val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false)) assertEquals(125, zp3.available()) - val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) + val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) assertEquals(238, zp4.available()) } @Test fun testReservedSpace() { - val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) + val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) assertEquals(238, zp1.available()) assertTrue(50 in zp1.free) assertTrue(100 in zp1.free) @@ -203,7 +203,7 @@ class TestC64Zeropage { assertTrue(200 in zp1.free) assertTrue(255 in zp1.free) assertTrue(199 in zp1.free) - val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false)) + val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false)) assertEquals(139, zp2.available()) assertFalse(50 in zp2.free) assertFalse(100 in zp2.free) @@ -216,7 +216,7 @@ class TestC64Zeropage { @Test fun testBasicsafeAllocation() { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) assertEquals(16, zp.available()) assertFailsWith { @@ -239,7 +239,7 @@ class TestC64Zeropage { @Test fun testFullAllocation() { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) assertEquals(238, zp.available()) val loc = zp.allocate("", DataType.UWORD, null, errors) assertTrue(loc > 3) @@ -269,7 +269,7 @@ class TestC64Zeropage { @Test fun testEfficientAllocation() { - val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) + val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) assertEquals(16, zp.available()) assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors)) assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors)) diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 249987156..e43e87147 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -117,33 +117,38 @@ Directives Level: module, block. Sets special compiler options. - For a module option, only the ``enable_floats`` option is recognised, which will tell the compiler - to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal). - Otherwise, floating point support is not enabled. - When used in a block with the ``force_output`` option, it will force the block to be outputted - in the final program. Can be useful to make sure some - data is generated that would otherwise be discarded because it's not referenced (such as sprite data). + + - For a module option, there is ``enable_floats``, which will tell the compiler + to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal). + Otherwise, floating point support is not enabled. + - There's also ``no_sysinit`` which cause the resulting program to *not* include + the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to + take care of that yourself. The program will just start running from whatever state the machine is in when the + program was launched. + - When used in a block with the ``force_output`` option, it will force the block to be outputted + in the final program. Can be useful to make sure some + data is generated that would otherwise be discarded because it's not referenced (such as sprite data). .. data:: %asmbinary "" [, [, ]] - Level: block. - This directive can only be used inside a block. - The assembler will include the file as binary bytes at this point, prog8 will not process this at all. - The optional offset and length can be used to select a particular piece of the file. - The file is located relative to the current working directory! + Level: block. + This directive can only be used inside a block. + The assembler will include the file as binary bytes at this point, prog8 will not process this at all. + The optional offset and length can be used to select a particular piece of the file. + The file is located relative to the current working directory! .. data:: %asminclude "", "scopelabel" - Level: block. - This directive can only be used inside a block. - The assembler will include the file as raw assembly source text at this point, - prog8 will not process this at all, with one exception: the labels. - The scopelabel argument will be used as a prefix to access the labels from the included source code, - otherwise you would risk symbol redefinitions or duplications. - If you know what you are doing you can leave it as an empty string to not have a scope prefix. - The compiler first looks for the file relative to the same directory as the module containing this statement is in, - if the file can't be found there it is searched relative to the current directory. + Level: block. + This directive can only be used inside a block. + The assembler will include the file as raw assembly source text at this point, + prog8 will not process this at all, with one exception: the labels. + The scopelabel argument will be used as a prefix to access the labels from the included source code, + otherwise you would risk symbol redefinitions or duplications. + If you know what you are doing you can leave it as an empty string to not have a scope prefix. + The compiler first looks for the file relative to the same directory as the module containing this statement is in, + if the file can't be found there it is searched relative to the current directory. .. data:: %breakpoint diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e44eb4926..ca7b72a94 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO ==== - get rid of all other TODO's in the code ;-) -- add an %option that omits the 'system-init' code at the start. Useful to create separate standalone routines that shouldn't re-init the whole machine every time they're called - line-circle-gfx examples are now a few hundred bytes larger than before (~4.0/4.1 version i think?). Why is that, can it be fixed? - until condition should be able to refer to variables defined IN the do-until block itself. - add support? example? for processing arguments to a sys call : sys 999, 1, 2, "aaa" diff --git a/examples/test.p8 b/examples/test.p8 index edca60988..6e4d53e02 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,23 +7,10 @@ main $0900{ sub start() { - ubyte v = 1 - @($c000+v) = 10 - txt.print_ub(@($c001)) - txt.chrout('\n') - - @($c000+v) ++ - txt.print_ub(@($c001)) - txt.chrout('\n') - - @($c000+v) += 10 - txt.print_ub(@($c001)) - txt.chrout('\n') - - @($c000+v) *= 10 - txt.print_ub(@($c001)) - txt.chrout('\n') + do { + ubyte v = 1 + } until v==0 ; @($c000) *= 99 ; TODO implement