Files
prog8/compiler/test/TestMemory.kt
T

503 lines
22 KiB
Kotlin

package prog8tests.compiler
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.datatest.withData
import io.kotest.engine.spec.tempdir
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.source.SourceCode
import prog8.code.target.*
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
class TestMemory: FunSpec({
val c64target = C64Target()
val outputDir = tempdir().toPath()
fun wrapWithProgram(statements: List<Statement>): Program {
val program = Program("test", DummyFunctions, c64target)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, null, statements.toMutableList(), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
program.addModule(module)
return program
}
test("assignment target not in mapped IO space C64") {
var memexpr = NumericLiteral.optimalInteger(0x0002, Position.DUMMY)
var target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0x1000, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0x9fff, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xa000, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xc000, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xcfff, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xeeee, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xffff, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
}
test("assign target in mapped IO space C64") {
var memexpr = NumericLiteral.optimalInteger(0x0000, Position.DUMMY)
var target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
memexpr = NumericLiteral.optimalInteger(0x0001, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
memexpr = NumericLiteral.optimalInteger(0xd000, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
memexpr = NumericLiteral.optimalInteger(0xdfff, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
}
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl.builder(DataType.BYTE, Position.DUMMY)
.names("address")
.type(vartype)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(decl, assignment))
return target
}
test("identifier mapped to IO memory on C64") {
var target = createTestProgramForMemoryRefViaVar(0x1000u, VarDeclType.VAR)
target.isIOAddress(c64target) shouldBe false
target = createTestProgramForMemoryRefViaVar(0xd020u, VarDeclType.VAR)
target.isIOAddress(c64target) shouldBe false
target = createTestProgramForMemoryRefViaVar(0x1000u, VarDeclType.CONST)
target.isIOAddress(c64target) shouldBe false
target = createTestProgramForMemoryRefViaVar(0xd020u, VarDeclType.CONST)
target.isIOAddress(c64target) shouldBe true
target = createTestProgramForMemoryRefViaVar(0x1000u, VarDeclType.MEMORY)
target.isIOAddress(c64target) shouldBe false
target = createTestProgramForMemoryRefViaVar(0xd020u, VarDeclType.MEMORY)
target.isIOAddress(c64target) shouldBe true
}
test("memory expression mapped to IO memory on C64") {
var memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
var target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0xd020, Position.DUMMY), Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
}
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl.builder(DataType.BYTE, Position.DUMMY).names("address").build()
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
null,
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, null, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, c64target)
.addModule(module)
target.isIOAddress(c64target) shouldBe false
}
test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u
val decl = VarDecl.builder(DataType.UBYTE, Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
null,
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, null, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, c64target)
.addModule(module)
target.isIOAddress(c64target) shouldBe false
}
test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u
val decl = VarDecl.builder(DataType.UBYTE, Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
null,
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, null, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, c64target)
.addModule(module)
target.isIOAddress(c64target) shouldBe true
}
test("array not in mapped IO ram") {
val decl = VarDecl.builder(DataType.arrayFor(BaseDataType.UBYTE, c64target), Position.DUMMY)
.names("address")
.build()
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, null, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, c64target)
.addModule(module)
target.isIOAddress(c64target) shouldBe false
}
test("memory mapped array not in mapped IO ram") {
val address = 0x1000u
val decl = VarDecl.builder(DataType.arrayFor(BaseDataType.UBYTE, c64target), Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, null, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, c64target)
.addModule(module)
target.isIOAddress(c64target) shouldBe false
}
test("memory mapped array in mapped IO ram") {
val address = 0xd800u
val decl = VarDecl.builder(DataType.arrayFor(BaseDataType.UBYTE, c64target), Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, null, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, c64target)
.addModule(module)
target.isIOAddress(c64target) shouldBe true
}
test("memory() with spaces in name works") {
compileText(
C64Target(), false, """
main {
sub start() {
uword @shared mem = memory("a b c", 100, $100)
}
}
""", outputDir, writeAssembly = true) shouldNotBe null
}
test("memory() with invalid argument") {
compileText(
C64Target(), false, """
main {
sub start() {
uword @shared mem1 = memory("abc", 100, -2)
}
}
""", outputDir, writeAssembly = true) shouldBe null
}
context("memsizer") {
withData(VMTarget(), C64Target(), PETTarget(), C128Target()) { target ->
shouldThrow<IllegalArgumentException> {
target.memorySize(BaseDataType.UNDEFINED)
}
target.memorySize(BaseDataType.BOOL) shouldBe 1
target.memorySize(BaseDataType.BYTE) shouldBe 1
target.memorySize(BaseDataType.WORD) shouldBe 2
target.memorySize(BaseDataType.LONG) shouldBe 4
target.memorySize(BaseDataType.FLOAT) shouldBe target.FLOAT_MEM_SIZE.toInt()
target.memorySize(DataType.BOOL, null) shouldBe 1
target.memorySize(DataType.WORD, null) shouldBe 2
target.memorySize(DataType.LONG, null) shouldBe 4
target.memorySize(DataType.FLOAT, null) shouldBe target.FLOAT_MEM_SIZE.toInt()
target.memorySize(DataType.STR, null) shouldBe target.POINTER_MEM_SIZE.toInt()
target.memorySize(DataType.STR, 50) shouldBe 50
target.memorySize(BaseDataType.STR) shouldBe target.POINTER_MEM_SIZE.toInt()
target.memorySize(BaseDataType.ARRAY) shouldBe target.POINTER_MEM_SIZE.toInt()
target.memorySize(BaseDataType.ARRAY_SPLITW) shouldBe target.POINTER_MEM_SIZE.toInt()
target.memorySize(DataType.arrayFor(BaseDataType.BOOL, c64target), 10) shouldBe 10
target.memorySize(DataType.arrayFor(BaseDataType.BYTE, c64target), 10) shouldBe 10
target.memorySize(DataType.arrayFor(BaseDataType.WORD, c64target), 10) shouldBe 20
target.memorySize(DataType.arrayFor(BaseDataType.UWORD, c64target), 10) shouldBe 20
target.memorySize(DataType.arrayFor(BaseDataType.LONG, c64target), 10) shouldBe 40
target.memorySize(DataType.arrayFor(BaseDataType.FLOAT, c64target), 10) shouldBe 10*target.FLOAT_MEM_SIZE.toInt()
target.memorySize(DataType.splitWordArrayFor(BaseDataType.WORD), 10) shouldBe 20
target.memorySize(DataType.splitWordArrayFor(BaseDataType.UWORD), 10) shouldBe 20
target.memorySize(DataType.BOOL, 10) shouldBe 10
target.memorySize(DataType.UWORD, 10) shouldBe 20
target.memorySize(DataType.LONG, 10) shouldBe 40
target.memorySize(DataType.FLOAT, 10) shouldBe 10*target.FLOAT_MEM_SIZE.toInt()
}
}
test("errors when writing to data in ROM (romable code)") {
val src="""
%option romable
main {
ubyte[] array = [1,2,3,4,5]
ubyte[10] ramarray
str name = "foobar"
&ubyte[10] memorymappedarray = 1000
&ubyte[10] memorymappedarray_zp = 200
sub start() {
array[2] = 99
name[2] = 'x'
ramarray[2] = 42
memorymappedarray[2] = 42
memorymappedarray_zp[2] = 42
}
}"""
val errors=ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, errors=errors, writeAssembly = false, varshigh=1) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "12:9: cannot write to a string or an array with initalization values (located in ROM"
errors.errors[1] shouldContain "13:9: cannot write to a string or an array with initalization values (located in ROM"
errors.errors[2] shouldContain "15:9: cannot write to this memory mapped string or array (possibly located in ROM"
}
test("long addresses not supported in memread and memwrite for 6502") {
val src = """
main {
sub start() {
const long bufferll = 888888
long @shared addrlv = 777777
cx16.r0L = bufferll[2]
cx16.r1L = @(999999)
cx16.r2L = @(addrlv)
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), true, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "invalid address type"
errors.errors[1] shouldContain "invalid address type"
errors.errors[2] shouldContain "invalid address type"
}
test("long addresses supported in memread and memwrite for m68k") {
val src = """
main {
sub start() {
const long bufferll = 888888
long @shared addrlv = 777777
ubyte result1 = bufferll[2]
ubyte result2 = @(999999)
ubyte result3 = @(addrlv)
}
}"""
val errors = ErrorReporterForTests()
compileText(Qemu68kTarget(), true, src, outputDir, errors = errors, writeAssembly = false) shouldNotBe null
errors.errors.size shouldBe 0
}
})