diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 4d0239512..9373303bd 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -186,7 +186,7 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable { else -> throw AssemblyError("weird array value element $elt") } } - val result = PtVariable(name, type, zeropage, align, newValue, arraySize, position) + val result = PtVariable(name, type, zeropage, align, dirty, newValue, arraySize, position) result.parent = parent result } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 48f688fa0..880338e2c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -220,7 +220,8 @@ internal class ProgramAndVarsGen( } private fun tempVars() { - asmgen.out("; expression temp vars\n .section BSS") + // these can be in the no clear section because they'll always get assigned a new value + asmgen.out("; expression temp vars\n .section BSS_NOCLEAR") for((dt, count) in asmgen.tempVarsCounters) { if(count>0) { for(num in 1..count) { @@ -238,7 +239,7 @@ internal class ProgramAndVarsGen( } } } - asmgen.out(" .send BSS") + asmgen.out(" .send BSS_NOCLEAR") } private fun footer() { @@ -494,7 +495,7 @@ internal class ProgramAndVarsGen( sub.children.forEach { asmgen.translate(it) } asmgen.out("; variables") - asmgen.out(" .section BSS") + asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use val asmGenInfo = asmgen.subroutineExtra(sub) for((dt, name, addr) in asmGenInfo.extraVars) { if(addr!=null) @@ -510,7 +511,7 @@ internal class ProgramAndVarsGen( asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}") if(asmGenInfo.usedFloatEvalResultVar2) asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}") - asmgen.out(" .send BSS") + asmgen.out(" .send BSS_NOCLEAR") // normal statically allocated variables val variables = varsInSubroutine @@ -638,15 +639,27 @@ internal class ProgramAndVarsGen( val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized } if(varsNoInit.isNotEmpty()) { asmgen.out("; non-zeropage variables") - asmgen.out(" .section BSS") - val (notAligned, aligned) = varsNoInit.partition { it.align==0u } - notAligned.sortedWith(compareBy { it.name }.thenBy { it.dt.base }).forEach { - uninitializedVariable2asm(it) + val (dirty, clean) = varsNoInit.partition { it.dirty } + + fun generate(section: String, variables: List) { + asmgen.out(" .section $section") + val (notAligned, aligned) = variables.partition { it.align == 0u } + notAligned.sortedWith(compareBy { it.name }.thenBy { it.dt.base }).forEach { + uninitializedVariable2asm(it) + } + aligned.sortedWith(compareBy { it.align }.thenBy { it.name }.thenBy { it.dt.base }) + .forEach { uninitializedVariable2asm(it) } + asmgen.out(" .send $section") } - aligned.sortedWith(compareBy { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach { - uninitializedVariable2asm(it) + + if(clean.isNotEmpty()) { + // clean vars end up in BSS so they're at least cleared to 0 at startup + generate("BSS", clean) + } + if(dirty.isNotEmpty()) { + // dirty vars end up in BSS_NOCLEAR so they're left at whatever value is in there on startup + generate("BSS_NOCLEAR", dirty) } - asmgen.out(" .send BSS") } if(varsWithInit.isNotEmpty()) { diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt index 9cda8f359..c581db0c4 100644 --- a/codeGenCpu6502/test/TestCodegen.kt +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -58,6 +58,7 @@ class TestCodegen: FunSpec({ DataType.UBYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -67,6 +68,7 @@ class TestCodegen: FunSpec({ DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, 0u, + false, null, 3u, Position.DUMMY @@ -76,6 +78,7 @@ class TestCodegen: FunSpec({ DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, 0u, + false, null, 3u, Position.DUMMY @@ -85,6 +88,7 @@ class TestCodegen: FunSpec({ DataType.WORD, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt index eff8a4b39..f0274cade 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt @@ -63,7 +63,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable { variable.initializationArrayValue?.map { convertArrayElt(it) }, variable.length, variable.zpwish, - variable.align) + variable.align, + variable.dirty) } else { fun fixupAddressOfInArray(array: List?): List? { if(array==null) @@ -87,7 +88,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable { fixupAddressOfInArray(variable.initializationArrayValue), variable.length, variable.zpwish, - variable.align + variable.align, + variable.dirty ) } } diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index 6b219ab44..f966c824b 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -52,6 +52,7 @@ class TestVmCodeGen: FunSpec({ DataType.UBYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -61,6 +62,7 @@ class TestVmCodeGen: FunSpec({ DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, 0u, + false, null, 3u, Position.DUMMY @@ -70,6 +72,7 @@ class TestVmCodeGen: FunSpec({ DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, 0u, + false, null, 3u, Position.DUMMY @@ -79,6 +82,7 @@ class TestVmCodeGen: FunSpec({ DataType.WORD, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -167,6 +171,7 @@ class TestVmCodeGen: FunSpec({ DataType.FLOAT, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -238,6 +243,7 @@ class TestVmCodeGen: FunSpec({ DataType.FLOAT, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -305,6 +311,7 @@ class TestVmCodeGen: FunSpec({ DataType.FLOAT, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -360,6 +367,7 @@ class TestVmCodeGen: FunSpec({ DataType.BYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -431,6 +439,7 @@ class TestVmCodeGen: FunSpec({ DataType.BYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -498,6 +507,7 @@ class TestVmCodeGen: FunSpec({ DataType.BYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY diff --git a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt index b45ea73ec..e0e7c6c05 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt @@ -646,6 +646,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro srcVar.datatype, srcVar.zeropage, srcVar.alignment, + srcVar.dirty, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position diff --git a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt index 012bceae6..0ffed37de 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt @@ -82,6 +82,7 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map + + diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index 817ff3bcd..f62d60a24 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -86,14 +86,14 @@ class TestSymbolTable: FunSpec({ test("static vars") { val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY) - val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, node) + val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false,node) stVar1.setOnetimeInitNumeric(99.0) - val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, node) + val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, node) val arrayInitNonzero = listOf(StArrayElement(1.1, null, null), StArrayElement(2.2, null, null), StArrayElement(3.3, null, null)) val arrayInitAllzero = listOf(StArrayElement(0.0, null, null), StArrayElement(0.0, null, null), StArrayElement(0.0, null, null)) - val stVar3 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitNonzero, 3u, ZeropageWish.DONTCARE, 0u, node) - val stVar4 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitAllzero, 3u, ZeropageWish.DONTCARE, 0u, node) - val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3u, ZeropageWish.DONTCARE, 0u, node) + val stVar3 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitNonzero, 3u, ZeropageWish.DONTCARE, 0u, false, node) + val stVar4 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitAllzero, 3u, ZeropageWish.DONTCARE, 0u, false, node) + val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3u, ZeropageWish.DONTCARE, 0u, false, node) stVar1.uninitialized shouldBe false stVar1.length shouldBe null @@ -125,6 +125,7 @@ private fun makeSt(): SymbolTable { DataType.BYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -134,6 +135,7 @@ private fun makeSt(): SymbolTable { DataType.BYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -143,6 +145,7 @@ private fun makeSt(): SymbolTable { DataType.FLOAT, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -152,6 +155,7 @@ private fun makeSt(): SymbolTable { DataType.UWORD, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -161,6 +165,7 @@ private fun makeSt(): SymbolTable { DataType.BYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -170,6 +175,7 @@ private fun makeSt(): SymbolTable { DataType.BYTE, ZeropageWish.DONTCARE, 0u, + false, null, null, Position.DUMMY @@ -205,12 +211,12 @@ private fun makeSt(): SymbolTable { block1.add(sub12) block1.add(StConstant("c1", BaseDataType.UWORD, 12345.0, astConstant1)) block1.add(StConstant("blockc", BaseDataType.UWORD, 999.0, astConstant2)) - sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, astSub1v1)) - sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, astSub1v2)) + sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v1)) + sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v2)) sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3)) sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4)) - sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, astSub2v1)) - sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, astSub2v2)) + sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub2v1)) + sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub2v2)) val block2 = StNode("block2", StNodeType.BLOCK, astBlock2) val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21) val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22) diff --git a/docs/source/software.rst b/docs/source/software.rst index 037878385..c6e1fe72b 100644 --- a/docs/source/software.rst +++ b/docs/source/software.rst @@ -50,6 +50,10 @@ Various things: of routines to perform tile and sprite transformations and rotation, using the VeraFX hardware feature. Includes examples. +`C64 REU Banking `_ + A Prog8 library module that provides Commander X16 style RAM banking on a C64 with an REU. + This module provides cx16.rambank(), x16jsrfar() and extsub @bank functionality on a C64. + .. image:: _static/curious.png :align: center diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ff3af7d7b..dfde4d1b9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -34,8 +34,8 @@ STRUCTS and TYPED POINTERS - DONE: pointer arrays are split-words only, enforce this (variable dt + initializer array dt) - DONE: make an error message for all pointer expressions (prefixed, binary) so we can start implementing the ones we need one by one. - DONE: start by making ptr.value++ work , and ptr.value = ptr.value+20, and ptr.value = cx16.r0L+20+ptr.value Likewise for subtraction. DON'T FORGET C POINTER SEMANTICS. Other operators are nonsensical for ptr arith +- DONE: support @dirty on pointer vars -> uninitialized pointer placed in BSS_noclear segment - fix actual _msb/_lsb storage of the split-words pointer-arrays -- support @dirty on pointer vars -> uninitialized pointer placed in BSS_noclear segment - pointer types in subroutine signatures (both normal and asm-subs) - support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field) - are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can't we just use ARRAY? @@ -119,6 +119,7 @@ Optimizations ------------- - Sorting module gnomesort_uw could be optimized more by fully rewriting it in asm? Shellshort seems consistently faster even if most of the words are already sorted. +- can the for loop temp var be replaced by the same logic that createRepeatCounterVar() uses for repeat loops? Take care of nested for/repeat loops to not use the same var - Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples) - Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression" - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served? diff --git a/examples/test.p8 b/examples/test.p8 index 4185883ab..f32206483 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,9 +4,6 @@ main { sub start() { -; uword buf = memory("buffer", 2000, 0) -; sys.memset(buf, 2000, 0) - ; put 9 nodes into the buffer sequentially. ; each of the first 3 nodes points to the 4th, 5th, 6th. ; these in turn point to the 7th, 8th and 9th. @@ -40,196 +37,199 @@ main { ; static initializer syntax: -; ^^Node @shared node0 = Node() ; no initialization (will be in BSS and zeroed out at startup) -; ^^Node @shared node1 = Node( false, 11, 0 ) -; ^^Node @shared node2 = Node( false, 22, 0 ) -; ^^Node @shared node3 = Node( true, 33, 0 ) -; -; ; list of pointers: (W.I.P.): -;; ^^Node[5] @shared nodes -;; for nptr in nodes { -;; txt.print_uw(nptr) -;; txt.spc() -;; } -;; txt.nl() -; -; ; link up -; node0.next = node1 -; node1.next = node2 -; node2.next = node3 -; -; ^^Node nptr = node0 -; while nptr { -; txt.print("node at ") + ^^Node @shared node0 = Node() ; no initialization (will be in BSS and zeroed out at startup) + ^^Node @shared node1 = Node( false, 11, 0 ) + ^^Node @shared node2 = Node( false, 22, 0 ) + ^^Node @shared node3 = Node( true, 33, 0 ) + + ; list of pointers: (W.I.P.): +; ^^Node[5] @shared nodes +; for nptr in nodes { ; txt.print_uw(nptr) -; txt.print("\n flag=") -; txt.print_bool(nptr.flag) -; txt.print("\n value=") -; txt.print_ub(nptr.value) -; txt.nl() -; nptr = nptr.next -; } -; -; ^^Node n0,n1,n2,n3,n4,n5,n6,n7,n8 -; -; n0 = buf + 0 -; n1 = buf + sizeof(Node) -; n2 = buf + sizeof(Node)*2 -; n3 = buf + sizeof(Node)*3 -; n4 = buf + sizeof(Node)*4 -; n5 = buf + sizeof(Node)*5 -; n6 = buf + sizeof(Node)*6 -; n7 = buf + sizeof(Node)*7 -; n8 = buf + sizeof(Node)*8 -; -; n0.next = n3 -; n1.next = n4 -; n2.next = n5 -; n3.next = n6 -; n4.next = n7 -; n5.next = n8 -; -; n0.value = 'a' -; n1.value = 'b' -; n2.value = 'c' -; n3.value = 'd' -; n4.value = 'e' -; n5.value = 'f' -; n6.value = 'g' -; n7.value = 'h' -; n8.value = 'i' -; -; txt.print("struct size: ") -; txt.print_uw(sizeof(Node)) -; txt.nl() -; -; txt.print("pointer values: ") -; txt.print_uw(n0) -; txt.spc() -; txt.print_uw(n1) -; txt.spc() -; txt.print_uw(n2) -; txt.spc() -; txt.print_uw(n3) -; txt.spc() -; txt.print_uw(n4) -; txt.spc() -; txt.print_uw(n5) -; txt.spc() -; txt.print_uw(n6) -; txt.spc() -; txt.print_uw(n7) -; txt.spc() -; txt.print_uw(n8) -; txt.nl() -; -; txt.print("field address: ") -; txt.print_uw(&n0.value) -; txt.spc() -; txt.print_uw(&n1.value) -; txt.spc() -; txt.print_uw(&n2.value) -; txt.nl() -; txt.print_uw(&n6.value) -; txt.spc() -; txt.print_uw(&n7.value) -; txt.spc() -; txt.print_uw(&n8.value) -; txt.nl() -; txt.print_uw(&n0.next.next.value) -; txt.spc() -; txt.print_uw(&n1.next.next.value) -; txt.spc() -; txt.print_uw(&n2.next.next.value) -; txt.nl() -; -; txt.print("node values: ") -; txt.chrout(n0.value) -; txt.chrout(n1.value) -; txt.chrout(n2.value) -; txt.chrout(n3.value) -; txt.chrout(n4.value) -; txt.chrout(n5.value) -; txt.chrout(n6.value) -; txt.chrout(n7.value) -; txt.chrout(n8.value) -; txt.nl() -; -; txt.print("linked values:\n") -; txt.print("n0: ") -; ^^Node ptr = n0 -; while ptr { -; txt.chrout(ptr.value) -; ptr = ptr.next +; txt.spc() ; } ; txt.nl() -; txt.print("n1: ") -; ptr = n1 -; while ptr { -; txt.chrout(ptr.value) -; ptr = ptr.next -; } -; txt.nl() -; txt.print("n2: ") -; ptr = n2 -; while ptr { -; txt.chrout(ptr.value) -; ptr = ptr.next -; } -; txt.nl() -; -; txt.print("array syntax on nodes: ") -; txt.chrout(n0[0].value) -; txt.chrout(n0[1].value) -; txt.chrout(n0[2].value) -; txt.chrout(n0[3].value) -; txt.chrout(n0[4].value) -; txt.chrout(n0[5].value) -; txt.chrout(n0[6].value) -; txt.chrout(n0[7].value) -; txt.chrout(n0[8].value) -; txt.nl() -; -; txt.print("array syntax followed by dereference: ") -; txt.chrout(n0[0].next.next.value) -; txt.chrout(n0[1].next.next.value) -; txt.chrout(n0[2].next.next.value) -; txt.nl() -; -; txt.print("assigning to fields: ") -; n0.value = 'q' -; n1.value = 'w' -; n2.value = 'e' -; n0.next.next.value = 'x' -; n1.next.next.value = 'y' -; n2.next.next.value = 'z' -; txt.chrout(n0.value) -; txt.chrout(n1.value) -; txt.chrout(n2.value) -; txt.spc() -; txt.chrout(n0.next.next.value) -; txt.chrout(n1.next.next.value) -; txt.chrout(n2.next.next.value) -; txt.spc() -; txt.chrout(n6.value) -; txt.chrout(n7.value) -; txt.chrout(n8.value) -; txt.nl() -; -; txt.print("ptr to simple types: ") -; word w_value = -9999 -; txt.print_w(w_value) -; txt.spc() -; ^^word w_ptr = &w_value -; w_ptr^^ = 5555 -; txt.print_w(w_value) -; txt.nl() -; -; word[] @nosplit warray = [1111,2222,3333,4444,5555,6666] -; w_ptr = &warray -; txt.print_w(w_ptr^^) -; txt.spc() -; txt.print_w(w_ptr[4]^^) -; txt.nl() + + ; link up + node0.next = node1 + node1.next = node2 + node2.next = node3 + + ^^Node nptr = node0 + while nptr { + txt.print("node at ") + txt.print_uw(nptr) + txt.print("\n flag=") + txt.print_bool(nptr.flag) + txt.print("\n value=") + txt.print_ub(nptr.value) + txt.nl() + nptr = nptr.next + } + + ^^Node n0,n1,n2,n3,n4,n5,n6,n7,n8 + + uword buf = memory("buffer", 2000, 0) + sys.memset(buf, 2000, 0) + + n0 = buf + 0 + n1 = buf + sizeof(Node) + n2 = buf + sizeof(Node)*2 + n3 = buf + sizeof(Node)*3 + n4 = buf + sizeof(Node)*4 + n5 = buf + sizeof(Node)*5 + n6 = buf + sizeof(Node)*6 + n7 = buf + sizeof(Node)*7 + n8 = buf + sizeof(Node)*8 + + n0.next = n3 + n1.next = n4 + n2.next = n5 + n3.next = n6 + n4.next = n7 + n5.next = n8 + + n0.value = 'a' + n1.value = 'b' + n2.value = 'c' + n3.value = 'd' + n4.value = 'e' + n5.value = 'f' + n6.value = 'g' + n7.value = 'h' + n8.value = 'i' + + txt.print("struct size: ") + txt.print_uw(sizeof(Node)) + txt.nl() + + txt.print("pointer values: ") + txt.print_uw(n0) + txt.spc() + txt.print_uw(n1) + txt.spc() + txt.print_uw(n2) + txt.spc() + txt.print_uw(n3) + txt.spc() + txt.print_uw(n4) + txt.spc() + txt.print_uw(n5) + txt.spc() + txt.print_uw(n6) + txt.spc() + txt.print_uw(n7) + txt.spc() + txt.print_uw(n8) + txt.nl() + + txt.print("field address: ") + txt.print_uw(&n0.value) + txt.spc() + txt.print_uw(&n1.value) + txt.spc() + txt.print_uw(&n2.value) + txt.nl() + txt.print_uw(&n6.value) + txt.spc() + txt.print_uw(&n7.value) + txt.spc() + txt.print_uw(&n8.value) + txt.nl() + txt.print_uw(&n0.next.next.value) + txt.spc() + txt.print_uw(&n1.next.next.value) + txt.spc() + txt.print_uw(&n2.next.next.value) + txt.nl() + + txt.print("node values: ") + txt.chrout(n0.value) + txt.chrout(n1.value) + txt.chrout(n2.value) + txt.chrout(n3.value) + txt.chrout(n4.value) + txt.chrout(n5.value) + txt.chrout(n6.value) + txt.chrout(n7.value) + txt.chrout(n8.value) + txt.nl() + + txt.print("linked values:\n") + txt.print("n0: ") + ptr = n0 + while ptr { + txt.chrout(ptr.value) + ptr = ptr.next + } + txt.nl() + txt.print("n1: ") + ptr = n1 + while ptr { + txt.chrout(ptr.value) + ptr = ptr.next + } + txt.nl() + txt.print("n2: ") + ptr = n2 + while ptr { + txt.chrout(ptr.value) + ptr = ptr.next + } + txt.nl() + + txt.print("array syntax on nodes: ") + txt.chrout(n0[0].value) + txt.chrout(n0[1].value) + txt.chrout(n0[2].value) + txt.chrout(n0[3].value) + txt.chrout(n0[4].value) + txt.chrout(n0[5].value) + txt.chrout(n0[6].value) + txt.chrout(n0[7].value) + txt.chrout(n0[8].value) + txt.nl() + + txt.print("array syntax followed by dereference: ") + txt.chrout(n0[0].next.next.value) + txt.chrout(n0[1].next.next.value) + txt.chrout(n0[2].next.next.value) + txt.nl() + + txt.print("assigning to fields: ") + n0.value = 'q' + n1.value = 'w' + n2.value = 'e' + n0.next.next.value = 'x' + n1.next.next.value = 'y' + n2.next.next.value = 'z' + txt.chrout(n0.value) + txt.chrout(n1.value) + txt.chrout(n2.value) + txt.spc() + txt.chrout(n0.next.next.value) + txt.chrout(n1.next.next.value) + txt.chrout(n2.next.next.value) + txt.spc() + txt.chrout(n6.value) + txt.chrout(n7.value) + txt.chrout(n8.value) + txt.nl() + + txt.print("ptr to simple types: ") + word w_value = -9999 + txt.print_w(w_value) + txt.spc() + ^^word w_ptr = &w_value + w_ptr^^ = 5555 + txt.print_w(w_value) + txt.nl() + + word[] @nosplit warray = [1111,2222,3333,4444,5555,6666] + w_ptr = &warray + txt.print_w(w_ptr^^) + txt.spc() + txt.print_w(w_ptr[4]^^) + txt.nl() } } diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 98301358b..b0c429071 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -48,7 +48,8 @@ class IRFileReader { val programName = start.attributes.asSequence().single { it.name.localPart == "NAME" }.value val options = parseOptions(reader) val asmsymbols = parseAsmSymbols(reader) - val varsWithoutInit = parseVarsWithoutInit(reader) + val varsWithoutInitClean = parseVarsWithoutInit("VARIABLESNOINIT", false, reader) + val varsWithoutInitDirty = parseVarsWithoutInit("VARIABLESNOINITDIRTY", true, reader) val variables = parseVariables(reader) val structsWithoutInit = parseStructInstancesNoInit(reader) val structs = parseStructInstances(reader) @@ -60,7 +61,8 @@ class IRFileReader { val st = IRSymbolTable() asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)} - varsWithoutInit.forEach { st.add(it) } + varsWithoutInitClean.forEach { st.add(it) } + varsWithoutInitDirty.forEach { st.add(it) } variables.forEach { st.add(it) } constants.forEach { st.add(it) } memorymapped.forEach { st.add(it) } @@ -157,10 +159,10 @@ class IRFileReader { } } - private fun parseVarsWithoutInit(reader: XMLEventReader): List { + private fun parseVarsWithoutInit(segmentname: String, dirty: Boolean, reader: XMLEventReader): List { skipText(reader) val start = reader.nextEvent().asStartElement() - require(start.name.localPart=="VARIABLESNOINIT") { "missing VARIABLESNOINIT" } + require(start.name.localPart==segmentname) { "missing $segmentname" } val text = readText(reader).trim() require(reader.nextEvent().isEndElement) @@ -171,7 +173,7 @@ class IRFileReader { val variables = mutableListOf() text.lineSequence().forEach { line -> // example: uword main.start.qq2 zp=DONTCARE - val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLESNOINIT $line") + val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid $segmentname $line") val type = match.groups["type"]!!.value val arrayspec = match.groups["arrayspec"]?.value ?: "" val name = match.groups["name"]!!.value @@ -185,7 +187,7 @@ class IRFileReader { val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish) // val isSplit = if(split.isBlank()) false else split.toBoolean() val align = if(alignment.isBlank()) 0u else alignment.toUInt() - val newVar = IRStStaticVariable(name, dt, null, null, null, arraysize, zp, align) + val newVar = IRStStaticVariable(name, dt, null, null, null, arraysize, zp, align, dirty) variables.add(newVar) } return variables @@ -250,6 +252,7 @@ class IRFileReader { val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish) if(split.isBlank()) false else split.toBoolean() val align = if(alignment.isBlank()) 0u else alignment.toUInt() + val dirty = false // these variables have initialization values. var initNumeric: Double? = null var initArray: IRStArray? = null when { @@ -274,7 +277,7 @@ class IRFileReader { if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) { initArray=null // arrays with just zeros can be left uninitialized } - val stVar = IRStStaticVariable(name, dt, initNumeric, null, initArray, arraysize, zp, align) + val stVar = IRStStaticVariable(name, dt, initNumeric, null, initArray, arraysize, zp, align, dirty) variables.add(stVar) } return variables diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index dd62e9d34..31153ba76 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -330,19 +330,25 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } } + fun writeNoInitVars(segmentname: String, variables: List) { + xml.writeStartElement(segmentname) + xml.writeCharacters("\n") + val (noinitNotAligned, noinitAligned) = variables.partition { it.align==0u || it.align==1u } + for (variable in noinitNotAligned) { + writeNoInitVar(variable) + } + for (variable in noinitAligned.sortedBy { it.align }) { + writeNoInitVar(variable) + } + xml.writeEndElement() + xml.writeCharacters("\n") + } + val (variablesNoInit, variablesWithInit) = irProgram.st.allVariables().partition { it.uninitialized } - xml.writeStartElement("VARIABLESNOINIT") - xml.writeCharacters("\n") - val (noinitNotAligned, noinitAligned) = variablesNoInit.partition { it.align==0u || it.align==1u } - for (variable in noinitNotAligned) { - writeNoInitVar(variable) - } - for (variable in noinitAligned.sortedBy { it.align }) { - writeNoInitVar(variable) - } - xml.writeEndElement() - xml.writeCharacters("\n") + val (dirtyvars, cleanvars) = variablesNoInit.partition { it.dirty } + writeNoInitVars("VARIABLESNOINIT", cleanvars) + writeNoInitVars("VARIABLESNOINITDIRTY", dirtyvars) xml.writeStartElement("VARIABLESWITHINIT") xml.writeCharacters("\n") diff --git a/intermediate/src/prog8/intermediate/IRSymbolTable.kt b/intermediate/src/prog8/intermediate/IRSymbolTable.kt index 65f680465..d3b0e25aa 100644 --- a/intermediate/src/prog8/intermediate/IRSymbolTable.kt +++ b/intermediate/src/prog8/intermediate/IRSymbolTable.kt @@ -1,10 +1,10 @@ package prog8.intermediate import prog8.code.INTERNED_STRINGS_MODULENAME -import prog8.code.core.BaseDataType import prog8.code.core.DataType import prog8.code.core.Encoding import prog8.code.core.ZeropageWish +import prog8.code.core.BaseDataType // In the Intermediate Representation, all nesting has been removed. @@ -101,7 +101,8 @@ class IRStStaticVariable(name: String, val onetimeInitializationArrayValue: IRStArray?, val length: UInt?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val zpwish: ZeropageWish, // used in the variable allocator - val align: UInt + val align: UInt, + val dirty: Boolean ) : IRStNode(name, IRStNodeType.STATICVAR) { init { if(align > 0u) { diff --git a/intermediate/test/TestIRFileInOut.kt b/intermediate/test/TestIRFileInOut.kt index 91e391849..64f229d6b 100644 --- a/intermediate/test/TestIRFileInOut.kt +++ b/intermediate/test/TestIRFileInOut.kt @@ -55,6 +55,8 @@ loadAddress=$0000 uword sys.bssvar zp=DONTCARE align=0 + + uword sys.wait.jiffies=10 zp=DONTCARE align=0 ubyte[3] sys.emptystring=0,0,0 zp=DONTCARE align=0 diff --git a/simpleAst/src/prog8/code/SymbolTable.kt b/simpleAst/src/prog8/code/SymbolTable.kt index b15749aa2..a05183057 100644 --- a/simpleAst/src/prog8/code/SymbolTable.kt +++ b/simpleAst/src/prog8/code/SymbolTable.kt @@ -215,6 +215,7 @@ class StStaticVariable(name: String, val length: UInt?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val zpwish: ZeropageWish, // used in the variable allocator val align: UInt, + val dirty: Boolean, astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) { var initializationNumericValue: Double? = null diff --git a/simpleAst/src/prog8/code/SymbolTableMaker.kt b/simpleAst/src/prog8/code/SymbolTableMaker.kt index 134958efd..120ee647f 100644 --- a/simpleAst/src/prog8/code/SymbolTableMaker.kt +++ b/simpleAst/src/prog8/code/SymbolTableMaker.kt @@ -106,7 +106,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp // if(node.type in SplitWordArrayTypes) { // ... split array also add _lsb and _msb to symboltable? // } - val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align, node) + val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align, node.dirty,node) if(initialNumeric!=null) stVar.setOnetimeInitNumeric(initialNumeric) stVar diff --git a/simpleAst/src/prog8/code/ast/AstStatements.kt b/simpleAst/src/prog8/code/ast/AstStatements.kt index c719cd5ae..adceb8d3e 100644 --- a/simpleAst/src/prog8/code/ast/AstStatements.kt +++ b/simpleAst/src/prog8/code/ast/AstStatements.kt @@ -198,6 +198,7 @@ class PtVariable( override val type: DataType, val zeropage: ZeropageWish, val align: UInt, + val dirty: Boolean, val value: PtExpression?, val arraySize: UInt?, position: Position @@ -215,6 +216,8 @@ class PtVariable( // The IR codegen however is different it has a special section for all variables // that have a non-zero initialization value, regardless of the datatype. It removes the initialization // assignment and puts the value back into the variable (but only in the symboltable). + + require(!dirty) { "dirty var cannot have init value" } } value?.let {it.parent=this} diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index f09a5b96c..13053dd89 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -196,10 +196,10 @@ class VmProgramLoader { program.st.allVariables().forEach { variable -> var addr = allocations.allocations.getValue(variable.name) - // zero out uninitialized ('bss') variables. - if(variable.uninitialized) { + // zero out uninitialized non-dirty ('bss') variables. + if(variable.uninitialized && !variable.dirty) { val dt = variable.dt - if(dt.isArray) { + if(variable.dt.isArray) { repeat(variable.length!!.toInt()) { when { dt.isPointerArray -> { diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index a49fe6f21..8962eee9d 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -103,6 +103,8 @@ class TestVm: FunSpec( { + +