prog8/compiler/test/ZeropageTests.kt

271 lines
13 KiB
Kotlin
Raw Normal View History

2021-06-01 19:21:33 +00:00
package prog8tests
2022-01-15 11:29:29 +00:00
import com.github.michaelbull.result.expectError
import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.onFailure
import io.kotest.assertions.fail
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.comparables.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
2021-06-01 19:21:33 +00:00
import prog8.ast.base.DataType
2021-12-28 13:23:36 +00:00
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
import prog8.codegen.target.c64.C64Zeropage
import prog8.codegen.target.cx16.CX16Zeropage
import prog8.compilerinterface.*
import prog8tests.helpers.DummyCompilationTarget
2021-10-29 14:46:56 +00:00
import prog8tests.helpers.ErrorReporterForTests
2021-06-01 19:21:33 +00:00
class TestAbstractZeropage: FunSpec({
class DummyZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x10u
override val SCRATCH_REG = 0x11u
override val SCRATCH_W1 = 0x20u
override val SCRATCH_W2 = 0x30u
init {
free.addAll(0u..255u)
removeReservedFromFreePool()
}
}
test("testAbstractZeropage") {
val zp = DummyZeropage(
CompilationOptions(
OutputType.RAW,
LauncherType.NONE,
ZeropageType.FULL,
listOf((0x50u..0x5fu)),
false,
false,
DummyCompilationTarget
)
)
zp.free.size shouldBe 256-6-16
}
})
2021-06-01 19:21:33 +00:00
class TestC64Zeropage: FunSpec({
2021-06-01 19:21:33 +00:00
val errors = ErrorReporterForTests()
test("testNames") {
2021-06-01 19:21:33 +00:00
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
2022-01-15 16:05:34 +00:00
var result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) }
2022-01-15 16:05:34 +00:00
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) }
2022-01-15 16:05:34 +00:00
result = zp.allocate(listOf("varname"), DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) }
2022-01-15 16:05:34 +00:00
shouldThrow<IllegalArgumentException> { zp.allocate(listOf("varname"), DataType.UBYTE, null, null, errors) }
result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) }
2021-06-01 19:21:33 +00:00
}
test("testZpFloatEnable") {
2021-06-01 19:21:33 +00:00
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
2022-01-15 16:05:34 +00:00
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "should be allocation error due to disabled floats" }
2021-06-01 19:21:33 +00:00
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
2022-01-15 16:05:34 +00:00
result = zp2.allocate(emptyList(), DataType.FLOAT, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "should be allocation error due to disabled ZP use" }
2021-06-01 19:21:33 +00:00
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
2022-01-15 16:05:34 +00:00
zp3.allocate(emptyList(), DataType.FLOAT, null, null, errors)
2021-06-01 19:21:33 +00:00
}
test("testZpModesWithFloats") {
2021-06-01 19:21:33 +00:00
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> {
2021-06-01 19:21:33 +00:00
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
}
shouldThrow<InternalCompilerException> {
2021-06-01 19:21:33 +00:00
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
}
}
test("testZpDontuse") {
2021-06-01 19:21:33 +00:00
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
println(zp.free)
zp.availableBytes() shouldBe 0
2022-01-15 16:05:34 +00:00
val result = zp.allocate(emptyList(), DataType.BYTE, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "expected error due to disabled ZP use" }
2021-06-01 19:21:33 +00:00
}
test("testFreeSpacesBytes") {
2021-06-01 19:21:33 +00:00
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
zp1.availableBytes() shouldBe 18
2021-06-01 19:21:33 +00:00
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
zp2.availableBytes() shouldBe 85
2021-06-01 19:21:33 +00:00
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
zp3.availableBytes() shouldBe 125
2021-06-01 19:21:33 +00:00
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
zp4.availableBytes() shouldBe 239
2022-01-15 16:05:34 +00:00
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
zp4.availableBytes() shouldBe 238
2022-01-15 16:05:34 +00:00
zp4.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
zp4.availableBytes() shouldBe 237
2021-06-01 19:21:33 +00:00
}
test("testReservedSpace") {
2021-06-01 19:21:33 +00:00
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
zp1.availableBytes() shouldBe 239
50u shouldBeIn zp1.free
100u shouldBeIn zp1.free
49u shouldBeIn zp1.free
101u shouldBeIn zp1.free
200u shouldBeIn zp1.free
255u shouldBeIn zp1.free
199u shouldBeIn zp1.free
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), false, false, C64Target))
zp2.availableBytes() shouldBe 139
50u shouldNotBeIn zp2.free
100u shouldNotBeIn zp2.free
49u shouldBeIn zp2.free
101u shouldBeIn zp2.free
200u shouldNotBeIn zp2.free
255u shouldNotBeIn zp2.free
199u shouldBeIn zp2.free
2021-06-01 19:21:33 +00:00
}
test("testBasicsafeAllocation") {
2021-06-01 19:21:33 +00:00
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
2021-06-01 19:21:33 +00:00
2022-01-15 16:05:34 +00:00
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" }
2021-06-01 19:21:33 +00:00
for (i in 0 until zp.availableBytes()) {
2022-01-15 16:05:34 +00:00
val alloc = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
alloc.getOrElse { throw it }
2021-06-01 19:21:33 +00:00
}
zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false
2022-01-15 16:05:34 +00:00
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "expected allocation error" }
2022-01-15 16:05:34 +00:00
result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "expected allocation error" }
2021-06-01 19:21:33 +00:00
}
test("testFullAllocation") {
2021-06-01 19:21:33 +00:00
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
zp.availableBytes() shouldBe 239
zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() shouldBe true
2022-01-15 16:05:34 +00:00
var result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
val loc = result.getOrElse { throw it } .first
loc shouldBeGreaterThan 3u
loc shouldNotBeIn zp.free
2021-06-01 19:21:33 +00:00
val num = zp.availableBytes() / 2
for(i in 0..num-3) {
2022-01-15 16:05:34 +00:00
zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
2021-06-01 19:21:33 +00:00
}
zp.availableBytes() shouldBe 5
2021-06-01 19:21:33 +00:00
2022-01-15 11:29:29 +00:00
// can't allocate because no more sequential bytes, only fragmented
2022-01-15 16:05:34 +00:00
result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "should give allocation error" }
2021-06-01 19:21:33 +00:00
for(i in 0..4) {
2022-01-15 16:05:34 +00:00
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
2021-06-01 19:21:33 +00:00
}
zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false
2022-01-15 16:05:34 +00:00
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
2022-01-15 11:29:29 +00:00
result.expectError { "should give allocation error" }
2021-06-01 19:21:33 +00:00
}
test("testEfficientAllocation") {
2021-06-01 19:21:33 +00:00
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
zp.availableBytes() shouldBe 18
2022-01-15 16:05:34 +00:00
zp.allocate(emptyList(), DataType.WORD, null, null, errors ).getOrElse{throw it}.first shouldBe 0x04u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0au
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9bu
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9eu
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xa5u
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xb0u
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xbeu
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0eu
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x92u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x96u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u
zp.availableBytes() shouldBe 0
2021-06-01 19:21:33 +00:00
}
test("testReservedLocations") {
2021-06-01 19:21:33 +00:00
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 + 1u shouldBe zp.SCRATCH_REG
}
2021-06-01 19:21:33 +00:00
}
})
2021-06-01 19:21:33 +00:00
class TestCx16Zeropage: FunSpec({
val errors = ErrorReporterForTests()
2021-06-01 19:21:33 +00:00
test("testReservedLocations") {
2021-06-01 19:21:33 +00:00
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 + 1u shouldBe zp.SCRATCH_REG
}
2021-06-01 19:21:33 +00:00
}
test("testFreeSpacesBytes") {
2021-06-01 19:21:33 +00:00
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
zp1.availableBytes() shouldBe 88
2021-06-01 19:21:33 +00:00
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
zp2.availableBytes() shouldBe 175
2021-06-01 19:21:33 +00:00
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
zp3.availableBytes() shouldBe 216
2022-01-15 16:05:34 +00:00
zp3.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
zp3.availableBytes() shouldBe 215
2022-01-15 16:05:34 +00:00
zp3.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
zp3.availableBytes() shouldBe 214
2021-06-01 19:21:33 +00:00
}
test("testReservedSpace") {
2021-06-01 19:21:33 +00:00
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
zp1.availableBytes() shouldBe 216
0x22u shouldBeIn zp1.free
0x80u shouldBeIn zp1.free
0xffu shouldBeIn zp1.free
0x02u shouldNotBeIn zp1.free
0x21u shouldNotBeIn zp1.free
2021-06-01 19:21:33 +00:00
}
test("preallocated zp vars") {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
2022-02-06 17:57:23 +00:00
zp1.variables[listOf("test")] shouldBe null
zp1.variables[listOf("cx16", "r0")] shouldNotBe null
zp1.variables[listOf("cx16", "r15")] shouldNotBe null
zp1.variables[listOf("cx16", "r0L")] shouldNotBe null
zp1.variables[listOf("cx16", "r15L")] shouldNotBe null
zp1.variables[listOf("cx16", "r0sH")] shouldNotBe null
zp1.variables[listOf("cx16", "r15sH")] shouldNotBe null
}
})