From 02f3f5d0f5feaabf1fdd230065574f7047af73ca Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 16 Dec 2024 07:26:59 +0100 Subject: [PATCH] @split is back to force splitting of word arrays --- benchmark-program/b_textelite.p8 | 72 +++++++++--------- codeCore/src/prog8/code/core/Enumerations.kt | 6 ++ .../optimizer/ConstantIdentifierReplacer.kt | 6 +- compiler/res/prog8lib/cx16/palette.p8 | 5 +- .../compiler/astprocessing/AstPreprocessor.kt | 2 +- .../astprocessing/StatementReorderer.kt | 1 + .../compiler/astprocessing/VariousCleanups.kt | 38 ++++++++++ compiler/test/TestMemory.kt | 21 ++++-- compiler/test/ast/TestVariousCompilerAst.kt | 4 +- .../test/codegeneration/TestAsmGenSymbols.kt | 9 ++- compilerAst/src/prog8/ast/Program.kt | 3 +- .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 17 ++++- .../src/prog8/ast/statements/AstStatements.kt | 16 ++-- docs/source/libraries.rst | 3 + docs/source/todo.rst | 12 +-- examples/test.p8 | 14 +++- examples/textelite.p8 | 74 +++++++++---------- 17 files changed, 190 insertions(+), 113 deletions(-) diff --git a/benchmark-program/b_textelite.p8 b/benchmark-program/b_textelite.p8 index f49e31e59..0223186a5 100644 --- a/benchmark-program/b_textelite.p8 +++ b/benchmark-program/b_textelite.p8 @@ -742,42 +742,42 @@ elite_galaxy { } elite_planet { - str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] - str[] words82 = ["very", "mildly", "most", "reasonably", ""] - str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] - str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] - str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] - str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] - str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] - str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] - str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] - str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] - str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] - str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] - str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] - str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] - str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] - str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] - str[] words91 = ["planet", "world", "place", "little planet", "dump"] - str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] - str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] - str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] - str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] - str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] - str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] - str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] - str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] - str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] - str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] - str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] - str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] - str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] - str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] - str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] - str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] - str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] - str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] - str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""] + str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] + str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"] + str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] uword[] @shared wordlists = [ words81, words82, words83, words84, words85, words86, words87, words88, diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index 829e99de8..27a7a8bd1 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -364,3 +364,9 @@ enum class ZeropageWish { DONTCARE, NOT_IN_ZEROPAGE } + +enum class SplitWish { + DONTCARE, + SPLIT, + NOSPLIT +} \ No newline at end of file diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt index 74c2fecbe..8310e404d 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -70,7 +70,7 @@ class VarConstantValueTypeAdjuster( if (declValue != null) { // variable is never written to, so it can be replaced with a constant, IF the value is a constant errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position) - val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position) + val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position) decl.value = null return listOf( IAstModification.ReplaceNode(decl, const, parent) @@ -90,7 +90,7 @@ class VarConstantValueTypeAdjuster( } // variable only has a single write, and it is the initialization value, so it can be replaced with a constant, IF the value is a constant errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position) - val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position) + val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position) return listOf( IAstModification.ReplaceNode(decl, const, parent), IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer) @@ -391,7 +391,7 @@ internal class ConstantIdentifierReplacer( val targetDatatype = assignment.target.inferType(program) if(targetDatatype.isArray) { val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOrUndef(), - ZeropageWish.DONTCARE, null, "dummy", emptyList(), + ZeropageWish.DONTCARE, SplitWish.NOSPLIT, null, "dummy", emptyList(), assignment.value, false, 0u, false, Position.DUMMY) val replaceValue = createConstArrayInitializerValue(decl) if(replaceValue!=null) { diff --git a/compiler/res/prog8lib/cx16/palette.p8 b/compiler/res/prog8lib/cx16/palette.p8 index 15555eb92..d715384ff 100644 --- a/compiler/res/prog8lib/cx16/palette.p8 +++ b/compiler/res/prog8lib/cx16/palette.p8 @@ -145,8 +145,9 @@ palette { sub fade_step_colors(ubyte startindex, ubyte endindex, uword target_colors) -> bool { ; Perform one color fade step for multiple consecutive palette entries, to different target colors. ; startindex = palette index of first color to fade - ; endindex = palette index of last color to fade - ; target_colors = address of uword $RGB array of colors to fade towards + ; endindex = palette index of last color to fade, inclusive + ; target_colors = address of uword $RGB array of colors to fade towards, + ; in *linear* storage format (@nosplit) which is usually how palette blobs are loaded from disk. ; Returns true if one or more colors were changed, false if no fade steps were done anymore. ; So you usually keep calling this until it returns false. bool changed = false diff --git a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt index f643ed339..e7942fd1e 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt @@ -194,7 +194,7 @@ class AstPreprocessor(val program: Program, private fun makeUnSplitArray(decl: VarDecl): Iterable { val splitDt = DataType.arrayFor(decl.datatype.sub!!.dt, false) val newDecl = VarDecl( - decl.type, decl.origin, splitDt, decl.zeropage, decl.arraysize, decl.name, emptyList(), + decl.type, decl.origin, splitDt, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, emptyList(), decl.value?.copy(), decl.sharedWithAsm, decl.alignment, false, decl.position ) return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent)) diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 4d99e9a0b..ced78717b 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -217,6 +217,7 @@ internal class StatementReorderer( .map { val newvar = VarDecl(it.type, it.origin, DataType.forDt(BaseDataType.UWORD), it.zeropage, + it.splitwordarray, null, it.name, emptyList(), diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 756991ed8..bc6d3c96a 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -36,6 +36,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, when(decl.type) { VarDeclType.VAR -> { if(decl.isArray) { + if(decl.datatype.isSplitWordArray) errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position) } else { if (valueDt.largerSizeThan(decl.datatype)) { @@ -72,6 +73,43 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, } + // check splitting of word arrays + if(!decl.datatype.isWordArray && decl.splitwordarray != SplitWish.DONTCARE) { + if(decl.origin != VarDeclOrigin.ARRAYLITERAL) + errors.err("@split and @nosplit are for word arrays only", decl.position) + } + else if(decl.datatype.isWordArray) { + var changeDataType: DataType? = null + var changeSplit: SplitWish = decl.splitwordarray + when(decl.splitwordarray) { + SplitWish.DONTCARE -> { + if(options.dontSplitWordArrays) { + changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null + changeSplit = SplitWish.NOSPLIT + } + else { + changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true) + changeSplit = SplitWish.SPLIT + } + } + SplitWish.SPLIT -> { + changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true) + } + SplitWish.NOSPLIT -> { + changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null + } + } + if(changeDataType!=null) { + var value = decl.value + if(value is ArrayLiteral && !(value.type istype changeDataType)) { + value = ArrayLiteral(InferredTypes.knownFor(changeDataType), value.value, value.position) + } + val newDecl = VarDecl(decl.type, decl.origin, changeDataType, decl.zeropage, + changeSplit, decl.arraysize, decl.name, decl.names, + value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position) + return listOf(IAstModification.ReplaceNode(decl, newDecl, parent)) + } + } return noModifications } diff --git a/compiler/test/TestMemory.kt b/compiler/test/TestMemory.kt index 9aec6e95f..006486d58 100644 --- a/compiler/test/TestMemory.kt +++ b/compiler/test/TestMemory.kt @@ -111,7 +111,8 @@ class TestMemory: FunSpec({ } fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget { - val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.BYTE), ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) + val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.BYTE), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) val memexpr = IdentifierReference(listOf("address"), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) @@ -149,7 +150,8 @@ class TestMemory: FunSpec({ } test("regular variable not in mapped IO ram on C64") { - val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.BYTE), ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, 0u, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.BYTE), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "address", emptyList(), null, false, 0u, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, 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, mutableListOf(decl, assignment), Position.DUMMY) @@ -161,7 +163,8 @@ class TestMemory: FunSpec({ test("memory mapped variable not in mapped IO ram on C64") { val address = 0x1000u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UBYTE), ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UBYTE), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, 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, mutableListOf(decl, assignment), Position.DUMMY) @@ -173,7 +176,8 @@ class TestMemory: FunSpec({ test("memory mapped variable in mapped IO ram on C64") { val address = 0xd020u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UBYTE), ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UBYTE), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, 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, mutableListOf(decl, assignment), Position.DUMMY) @@ -184,7 +188,8 @@ class TestMemory: FunSpec({ } test("array not in mapped IO ram") { - val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, 0u, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "address", emptyList(), null, false, 0u, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) @@ -197,7 +202,8 @@ class TestMemory: FunSpec({ test("memory mapped array not in mapped IO ram") { val address = 0x1000u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) @@ -210,7 +216,8 @@ class TestMemory: FunSpec({ test("memory mapped array in mapped IO ram") { val address = 0xd800u - val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) + val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY) val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY) diff --git a/compiler/test/ast/TestVariousCompilerAst.kt b/compiler/test/ast/TestVariousCompilerAst.kt index 2c19fd198..b4dab744f 100644 --- a/compiler/test/ast/TestVariousCompilerAst.kt +++ b/compiler/test/ast/TestVariousCompilerAst.kt @@ -913,7 +913,7 @@ main { (st1[1] as VarDecl).name shouldBe "names" val array1 = (st1[1] as VarDecl).value as ArrayLiteral array1.type.isArray shouldBe true - array1.type.getOrUndef() shouldBe DataType.arrayFor(BaseDataType.UWORD, false) + array1.type.getOrUndef() shouldBe DataType.arrayFor(BaseDataType.UWORD, true) val ast2 = result.codegenAst!! val st2 = ast2.entrypoint()!!.children @@ -921,7 +921,7 @@ main { (st2[0] as PtVariable).name shouldBe "p8v_variable" (st2[1] as PtVariable).name shouldBe "p8v_names" val array2 = (st2[1] as PtVariable).value as PtArray - array2.type shouldBe DataType.arrayFor(BaseDataType.UWORD, false) + array2.type shouldBe DataType.arrayFor(BaseDataType.UWORD, true) } test("defer syntactic sugaring") { diff --git a/compiler/test/codegeneration/TestAsmGenSymbols.kt b/compiler/test/codegeneration/TestAsmGenSymbols.kt index 0b79ca8c8..a65f5b43d 100644 --- a/compiler/test/codegeneration/TestAsmGenSymbols.kt +++ b/compiler/test/codegeneration/TestAsmGenSymbols.kt @@ -46,8 +46,10 @@ class TestAsmGenSymbols: StringSpec({ } */ - val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UWORD), ZeropageWish.DONTCARE, null, "localvar", emptyList(), NumericLiteral.optimalInteger(1234, Position.DUMMY), false, 0u, false, Position.DUMMY) - val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UWORD), ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, 0u, false, Position.DUMMY) + val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UWORD), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "localvar", emptyList(), NumericLiteral.optimalInteger(1234, Position.DUMMY), false, 0u, false, Position.DUMMY) + val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UWORD), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "tgt", emptyList(), null, false, 0u, false, Position.DUMMY) val labelInSub = Label("locallabel", Position.DUMMY) val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, false, Position.DUMMY) @@ -63,7 +65,8 @@ class TestAsmGenSymbols: StringSpec({ val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8) val subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY) val labelInBlock = Label("label_outside", Position.DUMMY) - val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UWORD), ZeropageWish.DONTCARE, null, "var_outside", emptyList(),null, false, 0u, false, Position.DUMMY) + val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UWORD), ZeropageWish.DONTCARE, + SplitWish.DONTCARE, null, "var_outside", emptyList(),null, false, 0u, false, Position.DUMMY) val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY) val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) diff --git a/compilerAst/src/prog8/ast/Program.kt b/compilerAst/src/prog8/ast/Program.kt index 48af21843..8f50a16a8 100644 --- a/compilerAst/src/prog8/ast/Program.kt +++ b/compilerAst/src/prog8/ast/Program.kt @@ -87,7 +87,8 @@ class Program(val name: String, fun addNewInternedStringvar(string: StringLiteral): Pair, VarDecl> { val varName = "string_${internedStringsBlock.statements.size}" val decl = VarDecl( - VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.forDt(BaseDataType.STR), ZeropageWish.NOT_IN_ZEROPAGE, null, varName, emptyList(), string, + VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.forDt(BaseDataType.STR), ZeropageWish.NOT_IN_ZEROPAGE, + SplitWish.DONTCARE, null, varName, emptyList(), string, sharedWithAsm = false, alignment = 0u, dirty = false, position = string.position ) internedStringsBlock.statements.add(decl) diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index 35dc5deb1..b1e9d9b77 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -365,6 +365,16 @@ private fun getZpOption(options: DecloptionsContext?): ZeropageWish { } } +private fun getSplitOption(options: DecloptionsContext?): SplitWish { + if(options==null) + return SplitWish.DONTCARE + return when { + options.NOSPLIT().isNotEmpty() -> SplitWish.NOSPLIT + options.SPLIT().isNotEmpty() -> SplitWish.SPLIT + else -> SplitWish.DONTCARE + } +} + private fun Assign_targetContext.toAst() : AssignTarget { return when(this) { is IdentifierTargetContext -> { @@ -754,25 +764,24 @@ private fun When_choiceContext.toAst(): WhenChoice { private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl { val options = decloptions() val zp = getZpOption(options) + val split = getSplitOption(options) val identifiers = identifier() val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() val name = if(identifiers.size==1) identifiername.text else "" val isArray = ARRAYSIG() != null || arrayindex() != null - if(options.SPLIT().isNotEmpty()) - throw SyntaxError("@split is now the default for word arrays. Use @nosplit if you don't want to split it.", toPosition()) - val nosplit = options.NOSPLIT().isNotEmpty() val alignword = options.ALIGNWORD().isNotEmpty() val align64 = options.ALIGN64().isNotEmpty() val alignpage = options.ALIGNPAGE().isNotEmpty() if(alignpage && alignword) throw SyntaxError("choose a single alignment option", toPosition()) val baseDt = datatype()?.toAst() ?: BaseDataType.UNDEFINED - val dt = if(isArray) DataType.arrayFor(baseDt, nosplit!=true) else DataType.forDt(baseDt) + val dt = if(isArray) DataType.arrayFor(baseDt, split!=SplitWish.NOSPLIT) else DataType.forDt(baseDt) return VarDecl( type, VarDeclOrigin.USERCODE, dt, zp, + split, arrayindex()?.toAst(), name, if(identifiers.size==1) emptyList() else identifiers.map { diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index a51c6c761..fb4f6046a 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -241,7 +241,8 @@ enum class VarDeclType { class VarDecl(val type: VarDeclType, val origin: VarDeclOrigin, val datatype: DataType, - var zeropage: ZeropageWish, + val zeropage: ZeropageWish, + val splitwordarray: SplitWish, var arraysize: ArrayIndex?, override val name: String, val names: List, @@ -270,7 +271,7 @@ class VarDecl(val type: VarDeclType, value = AddressOf(IdentifierReference(regname, param.position), null, param.position) } val dt = if(param.type.isArray) DataType.forDt(BaseDataType.UWORD) else param.type - return VarDecl(decltype, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, null, param.name, emptyList(), value, + return VarDecl(decltype, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, SplitWish.DONTCARE, null, param.name, emptyList(), value, sharedWithAsm = false, alignment = 0u, dirty = false, @@ -290,7 +291,8 @@ class VarDecl(val type: VarDeclType, } } val arraysize = ArrayIndex.forArray(array) - return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array, + return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, + SplitWish.NOSPLIT, arraysize, autoVarName, emptyList(), array, sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position) } } @@ -329,7 +331,7 @@ class VarDecl(val type: VarDeclType, fun copy(newDatatype: DataType): VarDecl { if(names.size>1) throw FatalAstException("should not copy a vardecl that still has multiple names") - val copy = VarDecl(type, origin, newDatatype, zeropage, arraysize?.copy(), name, names, value?.copy(), + val copy = VarDecl(type, origin, newDatatype, zeropage, splitwordarray, arraysize?.copy(), name, names, value?.copy(), sharedWithAsm, alignment, dirty, position) copy.allowInitializeWithZero = this.allowInitializeWithZero return copy @@ -344,19 +346,19 @@ class VarDecl(val type: VarDeclType, if(value==null || value?.isSimple==true) { // just copy the initialization value to a separate vardecl for each component return names.map { - val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), value?.copy(), + val copy = VarDecl(type, origin, datatype, zeropage, splitwordarray, arraysize?.copy(), it, emptyList(), value?.copy(), sharedWithAsm, alignment, dirty, position) copy.allowInitializeWithZero = this.allowInitializeWithZero copy } } else { // evaluate the value once in the vardecl for the first component, and set the other components to the first - val first = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), names[0], emptyList(), value?.copy(), + val first = VarDecl(type, origin, datatype, zeropage, splitwordarray, arraysize?.copy(), names[0], emptyList(), value?.copy(), sharedWithAsm, alignment, dirty, position) first.allowInitializeWithZero = this.allowInitializeWithZero val firstVar = firstVarAsValue(first) return listOf(first) + names.drop(1 ).map { - val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), firstVar.copy(), + val copy = VarDecl(type, origin, datatype, zeropage, splitwordarray, arraysize?.copy(), it, emptyList(), firstVar.copy(), sharedWithAsm, alignment, dirty, position) copy.allowInitializeWithZero = this.allowInitializeWithZero copy diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 83fa57fa8..c24428f3d 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -1017,6 +1017,9 @@ There are also a few better looking Commodore 64 color palettes available here, because the Commander X16's default colors for this (the first 16 colors) are too saturated and are quite different than how they looked on a VIC-II chip in a C64. +Some routines may require a colors array as @nosplit (such as fade_step_colors), otherwise wrong colors come out. +(this is the same for some kernal routines such as cx16.FB_set_palette) + Read the `palette source code `_ to see what's in there. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ac8bb8cd7..ad249c382 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,17 +1,17 @@ TODO ==== -- DONE: make word arrays split by default (remove @split tag) and use new @nosplit tag to make an array use the old storage format? +- add &< and &> operators to get the address of the lsb-array and msb-array, respectively. + +- DONE: make word arrays split by default (remove @split tag) and use new @nosplit tag to make an array use the old storage format - DONE: &splitarray will give you the start address of the lsb-array (which is immediately followed by the msb-array) - DONE: invert -splitarrays command line option: -dontsplitarrays and remove "splitarrays" %option switch - DONE: added sprites.pos_batch_nosplit when the x/y arrays are linear instead of split word arrays - DONE: add palette.set_rgb_nosplit() and set_rbg_be_nosplit() for linear instead of split word arrays -- update Syntax files + Document all of this (also that word arrays can then have length 256 by default as well, and that @nosplit will reduce it to half.) +- DONE: removed anyall module +- DONE: @split does now always splits a word array even when the dontsplit option is enabled (and @nosplit does the inverse) +- update Syntax files + Document all of this (also that word arrays can then have length 256 by default as well, and that @nosplit will reduce it to half.) + warning about using peekw that it may depend on array storage format - also document that kernal routines such as FB_set_palette() expect a @nosplit word array otherwise colors turn up bad -- benchmark program became slower!? (did get smaller) -- add &< and &> operators to get the address of the lsb-array and msb-array, respectively. - -- remove the error when using @split and make it so that @split always splits the array even when the dontsplit option is enabled. (ast check that you can only put this on word arrays) - announce prog8 on the 6502.org site? diff --git a/examples/test.p8 b/examples/test.p8 index db12c4541..093bdf2f0 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,12 +1,18 @@ %import textio -%import palette %zeropage basicsafe %option no_sysinit main { sub start() { - palette.set_c64pepto() - sys.wait(100) - palette.set_default16() + uword[] @split splitarray = [1000,2000,3000] + uword[] @nosplit nosplitarray = [1001,2002,3003] + uword[] wordarray = [1111,2222,3333] + + txt.print_uw(splitarray[2]) + txt.spc() + txt.print_uw(nosplitarray[2]) + txt.spc() + txt.print_uw(wordarray[2]) + txt.nl() } } diff --git a/examples/textelite.p8 b/examples/textelite.p8 index f48e3c0f1..df8b78d2a 100644 --- a/examples/textelite.p8 +++ b/examples/textelite.p8 @@ -706,42 +706,42 @@ planet { str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] - str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] - str[] words82 = ["very", "mildly", "most", "reasonably", ""] - str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] - str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] - str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] - str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] - str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] - str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] - str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] - str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] - str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] - str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] - str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] - str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] - str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] - str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] - str[] words91 = ["planet", "world", "place", "little planet", "dump"] - str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] - str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] - str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] - str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] - str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] - str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] - str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] - str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] - str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] - str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] - str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] - str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] - str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] - str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] - str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] - str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] - str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] - str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] - str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""] + str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] + str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"] + str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] uword[] @shared wordlists = [ words81, words82, words83, words84, words85, words86, words87, words88, @@ -1000,7 +1000,7 @@ planet { sub getword(ubyte listnum, ubyte wordidx) -> uword { uword list = wordlists[listnum-$81] - return peekw(list + wordidx*2) + return peekw(list + wordidx*2) ; this depends on the word list itself to be @nosplit ! } }