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
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 {
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> {
// TODO share counter variables between subroutines or even between repeat loops as long as they're not nested
val counterVar = makeLabel("repeatcounter")
val zpAvail = if(dt==DataType.UWORD) 2 else 1
return if(constIterations!=null && constIterations>=16 && zeropage.available() >= zpAvail) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, dt, stmt.position, errors)
out("$counterVar = $zpAddr ; auto zp $dt")
Pair(counterVar, false)
} else {
Pair(counterVar, true)
// println("REPEATCOUNTERVAR $counterVar SIZE $dt CTX ${stmt.definingSubroutine()}")
when(dt) {
DataType.UBYTE -> {
return if(constIterations!=null && constIterations>=16 && zeropage.hasByteAvailable()) {
// allocate count var on ZP
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
beq $endLabel""")
}
if(length>=16 && asmgen.zeropage.available() > 0) {
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
// allocate index var on ZP
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
@ -327,7 +327,7 @@ $loopLabel sty $indexVar
bne $loopLabel
beq $endLabel""")
}
if(length>=16 && asmgen.zeropage.available() > 0) {
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
// allocate index var on ZP
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")

View File

@ -179,28 +179,48 @@ class TestC64Zeropage {
fun testZpDontuse() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
println(zp.free)
assertEquals(0, zp.available())
assertEquals(0, zp.availableBytes())
assertFailsWith<CompilerException> {
zp.allocate("", DataType.BYTE, null, errors)
}
}
@Test
fun testFreeSpaces() {
fun testFreeSpacesBytes() {
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))
assertEquals(85, zp2.available())
assertEquals(85, zp2.availableBytes())
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))
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
fun testReservedSpace() {
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(100 in zp1.free)
assertTrue(49 in zp1.free)
@ -209,7 +229,7 @@ class TestC64Zeropage {
assertTrue(255 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))
assertEquals(139, zp2.available())
assertEquals(139, zp2.availableBytes())
assertFalse(50 in zp2.free)
assertFalse(100 in zp2.free)
assertTrue(49 in zp2.free)
@ -222,18 +242,22 @@ class TestC64Zeropage {
@Test
fun testBasicsafeAllocation() {
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> {
// in regular zp there aren't 5 sequential bytes free
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)
assertTrue(loc > 0)
}
assertEquals(0, zp.available())
assertEquals(0, zp.availableBytes())
assertFalse(zp.hasByteAvailable())
assertFalse(zp.hasWordAvailable())
assertFailsWith<ZeropageDepletedError> {
zp.allocate("", DataType.UBYTE, null, errors)
}
@ -245,16 +269,18 @@ class TestC64Zeropage {
@Test
fun testFullAllocation() {
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)
assertTrue(loc > 3)
assertFalse(loc in zp.free)
val num = zp.available() / 2
val num = zp.availableBytes() / 2
for(i in 0..num-4) {
zp.allocate("", DataType.UWORD, null, errors)
}
assertEquals(6,zp.available())
assertEquals(6,zp.availableBytes())
assertFailsWith<ZeropageDepletedError> {
// can't allocate because no more sequential bytes, only fragmented
@ -265,7 +291,9 @@ class TestC64Zeropage {
zp.allocate("", DataType.UBYTE, null, errors)
}
assertEquals(0, zp.available())
assertEquals(0, zp.availableBytes())
assertFalse(zp.hasByteAvailable())
assertFalse(zp.hasWordAvailable())
assertFailsWith<ZeropageDepletedError> {
// no more space
zp.allocate("", DataType.UBYTE, null, errors)
@ -275,7 +303,7 @@ class TestC64Zeropage {
@Test
fun testEfficientAllocation() {
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(0x06, 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(0x96, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0, zp.available())
assertEquals(0, zp.availableBytes())
}
@Test
@ -301,6 +329,8 @@ class TestC64Zeropage {
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCx16Zeropage {
private val errors = ErrorReporter()
@Test
fun testReservedLocations() {
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
@ -308,19 +338,37 @@ class TestCx16Zeropage {
}
@Test
fun testFreeSpaces() {
fun testFreeSpacesBytes() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
assertEquals(88, zp1.available())
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
assertEquals(175, zp3.available())
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
assertEquals(216, zp4.available())
assertEquals(88, zp1.availableBytes())
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
assertEquals(175, zp2.availableBytes())
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
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
fun testReservedSpace() {
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(0x80 in zp1.free)
assertTrue(0xff in zp1.free)

View File

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