add unit test for @dirty variables

This commit is contained in:
Irmen de Jong 2024-11-08 23:26:22 +01:00
parent 64164c1c72
commit 3ba1d00a7c
4 changed files with 426 additions and 363 deletions

View File

@ -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 #<value
sta ${'$'}ea
sta 123
label2:
sta othervalue ; but not these in the comments
; also not these
;; ...or these
// valid words 123456
""", false, Position.DUMMY).names
names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words")
}
test("array literals") {
val text="""
test("array literals") {
val text="""
%zeropage basicsafe
main {
@ -57,38 +39,138 @@ main {
uword[] @shared addresses5 = [1111, 2222]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("array init size mismatch error") {
val text="""
test("array init size mismatch error") {
val text="""
main {
sub start() {
ubyte[10] uba = [1,2,3]
bool[10] bba = [true, false, true]
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "size mismatch"
errors.errors[1] shouldContain "size mismatch"
}
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "size mismatch"
errors.errors[1] shouldContain "size mismatch"
}
test("invalid && operator") {
val text="""
test("returning array as uword") {
val src = """
main {
sub start() {
uword b1
uword b2
uword b3 = b1 && b2 ; invalid syntax: '&&' is not an operator, 'and' should be used instead
}
}"""
compileText(C64Target(), false, text, writeAssembly = false) shouldBe null
cx16.r0 = getarray()
}
test("string comparisons") {
val src="""
sub getarray() -> 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<VarDecl>()
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<Assignment>()
forloop.body.statements[1] shouldBe instanceOf<Assignment>()
forloop.body.statements[2] shouldBe instanceOf<Assignment>()
}
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<VarDecl>() // x
st[2] shouldBe instanceOf<VarDecl>() // y
st[4] shouldBe instanceOf<VarDecl>() // z
st[6] shouldBe instanceOf<VarDecl>() // k
st[8] shouldBe instanceOf<VarDecl>() // l
st[10] shouldBe instanceOf<VarDecl>() // m
st[12] shouldBe instanceOf<VarDecl>() // r
st[14] shouldBe instanceOf<VarDecl>() // s
st[16] shouldBe instanceOf<VarDecl>() // 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<Assignment>()
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<Assignment>()
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 #<value
sta ${'$'}ea
sta 123
label2:
sta othervalue ; but not these in the comments
; also not these
;; ...or these
// valid words 123456
""", false, Position.DUMMY).names
names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words")
}
test("invalid && operator") {
val text="""
main {
sub start() {
uword b1
uword b2
uword b3 = b1 && b2 ; invalid syntax: '&&' is not an operator, 'and' should be used instead
}
}"""
compileText(C64Target(), false, text, writeAssembly = false) shouldBe null
}
test("pointervariable indexing allowed with >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<VarDecl>()
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<Assignment>()
forloop.body.statements[1] shouldBe instanceOf<Assignment>()
forloop.body.statements[2] shouldBe instanceOf<Assignment>()
}
test("'not in' operator parsing") {
val src="""
main {
@ -552,69 +813,6 @@ main {
tc.expression shouldBe instanceOf<ArrayIndexedExpression>()
}
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<VarDecl>() // x
st[2] shouldBe instanceOf<VarDecl>() // y
st[4] shouldBe instanceOf<VarDecl>() // z
st[6] shouldBe instanceOf<VarDecl>() // k
st[8] shouldBe instanceOf<VarDecl>() // l
st[10] shouldBe instanceOf<VarDecl>() // m
st[12] shouldBe instanceOf<VarDecl>() // r
st[14] shouldBe instanceOf<VarDecl>() // s
st[16] shouldBe instanceOf<VarDecl>() // 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")
}
})

View File

@ -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
-------------

View File

@ -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:**

View File

@ -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 <prefix>'?
- 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