Merge branch 'master' into structs

# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
#	compiler/test/TestSymbolTable.kt
#	docs/source/todo.rst
#	examples/test.p8
#	intermediate/src/prog8/intermediate/IRFileReader.kt
#	intermediate/src/prog8/intermediate/IRFileWriter.kt
#	intermediate/src/prog8/intermediate/IRSymbolTable.kt
#	simpleAst/src/prog8/code/SymbolTable.kt
#	simpleAst/src/prog8/code/SymbolTableMaker.kt
#	virtualmachine/src/prog8/vm/VmProgramLoader.kt
This commit is contained in:
Irmen de Jong
2025-05-06 17:55:07 +02:00
21 changed files with 301 additions and 239 deletions

View File

@@ -186,7 +186,7 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
else -> throw AssemblyError("weird array value element $elt") 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.parent = parent
result result
} }

View File

@@ -220,7 +220,8 @@ internal class ProgramAndVarsGen(
} }
private fun tempVars() { 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) { for((dt, count) in asmgen.tempVarsCounters) {
if(count>0) { if(count>0) {
for(num in 1..count) { for(num in 1..count) {
@@ -238,7 +239,7 @@ internal class ProgramAndVarsGen(
} }
} }
} }
asmgen.out(" .send BSS") asmgen.out(" .send BSS_NOCLEAR")
} }
private fun footer() { private fun footer() {
@@ -494,7 +495,7 @@ internal class ProgramAndVarsGen(
sub.children.forEach { asmgen.translate(it) } sub.children.forEach { asmgen.translate(it) }
asmgen.out("; variables") asmgen.out("; variables")
asmgen.out(" .section BSS") asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
val asmGenInfo = asmgen.subroutineExtra(sub) val asmGenInfo = asmgen.subroutineExtra(sub)
for((dt, name, addr) in asmGenInfo.extraVars) { for((dt, name, addr) in asmGenInfo.extraVars) {
if(addr!=null) if(addr!=null)
@@ -510,7 +511,7 @@ internal class ProgramAndVarsGen(
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}") asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2) if(asmGenInfo.usedFloatEvalResultVar2)
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}") asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
asmgen.out(" .send BSS") asmgen.out(" .send BSS_NOCLEAR")
// normal statically allocated variables // normal statically allocated variables
val variables = varsInSubroutine val variables = varsInSubroutine
@@ -638,15 +639,27 @@ internal class ProgramAndVarsGen(
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized } val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
if(varsNoInit.isNotEmpty()) { if(varsNoInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables") asmgen.out("; non-zeropage variables")
asmgen.out(" .section BSS") val (dirty, clean) = varsNoInit.partition { it.dirty }
val (notAligned, aligned) = varsNoInit.partition { it.align==0u }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach { fun generate(section: String, variables: List<StStaticVariable>) {
uninitializedVariable2asm(it) asmgen.out(" .section $section")
val (notAligned, aligned) = variables.partition { it.align == 0u }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it)
}
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
.forEach { uninitializedVariable2asm(it) }
asmgen.out(" .send $section")
} }
aligned.sortedWith(compareBy<StStaticVariable> { 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()) { if(varsWithInit.isNotEmpty()) {

View File

@@ -58,6 +58,7 @@ class TestCodegen: FunSpec({
DataType.UBYTE, DataType.UBYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -67,6 +68,7 @@ class TestCodegen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
@@ -76,6 +78,7 @@ class TestCodegen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
@@ -85,6 +88,7 @@ class TestCodegen: FunSpec({
DataType.WORD, DataType.WORD,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY

View File

@@ -63,7 +63,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
variable.initializationArrayValue?.map { convertArrayElt(it) }, variable.initializationArrayValue?.map { convertArrayElt(it) },
variable.length, variable.length,
variable.zpwish, variable.zpwish,
variable.align) variable.align,
variable.dirty)
} else { } else {
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? { fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
if(array==null) if(array==null)
@@ -87,7 +88,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
fixupAddressOfInArray(variable.initializationArrayValue), fixupAddressOfInArray(variable.initializationArrayValue),
variable.length, variable.length,
variable.zpwish, variable.zpwish,
variable.align variable.align,
variable.dirty
) )
} }
} }

View File

@@ -52,6 +52,7 @@ class TestVmCodeGen: FunSpec({
DataType.UBYTE, DataType.UBYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -61,6 +62,7 @@ class TestVmCodeGen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
@@ -70,6 +72,7 @@ class TestVmCodeGen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
@@ -79,6 +82,7 @@ class TestVmCodeGen: FunSpec({
DataType.WORD, DataType.WORD,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -167,6 +171,7 @@ class TestVmCodeGen: FunSpec({
DataType.FLOAT, DataType.FLOAT,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -238,6 +243,7 @@ class TestVmCodeGen: FunSpec({
DataType.FLOAT, DataType.FLOAT,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -305,6 +311,7 @@ class TestVmCodeGen: FunSpec({
DataType.FLOAT, DataType.FLOAT,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -360,6 +367,7 @@ class TestVmCodeGen: FunSpec({
DataType.BYTE, DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -431,6 +439,7 @@ class TestVmCodeGen: FunSpec({
DataType.BYTE, DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -498,6 +507,7 @@ class TestVmCodeGen: FunSpec({
DataType.BYTE, DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY

View File

@@ -646,6 +646,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
srcVar.datatype, srcVar.datatype,
srcVar.zeropage, srcVar.zeropage,
srcVar.alignment, srcVar.alignment,
srcVar.dirty,
value, value,
srcVar.arraysize?.constIndex()?.toUInt(), srcVar.arraysize?.constIndex()?.toUInt(),
srcVar.position srcVar.position

View File

@@ -82,6 +82,7 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
DataType.UBYTE, DataType.UBYTE,
ZeropageWish.NOT_IN_ZEROPAGE, ZeropageWish.NOT_IN_ZEROPAGE,
0u, 0u,
true,
null, null,
null, null,
sub.position sub.position

View File

@@ -21,6 +21,8 @@ class TestLaunchEmu: FunSpec({
<VARIABLESNOINIT> <VARIABLESNOINIT>
</VARIABLESNOINIT> </VARIABLESNOINIT>
<VARIABLESNOINITDIRTY>
</VARIABLESNOINITDIRTY>
<VARIABLESWITHINIT> <VARIABLESWITHINIT>
</VARIABLESWITHINIT> </VARIABLESWITHINIT>
<STRUCTINSTANCESNOINIT> <STRUCTINSTANCESNOINIT>

View File

@@ -86,14 +86,14 @@ class TestSymbolTable: FunSpec({
test("static vars") { test("static vars") {
val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY) 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) 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 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 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 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, 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, node) val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3u, ZeropageWish.DONTCARE, 0u, false, node)
stVar1.uninitialized shouldBe false stVar1.uninitialized shouldBe false
stVar1.length shouldBe null stVar1.length shouldBe null
@@ -125,6 +125,7 @@ private fun makeSt(): SymbolTable {
DataType.BYTE, DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -134,6 +135,7 @@ private fun makeSt(): SymbolTable {
DataType.BYTE, DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -143,6 +145,7 @@ private fun makeSt(): SymbolTable {
DataType.FLOAT, DataType.FLOAT,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -152,6 +155,7 @@ private fun makeSt(): SymbolTable {
DataType.UWORD, DataType.UWORD,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -161,6 +165,7 @@ private fun makeSt(): SymbolTable {
DataType.BYTE, DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -170,6 +175,7 @@ private fun makeSt(): SymbolTable {
DataType.BYTE, DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@@ -205,12 +211,12 @@ private fun makeSt(): SymbolTable {
block1.add(sub12) block1.add(sub12)
block1.add(StConstant("c1", BaseDataType.UWORD, 12345.0, astConstant1)) block1.add(StConstant("c1", BaseDataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", BaseDataType.UWORD, 999.0, astConstant2)) 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("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, astSub1v2)) 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(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3))
sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4)) sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, astSub2v1)) 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, astSub2v2)) sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub2v2))
val block2 = StNode("block2", StNodeType.BLOCK, astBlock2) val block2 = StNode("block2", StNodeType.BLOCK, astBlock2)
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21) val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21)
val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22) val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22)

View File

@@ -50,6 +50,10 @@ Various things:
of routines to perform tile and sprite transformations and rotation, using of routines to perform tile and sprite transformations and rotation, using
the VeraFX hardware feature. Includes examples. the VeraFX hardware feature. Includes examples.
`C64 REU Banking <https://github.com/gillham/prog8reu>`_
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 .. image:: _static/curious.png
:align: center :align: center

View File

@@ -34,8 +34,8 @@ STRUCTS and TYPED POINTERS
- DONE: pointer arrays are split-words only, enforce this (variable dt + initializer array dt) - 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: 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: 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 - 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) - 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) - 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? - 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. - 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) - 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" - 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? - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?

View File

@@ -4,9 +4,6 @@
main { main {
sub start() { sub start() {
; uword buf = memory("buffer", 2000, 0)
; sys.memset(buf, 2000, 0)
; put 9 nodes into the buffer sequentially. ; put 9 nodes into the buffer sequentially.
; each of the first 3 nodes points to the 4th, 5th, 6th. ; each of the first 3 nodes points to the 4th, 5th, 6th.
; these in turn point to the 7th, 8th and 9th. ; these in turn point to the 7th, 8th and 9th.
@@ -40,196 +37,199 @@ main {
; static initializer syntax: ; static initializer syntax:
; ^^Node @shared node0 = Node() ; no initialization (will be in BSS and zeroed out at startup) ^^Node @shared node0 = Node() ; no initialization (will be in BSS and zeroed out at startup)
; ^^Node @shared node1 = Node( false, 11, 0 ) ^^Node @shared node1 = Node( false, 11, 0 )
; ^^Node @shared node2 = Node( false, 22, 0 ) ^^Node @shared node2 = Node( false, 22, 0 )
; ^^Node @shared node3 = Node( true, 33, 0 ) ^^Node @shared node3 = Node( true, 33, 0 )
;
; ; list of pointers: (W.I.P.): ; list of pointers: (W.I.P.):
;; ^^Node[5] @shared nodes ; ^^Node[5] @shared nodes
;; for nptr in 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 ")
; txt.print_uw(nptr) ; txt.print_uw(nptr)
; txt.print("\n flag=") ; txt.spc()
; 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.nl() ; txt.nl()
; txt.print("n1: ")
; ptr = n1 ; link up
; while ptr { node0.next = node1
; txt.chrout(ptr.value) node1.next = node2
; ptr = ptr.next node2.next = node3
; }
; txt.nl() ^^Node nptr = node0
; txt.print("n2: ") while nptr {
; ptr = n2 txt.print("node at ")
; while ptr { txt.print_uw(nptr)
; txt.chrout(ptr.value) txt.print("\n flag=")
; ptr = ptr.next txt.print_bool(nptr.flag)
; } txt.print("\n value=")
; txt.nl() txt.print_ub(nptr.value)
; txt.nl()
; txt.print("array syntax on nodes: ") nptr = nptr.next
; txt.chrout(n0[0].value) }
; txt.chrout(n0[1].value)
; txt.chrout(n0[2].value) ^^Node n0,n1,n2,n3,n4,n5,n6,n7,n8
; txt.chrout(n0[3].value)
; txt.chrout(n0[4].value) uword buf = memory("buffer", 2000, 0)
; txt.chrout(n0[5].value) sys.memset(buf, 2000, 0)
; txt.chrout(n0[6].value)
; txt.chrout(n0[7].value) n0 = buf + 0
; txt.chrout(n0[8].value) n1 = buf + sizeof(Node)
; txt.nl() n2 = buf + sizeof(Node)*2
; n3 = buf + sizeof(Node)*3
; txt.print("array syntax followed by dereference: ") n4 = buf + sizeof(Node)*4
; txt.chrout(n0[0].next.next.value) n5 = buf + sizeof(Node)*5
; txt.chrout(n0[1].next.next.value) n6 = buf + sizeof(Node)*6
; txt.chrout(n0[2].next.next.value) n7 = buf + sizeof(Node)*7
; txt.nl() n8 = buf + sizeof(Node)*8
;
; txt.print("assigning to fields: ") n0.next = n3
; n0.value = 'q' n1.next = n4
; n1.value = 'w' n2.next = n5
; n2.value = 'e' n3.next = n6
; n0.next.next.value = 'x' n4.next = n7
; n1.next.next.value = 'y' n5.next = n8
; n2.next.next.value = 'z'
; txt.chrout(n0.value) n0.value = 'a'
; txt.chrout(n1.value) n1.value = 'b'
; txt.chrout(n2.value) n2.value = 'c'
; txt.spc() n3.value = 'd'
; txt.chrout(n0.next.next.value) n4.value = 'e'
; txt.chrout(n1.next.next.value) n5.value = 'f'
; txt.chrout(n2.next.next.value) n6.value = 'g'
; txt.spc() n7.value = 'h'
; txt.chrout(n6.value) n8.value = 'i'
; txt.chrout(n7.value)
; txt.chrout(n8.value) txt.print("struct size: ")
; txt.nl() txt.print_uw(sizeof(Node))
; txt.nl()
; txt.print("ptr to simple types: ")
; word w_value = -9999 txt.print("pointer values: ")
; txt.print_w(w_value) txt.print_uw(n0)
; txt.spc() txt.spc()
; ^^word w_ptr = &w_value txt.print_uw(n1)
; w_ptr^^ = 5555 txt.spc()
; txt.print_w(w_value) txt.print_uw(n2)
; txt.nl() txt.spc()
; txt.print_uw(n3)
; word[] @nosplit warray = [1111,2222,3333,4444,5555,6666] txt.spc()
; w_ptr = &warray txt.print_uw(n4)
; txt.print_w(w_ptr^^) txt.spc()
; txt.spc() txt.print_uw(n5)
; txt.print_w(w_ptr[4]^^) txt.spc()
; txt.nl() 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()
} }
} }

View File

@@ -48,7 +48,8 @@ class IRFileReader {
val programName = start.attributes.asSequence().single { it.name.localPart == "NAME" }.value val programName = start.attributes.asSequence().single { it.name.localPart == "NAME" }.value
val options = parseOptions(reader) val options = parseOptions(reader)
val asmsymbols = parseAsmSymbols(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 variables = parseVariables(reader)
val structsWithoutInit = parseStructInstancesNoInit(reader) val structsWithoutInit = parseStructInstancesNoInit(reader)
val structs = parseStructInstances(reader) val structs = parseStructInstances(reader)
@@ -60,7 +61,8 @@ class IRFileReader {
val st = IRSymbolTable() val st = IRSymbolTable()
asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)} 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) } variables.forEach { st.add(it) }
constants.forEach { st.add(it) } constants.forEach { st.add(it) }
memorymapped.forEach { st.add(it) } memorymapped.forEach { st.add(it) }
@@ -157,10 +159,10 @@ class IRFileReader {
} }
} }
private fun parseVarsWithoutInit(reader: XMLEventReader): List<IRStStaticVariable> { private fun parseVarsWithoutInit(segmentname: String, dirty: Boolean, reader: XMLEventReader): List<IRStStaticVariable> {
skipText(reader) skipText(reader)
val start = reader.nextEvent().asStartElement() val start = reader.nextEvent().asStartElement()
require(start.name.localPart=="VARIABLESNOINIT") { "missing VARIABLESNOINIT" } require(start.name.localPart==segmentname) { "missing $segmentname" }
val text = readText(reader).trim() val text = readText(reader).trim()
require(reader.nextEvent().isEndElement) require(reader.nextEvent().isEndElement)
@@ -171,7 +173,7 @@ class IRFileReader {
val variables = mutableListOf<IRStStaticVariable>() val variables = mutableListOf<IRStStaticVariable>()
text.lineSequence().forEach { line -> text.lineSequence().forEach { line ->
// example: uword main.start.qq2 zp=DONTCARE // 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 type = match.groups["type"]!!.value
val arrayspec = match.groups["arrayspec"]?.value ?: "" val arrayspec = match.groups["arrayspec"]?.value ?: ""
val name = match.groups["name"]!!.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 zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
// val isSplit = if(split.isBlank()) false else split.toBoolean() // val isSplit = if(split.isBlank()) false else split.toBoolean()
val align = if(alignment.isBlank()) 0u else alignment.toUInt() 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) variables.add(newVar)
} }
return variables return variables
@@ -250,6 +252,7 @@ class IRFileReader {
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish) val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
if(split.isBlank()) false else split.toBoolean() if(split.isBlank()) false else split.toBoolean()
val align = if(alignment.isBlank()) 0u else alignment.toUInt() val align = if(alignment.isBlank()) 0u else alignment.toUInt()
val dirty = false // these variables have initialization values.
var initNumeric: Double? = null var initNumeric: Double? = null
var initArray: IRStArray? = null var initArray: IRStArray? = null
when { when {
@@ -274,7 +277,7 @@ class IRFileReader {
if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) { if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) {
initArray=null // arrays with just zeros can be left uninitialized 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) variables.add(stVar)
} }
return variables return variables

View File

@@ -330,19 +330,25 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
} }
} }
fun writeNoInitVars(segmentname: String, variables: List<IRStStaticVariable>) {
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 } val (variablesNoInit, variablesWithInit) = irProgram.st.allVariables().partition { it.uninitialized }
xml.writeStartElement("VARIABLESNOINIT") val (dirtyvars, cleanvars) = variablesNoInit.partition { it.dirty }
xml.writeCharacters("\n") writeNoInitVars("VARIABLESNOINIT", cleanvars)
val (noinitNotAligned, noinitAligned) = variablesNoInit.partition { it.align==0u || it.align==1u } writeNoInitVars("VARIABLESNOINITDIRTY", dirtyvars)
for (variable in noinitNotAligned) {
writeNoInitVar(variable)
}
for (variable in noinitAligned.sortedBy { it.align }) {
writeNoInitVar(variable)
}
xml.writeEndElement()
xml.writeCharacters("\n")
xml.writeStartElement("VARIABLESWITHINIT") xml.writeStartElement("VARIABLESWITHINIT")
xml.writeCharacters("\n") xml.writeCharacters("\n")

View File

@@ -1,10 +1,10 @@
package prog8.intermediate package prog8.intermediate
import prog8.code.INTERNED_STRINGS_MODULENAME import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.code.core.BaseDataType
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Encoding import prog8.code.core.Encoding
import prog8.code.core.ZeropageWish import prog8.code.core.ZeropageWish
import prog8.code.core.BaseDataType
// In the Intermediate Representation, all nesting has been removed. // In the Intermediate Representation, all nesting has been removed.
@@ -101,7 +101,8 @@ class IRStStaticVariable(name: String,
val onetimeInitializationArrayValue: IRStArray?, val onetimeInitializationArrayValue: IRStArray?,
val length: UInt?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte 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 zpwish: ZeropageWish, // used in the variable allocator
val align: UInt val align: UInt,
val dirty: Boolean
) : IRStNode(name, IRStNodeType.STATICVAR) { ) : IRStNode(name, IRStNodeType.STATICVAR) {
init { init {
if(align > 0u) { if(align > 0u) {

View File

@@ -55,6 +55,8 @@ loadAddress=$0000
<VARIABLESNOINIT> <VARIABLESNOINIT>
uword sys.bssvar zp=DONTCARE align=0 uword sys.bssvar zp=DONTCARE align=0
</VARIABLESNOINIT> </VARIABLESNOINIT>
<VARIABLESNOINITDIRTY>
</VARIABLESNOINITDIRTY>
<VARIABLESWITHINIT> <VARIABLESWITHINIT>
uword sys.wait.jiffies=10 zp=DONTCARE align=0 uword sys.wait.jiffies=10 zp=DONTCARE align=0
ubyte[3] sys.emptystring=0,0,0 zp=DONTCARE align=0 ubyte[3] sys.emptystring=0,0,0 zp=DONTCARE align=0

View File

@@ -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 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 zpwish: ZeropageWish, // used in the variable allocator
val align: UInt, val align: UInt,
val dirty: Boolean,
astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) { astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) {
var initializationNumericValue: Double? = null var initializationNumericValue: Double? = null

View File

@@ -106,7 +106,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
// if(node.type in SplitWordArrayTypes) { // if(node.type in SplitWordArrayTypes) {
// ... split array also add _lsb and _msb to symboltable? // ... 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) if(initialNumeric!=null)
stVar.setOnetimeInitNumeric(initialNumeric) stVar.setOnetimeInitNumeric(initialNumeric)
stVar stVar

View File

@@ -198,6 +198,7 @@ class PtVariable(
override val type: DataType, override val type: DataType,
val zeropage: ZeropageWish, val zeropage: ZeropageWish,
val align: UInt, val align: UInt,
val dirty: Boolean,
val value: PtExpression?, val value: PtExpression?,
val arraySize: UInt?, val arraySize: UInt?,
position: Position position: Position
@@ -215,6 +216,8 @@ class PtVariable(
// The IR codegen however is different it has a special section <VARIABLESWITHINIT> for all variables // The IR codegen however is different it has a special section <VARIABLESWITHINIT> for all variables
// that have a non-zero initialization value, regardless of the datatype. It removes the initialization // 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). // 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} value?.let {it.parent=this}

View File

@@ -196,10 +196,10 @@ class VmProgramLoader {
program.st.allVariables().forEach { variable -> program.st.allVariables().forEach { variable ->
var addr = allocations.allocations.getValue(variable.name) var addr = allocations.allocations.getValue(variable.name)
// zero out uninitialized ('bss') variables. // zero out uninitialized non-dirty ('bss') variables.
if(variable.uninitialized) { if(variable.uninitialized && !variable.dirty) {
val dt = variable.dt val dt = variable.dt
if(dt.isArray) { if(variable.dt.isArray) {
repeat(variable.length!!.toInt()) { repeat(variable.length!!.toInt()) {
when { when {
dt.isPointerArray -> { dt.isPointerArray -> {

View File

@@ -103,6 +103,8 @@ class TestVm: FunSpec( {
<VARIABLESNOINIT> <VARIABLESNOINIT>
</VARIABLESNOINIT> </VARIABLESNOINIT>
<VARIABLESNOINITDIRTY>
</VARIABLESNOINITDIRTY>
<VARIABLESWITHINIT> <VARIABLESWITHINIT>
</VARIABLESWITHINIT> </VARIABLESWITHINIT>