vm: memory is randomized on start instead of 0. P8ir file now has BSS segment. Vm clears BSS vars to 0.

This commit is contained in:
Irmen de Jong 2022-12-03 17:46:06 +01:00
parent 4831fad27a
commit 17bedac96c
11 changed files with 149 additions and 97 deletions

View File

@ -8,10 +8,6 @@ import prog8.code.core.*
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
*/
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
fun print() = printIndented(0)
override fun printProperties() { }
/**
* The table as a flat mapping of scoped names to the StNode.
* This gives the fastest lookup possible (no need to traverse tree nodes)
@ -138,29 +134,6 @@ open class StNode(val name: String,
}
}
fun printIndented(indent: Int) {
print(" ".repeat(indent))
when(type) {
StNodeType.GLOBAL -> print("SYMBOL-TABLE:")
StNodeType.BLOCK -> print("(B) ")
StNodeType.SUBROUTINE -> print("(S) ")
StNodeType.LABEL -> print("(L) ")
StNodeType.STATICVAR -> print("(V) ")
StNodeType.MEMVAR -> print("(M) ")
StNodeType.MEMORYSLAB -> print("(MS) ")
StNodeType.CONSTANT -> print("(C) ")
StNodeType.BUILTINFUNC -> print("(F) ")
StNodeType.ROMSUB -> print("(R) ")
}
printProperties()
println()
children.forEach { (_, node) -> node.printIndented(indent+1) }
}
open fun printProperties() {
print("$name ")
}
fun add(child: StNode) {
children[child.name] = child
child.parent = this
@ -201,18 +174,11 @@ class StStaticVariable(name: String,
require(length == onetimeInitializationStringValue.first.length+1)
}
}
override fun printProperties() {
print("$name dt=$dt zpw=$zpwish bss=$bss")
}
}
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
StNode(name, StNodeType.CONSTANT, position) {
override fun printProperties() {
print("$name dt=$dt value=$value")
}
}
@ -222,9 +188,6 @@ class StMemVar(name: String,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
position: Position) :
StNode(name, StNodeType.MEMVAR, position) {
override fun printProperties() {
print("$name dt=$dt address=${address.toHex()}")
}
}
class StMemorySlab(
@ -234,16 +197,10 @@ class StMemorySlab(
position: Position
):
StNode(name, StNodeType.MEMORYSLAB, position) {
override fun printProperties() {
print("$name size=$size align=$align")
}
}
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, position: Position) :
StNode(name, StNodeType.SUBROUTINE, position) {
override fun printProperties() {
print(name)
}
}
@ -253,9 +210,6 @@ class StRomSub(name: String,
val returns: List<RegisterOrStatusflag>,
position: Position) :
StNode(name, StNodeType.ROMSUB, position) {
override fun printProperties() {
print("$name address=${address.toHex()}")
}
}

View File

@ -19,6 +19,8 @@ class TestLaunchEmu: FunSpec({
<ASMSYMBOLS>
</ASMSYMBOLS>
<BSS>
</BSS>
<VARIABLES>
</VARIABLES>

View File

@ -3,15 +3,10 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- Think this through/ ask opinions: add a mechanism to allocate variables into golden ram (see GoldenRam class)
- block "golden" treated specially: every var in here will be allocated in the Golden ram area
- that block can only contain variables.
- the variables can NOT have initialization values, they will all be set to zero on startup (simple memset)
- just initialize them yourself in start() if you need a non-zero value
- OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram
- (need separate step in codegen and IR to write the "golden" variables)
- add more descriptions to require() calls (at least line number?)
- 6502 codegen: create BSS section in output assembly code and put StStaticVariables in there with bss=true.
Don't forget to add init code to zero out everything that was put in bss. If array in bss->only zero ONCE if possible.
Note that bss can still contain variables that have @zp tag and those are already dealt with differently
- regression test the various projects before release
...
@ -28,7 +23,6 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- create BSS section in output program and put StStaticVariables in there with bss=true. Don't forget to add init code to zero out everything that was put in bss. If array in bss->only zero ONCE! So requires self-modifying code
- ir: mechanism to determine for chunks which registers are getting input values from "outside"
- ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk)
- ir: peephole opt: renumber registers in chunks to start with 1 again every time (but keep entry values in mind!)
@ -48,6 +42,14 @@ Compiler:
Once new codegen is written that is based on the IR, this point is moot anyway as that will have its own dead code removal.
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that)
- add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
- block "golden" treated specially: every var in here will be allocated in the Golden ram area
- that block can only contain variables.
- the variables can NOT have initialization values, they will all be set to zero on startup (simple memset)
- just initialize them yourself in start() if you need a non-zero value
- OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram
- OR.... make all this more generic and use some %segment option to create real segments for 64tass?
- (need separate step in codegen and IR to write the "golden" variables)
Libraries:

View File

@ -1,37 +1,54 @@
%import textio
%import cx16logo
nop {
sub lda(ubyte sec) -> ubyte {
asl:
ubyte brk = sec
sec++
brk += sec
return brk
}
}
%zeropage basicsafe
%option no_sysinit
main {
sub ffalse(ubyte arg) -> ubyte {
arg++
return 0
}
sub ftrue(ubyte arg) -> ubyte {
arg++
return 128
}
sub start() {
ubyte col = 10
ubyte row = 20
cx16logo.logo_at(col, row)
txt.setcc(10, 10, 2, 3)
txt.print_ub(nop.lda(42))
txt.nl()
txt.print_uw(nop.lda.asl)
; TODO ALSO TEST AS GLOBALS
ubyte @requirezp zpvar = 10
ubyte @zp zpvar2 = 20
uword empty
ubyte[10] bssarray
uword[10] bsswordarray
ubyte[10] nonbssarray = 99
str name="irmen"
void ffalse(99)
void ftrue(99)
txt.print("10 ")
txt.print_ub(zpvar)
txt.nl()
zpvar++
txt.print("20 ")
txt.print_ub(zpvar2)
txt.nl()
zpvar2++
txt.print("0 ")
txt.print_uw(empty)
txt.nl()
empty++
txt.print("0 ")
txt.print_ub(bssarray[1])
txt.nl()
bssarray[1]++
txt.print("0 ")
txt.print_uw(bsswordarray[1])
txt.nl()
bsswordarray[1]++
txt.print("99 ")
txt.print_ub(nonbssarray[1])
txt.nl()
nonbssarray[1]++
txt.print("r ")
txt.chrout(name[1])
txt.nl()
name[1] = (name[1] as ubyte +1)
txt.print("try running again.\n")
}
}

View File

@ -44,6 +44,7 @@ class IRFileReader {
val programName = start.attributes.asSequence().single { it.name.localPart == "NAME" }.value
val options = parseOptions(reader)
val asmsymbols = parseAsmSymbols(reader)
val bss = parseBss(reader)
val variables = parseVariables(reader, options.dontReinitGlobals)
val memorymapped = parseMemMapped(reader)
val slabs = parseSlabs(reader)
@ -52,6 +53,7 @@ class IRFileReader {
val st = IRSymbolTable(null)
asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)}
bss.forEach { st.add(it) }
variables.forEach { st.add(it) }
memorymapped.forEach { st.add(it) }
slabs.forEach { st.add(it) }
@ -147,6 +149,33 @@ class IRFileReader {
}
}
private fun parseBss(reader: XMLEventReader): List<StStaticVariable> {
skipText(reader)
val start = reader.nextEvent().asStartElement()
require(start.name.localPart=="BSS") { "missing BSS" }
val text = readText(reader).trim()
require(reader.nextEvent().isEndElement)
return if(text.isBlank())
emptyList()
else {
val varPattern = Regex("(.+?)(\\[.+?\\])? (.+) (zp=(.+))?")
val bssVariables = mutableListOf<StStaticVariable>()
text.lineSequence().forEach { line ->
// example: uword main.start.qq2 zp=DONTCARE
val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid BSS $line")
val (type, arrayspec, name, _, zpwish) = match.destructured
if('.' !in name)
throw IRParseException("unscoped varname: $name")
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt: DataType = parseDatatype(type, arraysize!=null)
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
bssVariables.add(StStaticVariable(name, dt, true, null, null, null, arraysize, zp, Position.DUMMY))
}
return bssVariables
}
}
private fun parseVariables(reader: XMLEventReader, dontReinitGlobals: Boolean): List<StStaticVariable> {
skipText(reader)
val start = reader.nextEvent().asStartElement()
@ -165,6 +194,8 @@ class IRFileReader {
// ubyte[6] main.start.namestring=105,114,109,101,110,0
val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLE $line")
val (type, arrayspec, name, value, _, zpwish) = match.destructured
if('.' !in name)
throw IRParseException("unscoped varname: $name")
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt: DataType = parseDatatype(type, arraysize!=null)
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
@ -199,7 +230,7 @@ class IRFileReader {
DataType.STR -> throw IRParseException("STR should have been converted to byte array")
else -> throw IRParseException("weird dt")
}
require(!bss) { "bss var should be in BSS section" }
variables.add(StStaticVariable(name, dt, bss, initNumeric, null, initArray, arraysize, zp, Position.DUMMY))
}
return variables

View File

@ -19,7 +19,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
out.write("<PROGRAM NAME=\"${irProgram.name}\">\n")
writeOptions()
writeAsmSymbols()
writeVariableAllocations()
writeVariables()
out.write("\n<INITGLOBALS>\n")
if(!irProgram.options.dontReinitGlobals) {
@ -137,10 +137,17 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
out.write("</OPTIONS>\n")
}
private fun writeVariableAllocations() {
private fun writeVariables() {
out.write("\n<VARIABLES>\n")
for (variable in irProgram.st.allVariables()) {
out.write("\n<BSS>\n")
for (variable in irProgram.st.allVariables().filter { it.bss }) {
val typeStr = getTypeString(variable)
// bss variables have no initialization value
out.write("$typeStr ${variable.name} zp=${variable.zpwish}\n")
}
out.write("</BSS>\n<VARIABLES>\n")
for (variable in irProgram.st.allVariables().filter { !it.bss }) {
val typeStr = getTypeString(variable)
val value: String = when(variable.dt) {
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: "").toString()

View File

@ -49,15 +49,18 @@ output=PRG
launcher=BASIC
zeropage=KERNALSAFE
loadAddress=0
dontReinitGlobals=false
dontReinitGlobals=true
evalStackBaseAddress=null
</OPTIONS>
<ASMSYMBOLS>
</ASMSYMBOLS>
<BSS>
uword sys.bssvar zp=DONTCARE
</BSS>
<VARIABLES>
uword sys.wait.jiffies= zp=DONTCARE
uword sys.wait.jiffies=10 zp=DONTCARE
</VARIABLES>
<MEMORYMAPPEDVARIABLES>
@ -105,7 +108,10 @@ return
tempfile.deleteExisting()
program.name shouldBe "test-ir-reader"
program.blocks.size shouldBe 2
program.st.allVariables().count() shouldBe 1
program.st.lookup("sys.wait.jiffies") shouldBe instanceOf<StStaticVariable>()
program.st.allVariables().count() shouldBe 2
val var1 = program.st.lookup("sys.wait.jiffies") as StStaticVariable
val var2 = program.st.lookup("sys.bssvar") as StStaticVariable
var1.bss shouldBe false
var2.bss shouldBe true
}
})

View File

@ -1,10 +1,12 @@
package prog8.vm
import kotlin.random.Random
/**
* 64 Kb of random access memory.
* 64 Kb of random access memory. Initialized to random values.
*/
class Memory {
private val mem = Array<UByte>(64 * 1024) { 0u }
private val mem = Array<UByte>(64 * 1024) { Random.nextInt().toUByte() }
fun reset() {
mem.fill(0u)

View File

@ -1,5 +1,6 @@
package prog8.vm
import prog8.code.core.ArrayDatatypes
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.intermediate.*
@ -183,6 +184,29 @@ class VmProgramLoader {
) {
program.st.allVariables().forEach { variable ->
var addr = allocations.allocations.getValue(variable.name)
if(variable.bss && variable.dt in ArrayDatatypes) {
// zero out the array bss variable.
// non-array variables are reset using explicit assignment instructions.
repeat(variable.length!!) {
when(variable.dt) {
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
memory.setUB(addr, 0u)
addr++
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
memory.setUW(addr, 0u)
addr += 2
}
DataType.ARRAY_F -> {
memory.setFloat(addr, 0.0f)
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
}
else -> throw IRParseException("invalid dt")
}
}
}
variable.onetimeInitializationNumericValue?.let {
when(variable.dt) {
DataType.UBYTE -> memory.setUB(addr, it.toInt().toUByte())

View File

@ -1,6 +1,7 @@
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.vm.Memory
@ -46,7 +47,9 @@ class TestMemory: FunSpec({
test("32 bits float access") {
val mem = Memory()
mem.getFloat(1000) shouldBe 0.0
mem.getFloat(1000) shouldNotBe 0.0
mem.setFloat(1000, 0.0f)
mem.getFloat(1000) shouldBe 0.0f
mem.setFloat(1000, -9.876543f)
mem.getFloat(1000) shouldBe -9.876543f
}

View File

@ -2,6 +2,7 @@ import io.kotest.assertions.throwables.shouldThrowWithMessage
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.intermediate.*
@ -51,6 +52,7 @@ class TestVm: FunSpec( {
block += startSub
program.addBlock(block)
val vm = VirtualMachine(program)
vm.memory.setUW(1000, 0u)
vm.memory.getUW(1000) shouldBe 0u
vm.callStack.shouldBeEmpty()
@ -128,6 +130,8 @@ class TestVm: FunSpec( {
<ASMSYMBOLS>
</ASMSYMBOLS>
<BSS>
</BSS>
<VARIABLES>
</VARIABLES>