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")
}
}
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
}

View File

@@ -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<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it)
val (dirty, clean) = varsNoInit.partition { it.dirty }
fun generate(section: String, variables: List<StStaticVariable>) {
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()) {

View File

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

View File

@@ -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<StArrayElement>?): List<IRStArrayElement>? {
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
)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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()
}
}

View File

@@ -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<IRStStaticVariable> {
private fun parseVarsWithoutInit(segmentname: String, dirty: Boolean, reader: XMLEventReader): List<IRStStaticVariable> {
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<IRStStaticVariable>()
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

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 }
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")

View File

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

View File

@@ -55,6 +55,8 @@ loadAddress=$0000
<VARIABLESNOINIT>
uword sys.bssvar zp=DONTCARE align=0
</VARIABLESNOINIT>
<VARIABLESNOINITDIRTY>
</VARIABLESNOINITDIRTY>
<VARIABLESWITHINIT>
uword sys.wait.jiffies=10 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 zpwish: ZeropageWish, // used in the variable allocator
val align: UInt,
val dirty: Boolean,
astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) {
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) {
// ... 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

View File

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

View File

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

View File

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