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 { throw NotImplementedError("dummy") } override fun decodeString(bytes: List, 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 { 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 { zp.allocate("", DataType.FLOAT, null, errors) } val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target)) shouldThrow { 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 { C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target)) } shouldThrow { 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 { 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 { // 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 { zp.allocate("", DataType.UBYTE, null, errors) } shouldThrow { 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 { // 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 { // 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 } })