prog8/compiler/test/ZeropageTests.kt
2021-11-21 00:07:17 +01:00

309 lines
14 KiB
Kotlin

package prog8tests
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldBeIn
import io.kotest.matchers.collections.shouldNotBeIn
import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import prog8.ast.base.DataType
import prog8.compiler.target.C64Target
import prog8.compiler.target.Cx16Target
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
import prog8.compilerinterface.*
import prog8tests.helpers.ErrorReporterForTests
class TestAbstractZeropage: FunSpec({
class DummyCompilationTarget: ICompilationTarget {
override val name: String = "dummy"
override val machine: IMachineDefinition
get() = throw NotImplementedError("dummy")
override fun encodeString(str: String, altEncoding: Boolean): List<UByte> {
throw NotImplementedError("dummy")
}
override fun decodeString(bytes: List<UByte>, altEncoding: Boolean): String {
throw NotImplementedError("dummy")
}
override fun memorySize(dt: DataType): Int {
throw NotImplementedError("dummy")
}
}
class DummyZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1: Int = 0x10
override val SCRATCH_REG: Int = 0x11
override val SCRATCH_W1: Int= 0x20
override val SCRATCH_W2: Int = 0x30
init {
free.addAll(0..255)
removeReservedFromFreePool()
}
}
test("testAbstractZeropage") {
val compTarget = DummyCompilationTarget()
val zp = DummyZeropage(
CompilationOptions(
OutputType.RAW,
LauncherType.NONE,
ZeropageType.FULL,
listOf((0x50..0x5f)),
false,
false,
compTarget
)
)
zp.free.size shouldBe 256-6-16
}
})
class TestC64Zeropage: FunSpec({
val errors = ErrorReporterForTests()
test("testNames") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
zp.allocate("", DataType.UBYTE, null, errors)
zp.allocate("", DataType.UBYTE, null, errors)
zp.allocate("varname", DataType.UBYTE, null, errors)
shouldThrow<IllegalArgumentException> {
zp.allocate("varname", DataType.UBYTE, null, errors)
}
zp.allocate("varname2", DataType.UBYTE, null, errors)
}
test("testZpFloatEnable") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
shouldThrow<InternalCompilerException> {
zp.allocate("", DataType.FLOAT, null, errors)
}
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
shouldThrow<InternalCompilerException> {
zp2.allocate("", DataType.FLOAT, null, errors)
}
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
zp3.allocate("", DataType.FLOAT, null, errors)
}
test("testZpModesWithFloats") {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
shouldThrow<InternalCompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
}
shouldThrow<InternalCompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
}
}
test("testZpDontuse") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
println(zp.free)
zp.availableBytes() shouldBe 0
shouldThrow<InternalCompilerException> {
zp.allocate("", DataType.BYTE, null, errors)
}
}
test("testFreeSpacesBytes") {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
zp1.availableBytes() shouldBe 18
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
zp2.availableBytes() shouldBe 85
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
zp3.availableBytes() shouldBe 125
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
zp4.availableBytes() shouldBe 238
zp4.allocate("test", DataType.UBYTE, null, errors)
zp4.availableBytes() shouldBe 237
zp4.allocate("test2", DataType.UBYTE, null, errors)
zp4.availableBytes() shouldBe 236
}
test("testFreeSpacesWords") {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
zp1.availableWords() shouldBe 6
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
zp2.availableWords() shouldBe 38
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
zp3.availableWords() shouldBe 57
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
zp4.availableWords() shouldBe 116
zp4.allocate("test", DataType.UWORD, null, errors)
zp4.availableWords() shouldBe 115
zp4.allocate("test2", DataType.UWORD, null, errors)
zp4.availableWords() shouldBe 114
}
test("testReservedSpace") {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
zp1.availableBytes() shouldBe 238
50 shouldBeIn zp1.free
100 shouldBeIn zp1.free
49 shouldBeIn zp1.free
101 shouldBeIn zp1.free
200 shouldBeIn zp1.free
255 shouldBeIn zp1.free
199 shouldBeIn zp1.free
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target))
zp2.availableBytes() shouldBe 139
50 shouldNotBeIn zp2.free
100 shouldNotBeIn zp2.free
49 shouldBeIn zp2.free
101 shouldBeIn zp2.free
200 shouldNotBeIn zp2.free
255 shouldNotBeIn zp2.free
199 shouldBeIn zp2.free
}
test("testBasicsafeAllocation") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
zp.availableBytes() shouldBe 18
zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() shouldBe true
shouldThrow<ZeropageDepletedError> {
// in regular zp there aren't 5 sequential bytes free
zp.allocate("", DataType.FLOAT, null, errors)
}
for (i in 0 until zp.availableBytes()) {
val loc = zp.allocate("", DataType.UBYTE, null, errors)
loc shouldBeGreaterThan 0
}
zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false
shouldThrow<ZeropageDepletedError> {
zp.allocate("", DataType.UBYTE, null, errors)
}
shouldThrow<ZeropageDepletedError> {
zp.allocate("", DataType.UWORD, null, errors)
}
}
test("testFullAllocation") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
zp.availableBytes() shouldBe 238
zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() shouldBe true
val loc = zp.allocate("", DataType.UWORD, null, errors)
loc shouldBeGreaterThan 3
loc shouldNotBeIn zp.free
val num = zp.availableBytes() / 2
for(i in 0..num-4) {
zp.allocate("", DataType.UWORD, null, errors)
}
zp.availableBytes() shouldBe 6
shouldThrow<ZeropageDepletedError> {
// can't allocate because no more sequential bytes, only fragmented
zp.allocate("", DataType.UWORD, null, errors)
}
for(i in 0..5) {
zp.allocate("", DataType.UBYTE, null, errors)
}
zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false
shouldThrow<ZeropageDepletedError> {
// no more space
zp.allocate("", DataType.UBYTE, null, errors)
}
}
test("testEfficientAllocation") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
zp.availableBytes() shouldBe 18
zp.allocate("", DataType.WORD, null, errors) shouldBe 0x04
zp.allocate("", DataType.UBYTE, null, errors) shouldBe 0x06
zp.allocate("", DataType.UBYTE, null, errors) shouldBe 0x0a
zp.allocate("", DataType.UWORD, null, errors) shouldBe 0x9b
zp.allocate("", DataType.UWORD, null, errors) shouldBe 0x9e
zp.allocate("", DataType.UWORD, null, errors) shouldBe 0xa5
zp.allocate("", DataType.UWORD, null, errors) shouldBe 0xb0
zp.allocate("", DataType.UWORD, null, errors) shouldBe 0xbe
zp.allocate("", DataType.UBYTE, null, errors) shouldBe 0x0e
zp.allocate("", DataType.UBYTE, null, errors) shouldBe 0x92
zp.allocate("", DataType.UBYTE, null, errors) shouldBe 0x96
zp.allocate("", DataType.UBYTE, null, errors) shouldBe 0xf9
zp.availableBytes() shouldBe 0
}
test("testReservedLocations") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
withClue("zp _B1 and _REG must be next to each other to create a word") {
zp.SCRATCH_B1 + 1 shouldBe zp.SCRATCH_REG
}
}
})
class TestCx16Zeropage: FunSpec({
val errors = ErrorReporterForTests()
test("testReservedLocations") {
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
withClue("zp _B1 and _REG must be next to each other to create a word") {
zp.SCRATCH_B1 + 1 shouldBe zp.SCRATCH_REG
}
}
test("testFreeSpacesBytes") {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
zp1.availableBytes() shouldBe 88
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
zp2.availableBytes() shouldBe 175
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
zp3.availableBytes() shouldBe 216
zp3.allocate("test", DataType.UBYTE, null, errors)
zp3.availableBytes() shouldBe 215
zp3.allocate("test2", DataType.UBYTE, null, errors)
zp3.availableBytes() shouldBe 214
}
test("testFreeSpacesWords") {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
zp1.availableWords() shouldBe 108
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
zp2.availableWords() shouldBe 87
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
zp3.availableWords() shouldBe 44
zp3.allocate("test", DataType.UWORD, null, errors)
zp3.availableWords() shouldBe 43
zp3.allocate("test2", DataType.UWORD, null, errors)
zp3.availableWords() shouldBe 42
}
test("testReservedSpace") {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
zp1.availableBytes() shouldBe 216
0x22 shouldBeIn zp1.free
0x80 shouldBeIn zp1.free
0xff shouldBeIn zp1.free
0x02 shouldNotBeIn zp1.free
0x21 shouldNotBeIn zp1.free
}
})