added free words counting method to zeropage

This commit is contained in:
Irmen de Jong 2021-05-30 00:25:47 +02:00
parent 572bb38ddb
commit c03f6604af
5 changed files with 199 additions and 76 deletions

View File

@ -19,7 +19,29 @@ abstract class Zeropage(protected val options: CompilationOptions) {
val allowedDatatypes = NumericDatatypes val allowedDatatypes = NumericDatatypes
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size fun availableBytes() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
fun hasByteAvailable() = if(options.zeropage==ZeropageType.DONTUSE) false else free.isNotEmpty()
fun availableWords(): Int {
if(options.zeropage==ZeropageType.DONTUSE)
return 0
val words = free.windowed(2).filter { it[0] == it[1]-1 }
var nonOverlappingWordsCount = 0
var prevMsbLoc = -1
for(w in words) {
if(w[0]!=prevMsbLoc) {
nonOverlappingWordsCount++
prevMsbLoc = w[1]
}
}
return nonOverlappingWordsCount
}
fun hasWordAvailable(): Boolean {
if(options.zeropage==ZeropageType.DONTUSE)
return false
return free.windowed(2).any { it[0] == it[1] - 1 }
}
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: IErrorReporter): Int { fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: IErrorReporter): Int {
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"} assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}

View File

@ -1070,14 +1070,31 @@ $repeatLabel lda $counterVar
private fun createRepeatCounterVar(dt: DataType, constIterations: Int?, stmt: RepeatLoop): Pair<String, Boolean> { private fun createRepeatCounterVar(dt: DataType, constIterations: Int?, stmt: RepeatLoop): Pair<String, Boolean> {
// TODO share counter variables between subroutines or even between repeat loops as long as they're not nested // TODO share counter variables between subroutines or even between repeat loops as long as they're not nested
val counterVar = makeLabel("repeatcounter") val counterVar = makeLabel("repeatcounter")
val zpAvail = if(dt==DataType.UWORD) 2 else 1
return if(constIterations!=null && constIterations>=16 && zeropage.available() >= zpAvail) { // println("REPEATCOUNTERVAR $counterVar SIZE $dt CTX ${stmt.definingSubroutine()}")
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, dt, stmt.position, errors) when(dt) {
out("$counterVar = $zpAddr ; auto zp $dt") DataType.UBYTE -> {
Pair(counterVar, false) return if(constIterations!=null && constIterations>=16 && zeropage.hasByteAvailable()) {
} else { // allocate count var on ZP
Pair(counterVar, true) val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, stmt.position, errors)
out("$counterVar = $zpAddr ; auto zp byte")
Pair(counterVar, false)
} else {
Pair(counterVar, true)
}
}
DataType.UWORD -> {
return if(constIterations!=null && constIterations>=16 && zeropage.hasWordAvailable()) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, stmt.position, errors)
out("$counterVar = $zpAddr ; auto zp word")
Pair(counterVar, false)
} else {
Pair(counterVar, true)
}
}
else -> throw AssemblyError("invalidt dt")
} }
} }

View File

@ -288,7 +288,7 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(length>=16 && asmgen.zeropage.available() > 0) { if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
// allocate index var on ZP // allocate index var on ZP
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors) val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""") asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
@ -327,7 +327,7 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(length>=16 && asmgen.zeropage.available() > 0) { if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
// allocate index var on ZP // allocate index var on ZP
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors) val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""") asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")

View File

@ -179,28 +179,48 @@ class TestC64Zeropage {
fun testZpDontuse() { fun testZpDontuse() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
println(zp.free) println(zp.free)
assertEquals(0, zp.available()) assertEquals(0, zp.availableBytes())
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
zp.allocate("", DataType.BYTE, null, errors) zp.allocate("", DataType.BYTE, null, errors)
} }
} }
@Test @Test
fun testFreeSpaces() { fun testFreeSpacesBytes() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target)) val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
assertEquals(18, zp1.available()) assertEquals(18, zp1.availableBytes())
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
assertEquals(85, zp2.available()) assertEquals(85, zp2.availableBytes())
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target)) val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
assertEquals(125, zp3.available()) assertEquals(125, zp3.availableBytes())
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target)) val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertEquals(238, zp4.available()) assertEquals(238, zp4.availableBytes())
zp4.allocate("test", DataType.UBYTE, null, errors)
assertEquals(237, zp4.availableBytes())
zp4.allocate("test2", DataType.UBYTE, null, errors)
assertEquals(236, zp4.availableBytes())
}
@Test
fun testFreeSpacesWords() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
assertEquals(6, zp1.availableWords())
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
assertEquals(38, zp2.availableWords())
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
assertEquals(57, zp3.availableWords())
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertEquals(116, zp4.availableWords())
zp4.allocate("test", DataType.UWORD, null, errors)
assertEquals(115, zp4.availableWords())
zp4.allocate("test2", DataType.UWORD, null, errors)
assertEquals(114, zp4.availableWords())
} }
@Test @Test
fun testReservedSpace() { fun testReservedSpace() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target)) val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertEquals(238, zp1.available()) assertEquals(238, zp1.availableBytes())
assertTrue(50 in zp1.free) assertTrue(50 in zp1.free)
assertTrue(100 in zp1.free) assertTrue(100 in zp1.free)
assertTrue(49 in zp1.free) assertTrue(49 in zp1.free)
@ -209,7 +229,7 @@ class TestC64Zeropage {
assertTrue(255 in zp1.free) assertTrue(255 in zp1.free)
assertTrue(199 in zp1.free) assertTrue(199 in zp1.free)
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target))
assertEquals(139, zp2.available()) assertEquals(139, zp2.availableBytes())
assertFalse(50 in zp2.free) assertFalse(50 in zp2.free)
assertFalse(100 in zp2.free) assertFalse(100 in zp2.free)
assertTrue(49 in zp2.free) assertTrue(49 in zp2.free)
@ -222,18 +242,22 @@ class TestC64Zeropage {
@Test @Test
fun testBasicsafeAllocation() { fun testBasicsafeAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
assertEquals(18, zp.available()) assertEquals(18, zp.availableBytes())
assertTrue(zp.hasByteAvailable())
assertTrue(zp.hasWordAvailable())
assertFailsWith<ZeropageDepletedError> { assertFailsWith<ZeropageDepletedError> {
// in regular zp there aren't 5 sequential bytes free // in regular zp there aren't 5 sequential bytes free
zp.allocate("", DataType.FLOAT, null, errors) zp.allocate("", DataType.FLOAT, null, errors)
} }
for (i in 0 until zp.available()) { for (i in 0 until zp.availableBytes()) {
val loc = zp.allocate("", DataType.UBYTE, null, errors) val loc = zp.allocate("", DataType.UBYTE, null, errors)
assertTrue(loc > 0) assertTrue(loc > 0)
} }
assertEquals(0, zp.available()) assertEquals(0, zp.availableBytes())
assertFalse(zp.hasByteAvailable())
assertFalse(zp.hasWordAvailable())
assertFailsWith<ZeropageDepletedError> { assertFailsWith<ZeropageDepletedError> {
zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors)
} }
@ -245,16 +269,18 @@ class TestC64Zeropage {
@Test @Test
fun testFullAllocation() { fun testFullAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertEquals(238, zp.available()) assertEquals(238, zp.availableBytes())
assertTrue(zp.hasByteAvailable())
assertTrue(zp.hasWordAvailable())
val loc = zp.allocate("", DataType.UWORD, null, errors) val loc = zp.allocate("", DataType.UWORD, null, errors)
assertTrue(loc > 3) assertTrue(loc > 3)
assertFalse(loc in zp.free) assertFalse(loc in zp.free)
val num = zp.available() / 2 val num = zp.availableBytes() / 2
for(i in 0..num-4) { for(i in 0..num-4) {
zp.allocate("", DataType.UWORD, null, errors) zp.allocate("", DataType.UWORD, null, errors)
} }
assertEquals(6,zp.available()) assertEquals(6,zp.availableBytes())
assertFailsWith<ZeropageDepletedError> { assertFailsWith<ZeropageDepletedError> {
// can't allocate because no more sequential bytes, only fragmented // can't allocate because no more sequential bytes, only fragmented
@ -265,7 +291,9 @@ class TestC64Zeropage {
zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors)
} }
assertEquals(0, zp.available()) assertEquals(0, zp.availableBytes())
assertFalse(zp.hasByteAvailable())
assertFalse(zp.hasWordAvailable())
assertFailsWith<ZeropageDepletedError> { assertFailsWith<ZeropageDepletedError> {
// no more space // no more space
zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors)
@ -275,7 +303,7 @@ class TestC64Zeropage {
@Test @Test
fun testEfficientAllocation() { fun testEfficientAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
assertEquals(18, zp.available()) assertEquals(18, zp.availableBytes())
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors)) assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors))
@ -288,7 +316,7 @@ class TestC64Zeropage {
assertEquals(0x92, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x92, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0x96, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x96, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0, zp.available()) assertEquals(0, zp.availableBytes())
} }
@Test @Test
@ -301,6 +329,8 @@ class TestC64Zeropage {
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCx16Zeropage { class TestCx16Zeropage {
private val errors = ErrorReporter()
@Test @Test
fun testReservedLocations() { fun testReservedLocations() {
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target)) val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
@ -308,19 +338,37 @@ class TestCx16Zeropage {
} }
@Test @Test
fun testFreeSpaces() { fun testFreeSpacesBytes() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target)) val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
assertEquals(88, zp1.available()) assertEquals(88, zp1.availableBytes())
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target)) val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
assertEquals(175, zp3.available()) assertEquals(175, zp2.availableBytes())
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target)) val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
assertEquals(216, zp4.available()) assertEquals(216, zp3.availableBytes())
zp3.allocate("test", DataType.UBYTE, null, errors)
assertEquals(215, zp3.availableBytes())
zp3.allocate("test2", DataType.UBYTE, null, errors)
assertEquals(214, zp3.availableBytes())
}
@Test
fun testFreeSpacesWords() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
assertEquals(108, zp1.availableWords())
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
assertEquals(87, zp2.availableWords())
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
assertEquals(44, zp3.availableWords())
zp3.allocate("test", DataType.UWORD, null, errors)
assertEquals(43, zp3.availableWords())
zp3.allocate("test2", DataType.UWORD, null, errors)
assertEquals(42, zp3.availableWords())
} }
@Test @Test
fun testReservedSpace() { fun testReservedSpace() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target)) val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
assertEquals(216, zp1.available()) assertEquals(216, zp1.availableBytes())
assertTrue(0x22 in zp1.free) assertTrue(0x22 in zp1.free)
assertTrue(0x80 in zp1.free) assertTrue(0x80 in zp1.free)
assertTrue(0xff in zp1.free) assertTrue(0xff in zp1.free)

View File

@ -1,11 +1,16 @@
%import textio ; txt.* %import textio ; txt.*
%zeropage kernalsafe %zeropage basicsafe
main { main {
; test program for the optimization of repeat var allocation (asmgen.createRepeatCounterVar)
; output must be: 60 6164 6224 12328
uword xx
sub start() { sub start() {
ubyte xx xx=0
repeat 10 { repeat 10 {
xx++ xx++
@ -19,55 +24,86 @@ main {
repeat 10 { repeat 10 {
xx++ xx++
} }
repeat 5 {
repeat 4 {
xx++
}
}
txt.print_uw(xx)
txt.nl()
repeat 1000 {
xx++
}
repeat 1000 {
xx++
}
repeat 1000 {
xx++
}
repeat 260 {
repeat 4 {
xx++
}
}
repeat 260 {
repeat 260 {
xx++
}
}
txt.print_uw(xx)
txt.nl()
sub2()
if xx!=12328
txt.print("\n!fail!\n")
else
txt.print("\nok\n")
}
sub sub2() {
repeat 10 { repeat 10 {
xx++ xx++
} }
repeat 1000 { repeat 10 {
xx++ xx++
} }
repeat 1000 { repeat 10 {
xx++ xx++
} }
repeat 1000 { repeat 10 {
xx++ xx++
} }
repeat 1000 { repeat 5 {
xx++ repeat 4 {
} xx++
repeat 1000 { }
xx++
} }
txt.print_uw(xx)
txt.nl()
str string1 = "stringvalue\n" repeat 1000 {
str string2 = "stringvalue\n" xx++
str string3 = "stringvalue\n" }
str string4 = "a" repeat 1000 {
str string5 = "bb" xx++
}
txt.print(string1) repeat 1000 {
txt.print(string2) xx++
txt.print(string3) }
string1[1]='?' repeat 260 {
string2[2] = '?' repeat 4 {
string3[3] = '?' xx++
txt.print(string1) }
txt.print(string2) }
txt.print(string3) repeat 260 {
repeat 260 {
txt.print("a") xx++
txt.print("a") }
txt.print(string4) }
txt.print("bb") txt.print_uw(xx)
txt.print("bb") txt.nl()
txt.print(string5)
txt.print("\n")
txt.print("\n\n")
txt.print("hello\n")
txt.print("hello\n")
txt.print("hello\n")
txt.print("bye\n")
txt.print("bye\n")
txt.print("bye\n")
} }
} }