From ccc6b56e354d5e11f7291c69ccad8beb271df9af Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 2 May 2025 19:38:59 +0200 Subject: [PATCH 1/2] added link to prog8reu library --- docs/source/software.rst | 4 ++++ 1 file changed, 4 insertions(+) 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 From 522958e0e92cc3e94b96dd0ba98d3c3f7b91d55f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 6 May 2025 00:47:54 +0200 Subject: [PATCH 2/2] @dirty variables now actually end up in the uninitialized BSS_NOCLEAR section --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 2 +- .../codegen/cpu6502/ProgramAndVarsGen.kt | 35 ++++++--- codeGenCpu6502/test/TestCodegen.kt | 4 ++ .../prog8/codegen/intermediate/StConvert.kt | 6 +- codeGenIntermediate/test/TestVmCodeGen.kt | 10 +++ .../astprocessing/SimplifiedAstMaker.kt | 1 + .../astprocessing/SimplifiedAstPostprocess.kt | 1 + compiler/test/TestLaunchEmu.kt | 2 + compiler/test/TestSymbolTable.kt | 24 ++++--- docs/source/todo.rst | 2 +- examples/test.p8 | 72 ++++++++++++++++--- .../src/prog8/intermediate/IRFileReader.kt | 17 +++-- .../src/prog8/intermediate/IRFileWriter.kt | 28 +++++--- .../src/prog8/intermediate/IRSymbolTable.kt | 9 ++- intermediate/test/TestIRFileInOut.kt | 2 + simpleAst/src/prog8/code/SymbolTable.kt | 1 + simpleAst/src/prog8/code/SymbolTableMaker.kt | 2 +- simpleAst/src/prog8/code/ast/AstStatements.kt | 3 + .../src/prog8/vm/VmProgramLoader.kt | 4 +- virtualmachine/test/TestVm.kt | 2 + 20 files changed, 171 insertions(+), 56 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index c244850e0..baf7978c8 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 74804506a..9008999a5 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==0 } - 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 == 0 } + 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 fa961a453..3da07eb05 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt @@ -52,7 +52,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) @@ -76,7 +77,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 36d1550b4..052ab7a30 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt @@ -578,6 +578,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 fe7f33e7a..9e77e666c 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt @@ -46,6 +46,7 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map + + diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index 95dc6cb27..8a063cb30 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, 0, node) + val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0, false,node) stVar1.setOnetimeInitNumeric(99.0) - val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0, node) + val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0, 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, 3, ZeropageWish.DONTCARE, 0, node) - val stVar4 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, 0, node) - val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3, ZeropageWish.DONTCARE, 0, node) + val stVar3 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitNonzero, 3, ZeropageWish.DONTCARE, 0, false, node) + val stVar4 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, 0, false, node) + val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3, ZeropageWish.DONTCARE, 0, 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, 0, astSub1v1)) - sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, astSub1v2)) + sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, false, astSub1v1)) + sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, 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, 0, astSub2v1)) - sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, astSub2v2)) + sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, false, astSub2v1)) + sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, 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/todo.rst b/docs/source/todo.rst index 2e185dd6e..a744a2954 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,7 +1,6 @@ TODO ==== - ... @@ -68,6 +67,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 ce20dd70a..ae8fb2c5e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,15 +1,71 @@ -%zeropage basicsafe +%import floats %import textio -%import strings +%option no_sysinit +%zeropage basicsafe + +; DIRTY tests main { - sub start() { - str name1 = sc:"irmen de jong 123456789 the quick brown fox" - str name2 = sc:"jumps over the lazy frog" + 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] - txt.print_ub(strings.hash(name1)) - txt.spc() - txt.print_ub(strings.hash(name2)) + sub start() { + testdirty() + txt.nl() + testdirty() txt.nl() } + + sub testdirty() { + 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] + + txt.print("globals: ") + txt.print_uw(globw) + txt.spc() + floats.print(globf) + txt.print(" with init: ") + txt.print_uw(globwi) + txt.spc() + floats.print(globfi) + txt.print(" arrays: ") + txt.print_ub(globarr1[2]) + txt.spc() + txt.print_ub(globarr2[2]) + txt.print("\nlocals: ") + txt.print_uw(locw) + txt.spc() + floats.print(locf) + txt.print(" with init: ") + txt.print_uw(locwi) + txt.spc() + floats.print(locfi) + txt.print(" arrays: ") + txt.print_ub(locarr1[2]) + txt.spc() + txt.print_ub(locarr2[2]) + txt.nl() + + + globw++ + globwi++ + globf++ + globfi++ + globarr1[2]++ + globarr2[2]++ + locw++ + locwi++ + locf++ + locfi++ + locarr1[2]++ + locarr2[2]++ + } } diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 0a6f619a3..f1acfb866 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 constants = parseConstants(reader) val memorymapped = parseMemMapped(reader) @@ -58,7 +59,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) } @@ -153,10 +155,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) @@ -167,7 +169,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 @@ -181,7 +183,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.toInt()) + val newVar = IRStStaticVariable(name, dt, null, null, null, arraysize, zp, align.toInt(), dirty) variables.add(newVar) } return variables @@ -246,6 +248,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 { @@ -270,7 +273,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.toInt()) + val stVar = IRStStaticVariable(name, dt, initNumeric, null, initArray, arraysize, zp, align.toInt(), dirty) variables.add(stVar) } return variables diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 990194ab2..41ddcb262 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -328,19 +328,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==0 || it.align==1 } + 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==0 || it.align==1 } - 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 d0f6d6cd6..8dd937c76 100644 --- a/intermediate/src/prog8/intermediate/IRSymbolTable.kt +++ b/intermediate/src/prog8/intermediate/IRSymbolTable.kt @@ -1,7 +1,9 @@ package prog8.intermediate -import prog8.code.* -import prog8.code.core.* +import prog8.code.INTERNED_STRINGS_MODULENAME +import prog8.code.core.DataType +import prog8.code.core.Encoding +import prog8.code.core.ZeropageWish // In the Intermediate Representation, all nesting has been removed. @@ -93,7 +95,8 @@ class IRStStaticVariable(name: String, val onetimeInitializationArrayValue: IRStArray?, val length: Int?, // 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: Int + val align: Int, + val dirty: Boolean ) : IRStNode(name, IRStNodeType.STATICVAR) { init { if(align > 0) { diff --git a/intermediate/test/TestIRFileInOut.kt b/intermediate/test/TestIRFileInOut.kt index 388081e93..ec535adbb 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 144ea1bc2..c646fdc57 100644 --- a/simpleAst/src/prog8/code/SymbolTable.kt +++ b/simpleAst/src/prog8/code/SymbolTable.kt @@ -187,6 +187,7 @@ class StStaticVariable(name: String, val length: Int?, // 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: Int, + 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 fffcfc19a..f97e569ec 100644 --- a/simpleAst/src/prog8/code/SymbolTableMaker.kt +++ b/simpleAst/src/prog8/code/SymbolTableMaker.kt @@ -99,7 +99,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.toInt(), node) + val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align.toInt(), 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 66e453b6b..caeebc6db 100644 --- a/simpleAst/src/prog8/code/ast/AstStatements.kt +++ b/simpleAst/src/prog8/code/ast/AstStatements.kt @@ -189,6 +189,7 @@ class PtVariable( override val type: DataType, val zeropage: ZeropageWish, val align: UInt, + val dirty: Boolean, val value: PtExpression?, val arraySize: UInt?, position: Position @@ -204,6 +205,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 a1b34e65b..fcb77444f 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -196,8 +196,8 @@ 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) { if(variable.dt.isArray) { val dt = variable.dt repeat(variable.length!!) { diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index 939c81352..e9e2da167 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -103,6 +103,8 @@ class TestVm: FunSpec( { + +