diff --git a/compiler/test/ast/TestVariousCompilerAst.kt b/compiler/test/ast/TestVariousCompilerAst.kt index 7ea21ae48..8617ca723 100644 --- a/compiler/test/ast/TestVariousCompilerAst.kt +++ b/compiler/test/ast/TestVariousCompilerAst.kt @@ -19,28 +19,10 @@ import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText class TestVariousCompilerAst: FunSpec({ - test("symbol names in inline assembly blocks") { - val names1 = InlineAssembly(""" - - """, false, Position.DUMMY).names - names1 shouldBe emptySet() + context("arrays") { - val names2 = InlineAssembly(""" -label: lda # uword { + return [11,22,33] + } +}""" + compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null + } + + test("split arrays back to normal when address is taken") { + val src=""" +main { + sub start() { + cx16.r0L=0 + if cx16.r0L==0 { + uword[] addresses = [scores2, start] + uword[] @split scores1 = [10, 25, 50, 100] + uword[] @split scores2 = [100, 250, 500, 1000] + + cx16.r0 = &scores1 + cx16.r1 = &scores2 + cx16.r2 = &addresses + } + } +}""" + val errors = ErrorReporterForTests(keepMessagesAfterReporting = true) + compileText(C64Target(), optimize=false, src, writeAssembly=true, errors=errors) shouldNotBe null + errors.errors.size shouldBe 0 + errors.warnings.size shouldBe 2 + errors.warnings[0] shouldContain("address") + errors.warnings[1] shouldContain("address") + errors.warnings[0] shouldContain("split") + errors.warnings[1] shouldContain("split") + } + } + + context("alias") { + test("aliases ok") { + val src=""" +main { + alias print = txt.print + alias width = txt.DEFAULT_WIDTH + + sub start() { + alias print2 = txt.print + alias width2 = txt.DEFAULT_WIDTH + print("one") + print2("two") + txt.print_ub(width) + txt.print_ub(width2) + + ; chained aliases + alias chained = print2 + chained("chained") + } +} + +txt { + const ubyte DEFAULT_WIDTH = 80 + sub print_ub(ubyte value) { + ; nothing + } + sub print(str msg) { + ; nothing + } +} + +""" + compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null + } + + test("wrong alias gives correct error") { + val src=""" +main { + alias print = txt.print2222 + alias width = txt.DEFAULT_WIDTH + + sub start() { + alias print2 = txt.print + alias width2 = txt.DEFAULT_WIDTH_XXX + print("one") + print2("two") + txt.print_ub(width) + txt.print_ub(width2) + } +} + +txt { + const ubyte DEFAULT_WIDTH = 80 + sub print_ub(ubyte value) { + ; nothing + } + sub print(str msg) { + ; nothing + } +} + +""" + val errors = ErrorReporterForTests() + compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors) shouldBe null + errors.errors.size shouldBe 2 + errors.errors[0] shouldContain "undefined symbol: txt.print2222" + errors.errors[1] shouldContain "undefined symbol: txt.DEFAULT_WIDTH_XXX" + } + } + + context("strings") { + test("string comparisons") { + val src=""" main { sub start() { @@ -118,16 +200,16 @@ main { return 0 } }""" - val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!! - val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 17 - val result2 = compileText(VMTarget(), optimize=false, src, writeAssembly=true)!! - val stmts2 = result2.compilerAst.entrypoint.statements - stmts2.size shouldBe 17 - } + val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!! + val stmts = result.compilerAst.entrypoint.statements + stmts.size shouldBe 17 + val result2 = compileText(VMTarget(), optimize=false, src, writeAssembly=true)!! + val stmts2 = result2.compilerAst.entrypoint.statements + stmts2.size shouldBe 17 + } - test("string concatenation and repeats") { - val src=""" + test("string concatenation and repeats") { + val src=""" main { sub start() { str @shared name = "part1" + "part2" @@ -137,19 +219,276 @@ main { rept = "xyz" * (times+1) } }""" - val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!! - val stmts = result.compilerAst.entrypoint.statements - stmts.size shouldBe 6 - val name1 = stmts[0] as VarDecl - val rept1 = stmts[1] as VarDecl - (name1.value as StringLiteral).value shouldBe "part1part2" - (rept1.value as StringLiteral).value shouldBe "reprepreprep" - val name2strcopy = stmts[3] as IFunctionCall - val rept2strcopy = stmts[4] as IFunctionCall - val name2 = name2strcopy.args.first() as IdentifierReference - val rept2 = rept2strcopy.args.first() as IdentifierReference - (name2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xx1xx2" - (rept2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz" + val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!! + val stmts = result.compilerAst.entrypoint.statements + stmts.size shouldBe 6 + val name1 = stmts[0] as VarDecl + val rept1 = stmts[1] as VarDecl + (name1.value as StringLiteral).value shouldBe "part1part2" + (rept1.value as StringLiteral).value shouldBe "reprepreprep" + val name2strcopy = stmts[3] as IFunctionCall + val rept2strcopy = stmts[4] as IFunctionCall + val name2 = name2strcopy.args.first() as IdentifierReference + val rept2 = rept2strcopy.args.first() as IdentifierReference + (name2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xx1xx2" + (rept2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz" + } + + test("char as str param is error") { + val src = """ +main { + sub start() { + print('@') + } + + sub print(str message) { + } +}""" + val errors = ErrorReporterForTests() + compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null + errors.errors.single() shouldContain "cannot use byte value" + } + } + + context("return value") { + test("missing return value is a syntax error") { + val src=""" +main { + sub start() { + cx16.r0 = runit1() + cx16.r1 = runit2() + } + + sub runit1() -> uword { + repeat { + cx16.r0++ + goto runit1 + } + } + + sub runit2() -> uword { + cx16.r0++ + } +}""" + val errors = ErrorReporterForTests() + compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null + errors.errors.size shouldBe 2 + errors.errors[0] shouldContain "has result value" + errors.errors[1] shouldContain "has result value" + } + + test("missing return value is not a syntax error if there's an external goto") { + val src=""" +main { + sub start() { + cx16.r0 = runit1() + runit2() + } + + sub runit1() -> uword { + repeat { + cx16.r0++ + goto runit2 + } + } + + sub runit2() { + cx16.r0++ + } +}""" + compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null + } + } + + context("variable declarations") { + test("multi-var decls in scope with initializer") { + val src=""" +main { + sub start() { + ubyte w + + for w in 0 to 20 { + ubyte @zp x,y,z=13 + ubyte q,r,s + x++ + y++ + z++ + } + } +}""" + val result = compileText(VMTarget(), optimize = false, src, writeAssembly = false)!! + val st = result.compilerAst.entrypoint.statements + /* + sub start () { + ubyte s + s = 0 + ubyte r + r = 0 + ubyte q + q = 0 + ubyte @zp z + ubyte @zp y + ubyte @zp x + ubyte w + for w in 0 to 20 step 1 { + z = 13 + y = 13 + x = 13 + x++ + y++ + z++ + } + } + */ + val vars = st.filterIsInstance() + vars.size shouldBe 7 + vars.all { it.names.size<=1 } shouldBe true + vars.map { it.name }.toSet() shouldBe setOf("s","r","q","z","y","x","w") + val forloop = st.single { it is ForLoop } as ForLoop + forloop.body.statements[0] shouldBe instanceOf() + forloop.body.statements[1] shouldBe instanceOf() + forloop.body.statements[2] shouldBe instanceOf() + } + + test("multi vardecls smart desugaring") { + val src=""" +main { + sub start() { + ubyte @shared x,y,z + ubyte @shared k,l,m = 42 + uword @shared r,s,t = sys.progend() + } +}""" + val result = compileText(Cx16Target(), optimize=true, src, writeAssembly=false)!! + val st = result.compilerAst.entrypoint.statements + st.size shouldBe 18 + st[0] shouldBe instanceOf() // x + st[2] shouldBe instanceOf() // y + st[4] shouldBe instanceOf() // z + st[6] shouldBe instanceOf() // k + st[8] shouldBe instanceOf() // l + st[10] shouldBe instanceOf() // m + st[12] shouldBe instanceOf() // r + st[14] shouldBe instanceOf() // s + st[16] shouldBe instanceOf() // t + val valX = (st[1] as Assignment).value + (valX as NumericLiteral).number shouldBe 0.0 + val valY = (st[3] as Assignment).value + (valY as NumericLiteral).number shouldBe 0.0 + val valZ = (st[5] as Assignment).value + (valZ as NumericLiteral).number shouldBe 0.0 + val valK = (st[7] as Assignment).value + (valK as NumericLiteral).number shouldBe 42.0 + val valL = (st[9] as Assignment).value + (valL as NumericLiteral).number shouldBe 42.0 + val valM = (st[11] as Assignment).value + (valM as NumericLiteral).number shouldBe 42.0 + val valR = (st[13] as Assignment).value + (valR as FunctionCallExpression).target.nameInSource shouldBe listOf("sys", "progend") + val valS = (st[15] as Assignment).value + (valS as IdentifierReference).nameInSource shouldBe listOf("r") + val valT = (st[17] as Assignment).value + (valT as IdentifierReference).nameInSource shouldBe listOf("r") + } + + test("various multi var decl symbol lookups") { + val src=""" +main { + sub start() { + uword @shared a,b + b = a + cx16.r1L = lsb(a) + funcw(a) + funcb(lsb(a)) + } + + sub funcw(uword arg) { + arg++ + } + + sub funcb(ubyte arg) { + arg++ + } +}""" + compileText(Cx16Target(), false, src) shouldNotBe null + } + + test("@dirty variables") { + val src=""" +%import floats + +main { + uword @shared @dirty globw + uword @shared globwi = 4444 + float @shared @dirty globf + float @shared globfi = 4 + ubyte[5] @shared @dirty globarr1 + ubyte[] @shared globarr2 = [11,22,33,44,55] + + sub start() { + uword @shared @dirty locw + uword @shared locwi = 4444 + float @shared @dirty locf + float @shared locfi = 4.0 + ubyte[5] @shared @dirty locarr1 + ubyte[] @shared locarr2 = [11,22,33,44,55] + + sys.clear_carry() + } +}""" + + val errors = ErrorReporterForTests(keepMessagesAfterReporting = true) + val result = compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors)!! + errors.errors.size shouldBe 0 + errors.infos.size shouldBe 6 + errors.infos.all { "dirty variable" in it } shouldBe true + val start = result.compilerAst.entrypoint + val st = start.statements + st.size shouldBe 9 + val assignments = st.filterIsInstance() + assignments.size shouldBe 2 + assignments[0].target.identifier?.nameInSource shouldBe listOf("locwi") + assignments[1].target.identifier?.nameInSource shouldBe listOf("locfi") + val blockst = start.definingBlock.statements + blockst.size shouldBe(9) + val blockassignments = blockst.filterIsInstance() + blockassignments.size shouldBe 2 + blockassignments[0].target.identifier?.nameInSource shouldBe listOf("globwi") + blockassignments[1].target.identifier?.nameInSource shouldBe listOf("globfi") + } + } + +context("various") { + test("symbol names in inline assembly blocks") { + val names1 = InlineAssembly(""" + + """, false, Position.DUMMY).names + names1 shouldBe emptySet() + + val names2 = InlineAssembly(""" +label: lda #255") { @@ -324,20 +663,6 @@ other { compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null } - test("returning array as uword") { - val src = """ -main { - sub start() { - cx16.r0 = getarray() - } - - sub getarray() -> uword { - return [11,22,33] - } -}""" - compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null - } - test("when on booleans") { val src = """ main @@ -358,21 +683,6 @@ main errors.errors[0] shouldContain "use if" } - test("char as str param is error") { - val src = """ -main { - sub start() { - print('@') - } - - sub print(str message) { - } -}""" - val errors = ErrorReporterForTests() - compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null - errors.errors.single() shouldContain "cannot use byte value" - } - test("sizeof number const evaluation in vardecl") { val src=""" main { @@ -384,55 +694,6 @@ main { compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null } - test("multi-var decls in scope with initializer") { - val src=""" -main { - sub start() { - ubyte w - - for w in 0 to 20 { - ubyte @zp x,y,z=13 - ubyte q,r,s - x++ - y++ - z++ - } - } -}""" - val result = compileText(VMTarget(), optimize = false, src, writeAssembly = false)!! - val st = result.compilerAst.entrypoint.statements - /* - sub start () { - ubyte s - s = 0 - ubyte r - r = 0 - ubyte q - q = 0 - ubyte @zp z - ubyte @zp y - ubyte @zp x - ubyte w - for w in 0 to 20 step 1 { - z = 13 - y = 13 - x = 13 - x++ - y++ - z++ - } - } - */ - val vars = st.filterIsInstance() - vars.size shouldBe 7 - vars.all { it.names.size<=1 } shouldBe true - vars.map { it.name }.toSet() shouldBe setOf("s","r","q","z","y","x","w") - val forloop = st.single { it is ForLoop } as ForLoop - forloop.body.statements[0] shouldBe instanceOf() - forloop.body.statements[1] shouldBe instanceOf() - forloop.body.statements[2] shouldBe instanceOf() - } - test("'not in' operator parsing") { val src=""" main { @@ -552,69 +813,6 @@ main { tc.expression shouldBe instanceOf() } - test("multi vardecls smart desugaring") { - val src=""" -main { - sub start() { - ubyte @shared x,y,z - ubyte @shared k,l,m = 42 - uword @shared r,s,t = sys.progend() - } -}""" - val result = compileText(Cx16Target(), optimize=true, src, writeAssembly=false)!! - val st = result.compilerAst.entrypoint.statements - st.size shouldBe 18 - st[0] shouldBe instanceOf() // x - st[2] shouldBe instanceOf() // y - st[4] shouldBe instanceOf() // z - st[6] shouldBe instanceOf() // k - st[8] shouldBe instanceOf() // l - st[10] shouldBe instanceOf() // m - st[12] shouldBe instanceOf() // r - st[14] shouldBe instanceOf() // s - st[16] shouldBe instanceOf() // t - val valX = (st[1] as Assignment).value - (valX as NumericLiteral).number shouldBe 0.0 - val valY = (st[3] as Assignment).value - (valY as NumericLiteral).number shouldBe 0.0 - val valZ = (st[5] as Assignment).value - (valZ as NumericLiteral).number shouldBe 0.0 - val valK = (st[7] as Assignment).value - (valK as NumericLiteral).number shouldBe 42.0 - val valL = (st[9] as Assignment).value - (valL as NumericLiteral).number shouldBe 42.0 - val valM = (st[11] as Assignment).value - (valM as NumericLiteral).number shouldBe 42.0 - val valR = (st[13] as Assignment).value - (valR as FunctionCallExpression).target.nameInSource shouldBe listOf("sys", "progend") - val valS = (st[15] as Assignment).value - (valS as IdentifierReference).nameInSource shouldBe listOf("r") - val valT = (st[17] as Assignment).value - (valT as IdentifierReference).nameInSource shouldBe listOf("r") - } - - test("various multi var decl symbol lookups") { - val src=""" -main { - sub start() { - uword @shared a,b - b = a - cx16.r1L = lsb(a) - funcw(a) - funcb(lsb(a)) - } - - sub funcw(uword arg) { - arg++ - } - - sub funcb(ubyte arg) { - arg++ - } -}""" - compileText(Cx16Target(), false, src) shouldNotBe null - } - test("void assignment is invalid") { val src=""" main { @@ -640,54 +838,6 @@ main { errors.errors[2] shouldEndWith "cannot assign to 'void', perhaps a void function call was intended" } - test("missing return value is a syntax error") { - val src=""" -main { - sub start() { - cx16.r0 = runit1() - cx16.r1 = runit2() - } - - sub runit1() -> uword { - repeat { - cx16.r0++ - goto runit1 - } - } - - sub runit2() -> uword { - cx16.r0++ - } -}""" - val errors = ErrorReporterForTests() - compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null - errors.errors.size shouldBe 2 - errors.errors[0] shouldContain "has result value" - errors.errors[1] shouldContain "has result value" - } - - test("missing return value is not a syntax error if there's an external goto") { - val src=""" -main { - sub start() { - cx16.r0 = runit1() - runit2() - } - - sub runit1() -> uword { - repeat { - cx16.r0++ - goto runit2 - } - } - - sub runit2() { - cx16.r0++ - } -}""" - compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null - } - test("defer syntactic sugaring") { val src=""" main { @@ -727,99 +877,7 @@ main { val handler = sub.children[7] as PtSub handler.name shouldBe "p8s_prog8_invoke_defers" } - - test("aliases ok") { - val src=""" -main { - alias print = txt.print - alias width = txt.DEFAULT_WIDTH - - sub start() { - alias print2 = txt.print - alias width2 = txt.DEFAULT_WIDTH - print("one") - print2("two") - txt.print_ub(width) - txt.print_ub(width2) - - ; chained aliases - alias chained = print2 - chained("chained") - } } -txt { - const ubyte DEFAULT_WIDTH = 80 - sub print_ub(ubyte value) { - ; nothing - } - sub print(str msg) { - ; nothing - } -} - -""" - compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null - } - - test("wrong alias gives correct error") { - val src=""" -main { - alias print = txt.print2222 - alias width = txt.DEFAULT_WIDTH - - sub start() { - alias print2 = txt.print - alias width2 = txt.DEFAULT_WIDTH_XXX - print("one") - print2("two") - txt.print_ub(width) - txt.print_ub(width2) - } -} - -txt { - const ubyte DEFAULT_WIDTH = 80 - sub print_ub(ubyte value) { - ; nothing - } - sub print(str msg) { - ; nothing - } -} - -""" - val errors = ErrorReporterForTests() - compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors) shouldBe null - errors.errors.size shouldBe 2 - errors.errors[0] shouldContain "undefined symbol: txt.print2222" - errors.errors[1] shouldContain "undefined symbol: txt.DEFAULT_WIDTH_XXX" - } - - test("split arrays back to normal when address is taken") { - val src=""" -main { - sub start() { - cx16.r0L=0 - if cx16.r0L==0 { - uword[] addresses = [scores2, start] - uword[] @split scores1 = [10, 25, 50, 100] - uword[] @split scores2 = [100, 250, 500, 1000] - - cx16.r0 = &scores1 - cx16.r1 = &scores2 - cx16.r2 = &addresses - } - } -}""" - val errors = ErrorReporterForTests(keepMessagesAfterReporting = true) - compileText(C64Target(), optimize=false, src, writeAssembly=true, errors=errors) shouldNotBe null - errors.errors.size shouldBe 0 - errors.warnings.size shouldBe 2 - errors.warnings[0] shouldContain("address") - errors.warnings[1] shouldContain("address") - errors.warnings[0] shouldContain("split") - errors.warnings[1] shouldContain("split") - } }) diff --git a/docs/source/comparing.rst b/docs/source/comparing.rst index f4736f45a..adc343245 100644 --- a/docs/source/comparing.rst +++ b/docs/source/comparing.rst @@ -81,7 +81,9 @@ Foreign function interface (external/ROM calls) ----------------------------------------------- - You can use the ``extsub`` keyword to define the call signature of foreign functions (ROM routines or external routines elsewhere in RAM) in a natural way. Calling those generates code that is as efficient or even more efficient as calling regular subroutines. - No additional stubs are needed. You can even specify the memory bank the routine is in and the compiler takes care of bank switching when calling it. + No additional stubs are needed. Y +- High level support of memory banking: an ``extsub`` can be defined with the memory bank number (constant or variable) where the routine is located in, + and then when you call it as usual, the compiler takes care of the required bank switching. Optimizations ------------- diff --git a/docs/source/programming.rst b/docs/source/programming.rst index d7db1f7e2..4c7b90236 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -239,10 +239,16 @@ when assembling the rest of the code). Example:: **uninitialized variables:** All variables will be initialized by prog8 at startup, they'll get their assigned initialization value, or be cleared to zero. This (re)initialization is also done on each subroutine entry for the variables declared in the subroutine. -There may be certain scenarios where this initialization is redundant and/or where you want to avoid the overhead of it -You can do so by using the ``@dirty`` tag on the variable declaration. -This means that the variable will *not* be (re)initialized by Prog8 and that its value is undefined. -You have to assign it a value yourself first, before using the variable. If you don't do that, the value can be anything, so beware. + +There may be certain scenarios where this initialization is redundant and/or where you want to avoid the overhead of it. +In some cases, Prog8 itself can detect that a variable doesn't need a separate automatic initialization to zero, if +it's trivial that it is not being read between the variable's declaration and the first assignment. For instance, when +you declare a variable immediately before a for loop where it is the loop variable. However Prog8 is not yet very smart +at detecting these redundant initializations. If you want to be sure, check the generated assembly output. + +In any case, you can use the ``@dirty`` tag on the variable declaration to make the variable *not* being (re)initialized by Prog8. +This means its value will be undefined (it can be anything) until you assign a value yourself! Don't use such +a variable before you have done so. 🦶🔫 Footgun warning. **memory alignment:** diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 07f676035..6460d9274 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,28 +1,25 @@ TODO ==== -- add unit tests for @dirty variables - - for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed. regenerate symbol dump files -improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it. -Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc. -Add a -library $xxxx command line option to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program? -Need to add some way to generate a stable jump table at a given address. -Why are blocks without an addr moved BEHIND a block with an address? That's done in the StatementReorderer. - - -Improve register load order in subroutine call args assignments: -in certain situations, the "wrong" order of evaluation of function call arguments is done which results -in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!) -Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters(). Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ + +- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using '? +- Why are blocks without an addr moved BEHIND a block with an address? That's done in the StatementReorderer. +- Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it. + Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc. + Add a -library $xxxx command line option to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program? + Need to add some way to generate a stable jump table at a given address. +- Improve register load order in subroutine call args assignments: + in certain situations, the "wrong" order of evaluation of function call arguments is done which results + in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!) + Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters(). - remove 'extsub' as a recognised alternative for 'extsub' - Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions - Does it make codegen easier if everything is an expression? Start with the PtProgram ast , get rid of the statements there -> expressions that have Void data type