make word arrays split by default (w.i.p.)

This commit is contained in:
Irmen de Jong 2024-12-15 08:12:34 +01:00
parent 9e8cc8b54d
commit 8f799567cf
53 changed files with 246 additions and 234 deletions

View File

@ -127,7 +127,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
str
}
is PtVariable -> {
val split = if(node.type.isSplitWordArray) "@split" else ""
val split = if(node.type.isSplitWordArray) "" else "@nosplit"
val align = when(node.align) {
0u -> ""
2u -> "@alignword"

View File

@ -27,7 +27,7 @@ class CompilationOptions(val output: OutputType,
var varsGolden: Boolean = false,
var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false,
var dontSplitWordArrays: Boolean = false,
var breakpointCpuInstruction: String? = null,
var ignoreFootguns: Boolean = false,
var outputDir: Path = Path(""),

View File

@ -117,19 +117,18 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
fun arrayFor(elementDt: BaseDataType, split: Boolean=false): DataType {
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
if(split) return DataType(BaseDataType.ARRAY_SPLITW, SubType.forDt(actualElementDt))
else return DataType(BaseDataType.ARRAY, SubType.forDt(actualElementDt))
return if(splitwordarray && elementDt.isWord)
DataType(BaseDataType.ARRAY_SPLITW, SubType.forDt(actualElementDt))
else
DataType(BaseDataType.ARRAY, SubType.forDt(actualElementDt))
}
}
fun elementToArray(split: Boolean = false): DataType {
if(split) {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, true)
else throw IllegalArgumentException("invalid split array elt dt")
}
return arrayFor(base)
fun elementToArray(splitwords: Boolean = true): DataType {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
else arrayFor(base, false)
}
fun elementType(): DataType =
@ -152,8 +151,8 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubSignedWord -> "@split word[]"
SubUnsignedWord -> "@split uword[]"
SubSignedWord -> "word[] (split)"
SubUnsignedWord -> "uword[] (split)"
else -> throw IllegalArgumentException("invalid sub type")
}
}
@ -172,18 +171,18 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
BaseDataType.ARRAY -> {
when(sub) {
SubUnsignedByte -> "ubyte["
SubUnsignedWord -> "uword["
SubUnsignedWord -> "@nosplit uword["
SubBool -> "bool["
SubSignedByte -> "byte["
SubSignedWord -> "word["
SubSignedWord -> "@nosplit word["
SubFloat -> "float["
null -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubUnsignedWord -> "@split uword["
SubSignedWord -> "@split word["
SubUnsignedWord -> "uword["
SubSignedWord -> "word["
else -> throw IllegalArgumentException("invalid sub type")
}
}

View File

@ -670,7 +670,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val msbAdd: Int
if(indexer.splitWords) {
val arrayVariable = indexer.variable
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE), arrayVariable.position)
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
indexer.children[0].parent = indexer
elementSize = 1
msbAdd = 0

View File

@ -1939,11 +1939,14 @@ $endLabel""")
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray")
}
dt.isWordArray && !dt.isSplitWordArray -> {
dt.isWordArray -> {
assignExpressionToVariable(containment.needle, "P8ZP_SCRATCH_W1", elementDt)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, null, null)
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray")
if(dt.isSplitWordArray)
asmgen.out(" jsr prog8_lib.containment_splitwordarray")
else
asmgen.out(" jsr prog8_lib.containment_wordarray")
}
else -> throw AssemblyError("invalid dt")
}

View File

@ -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.splitArray, decl.alignment, decl.dirty, 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)
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.splitArray, decl.alignment, decl.dirty, 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)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
@ -392,7 +392,7 @@ internal class ConstantIdentifierReplacer(
if(targetDatatype.isArray) {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOrUndef(),
ZeropageWish.DONTCARE, null, "dummy", emptyList(),
assignment.value, false, false, 0u, false, Position.DUMMY)
assignment.value, false, 0u, false, Position.DUMMY)
val replaceValue = createConstArrayInitializerValue(decl)
if(replaceValue!=null) {
return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment))
@ -447,7 +447,7 @@ internal class ConstantIdentifierReplacer(
if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
if(constRange!=null) {
return ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.FLOAT)),
return ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.FLOAT, false)),
constRange.map { NumericLiteral(BaseDataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}

View File

@ -42,7 +42,7 @@ anyall {
sub anyw(uword arrayptr, uword num_elements) -> bool {
; -- returns true if any word in the array is not zero.
; doesn't work on @split arrays.
; TODO FIX: doesn't work on @split arrays.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
repeat lsb(num_elements) {
@ -62,7 +62,7 @@ anyall {
sub allw(uword arrayptr, uword num_elements) -> bool {
; -- returns true if all words in the array are not zero.
; doesn't work on @split arrays.
; TODO FIX: doesn't work on @split arrays.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
repeat lsb(num_elements) {

View File

@ -188,7 +188,7 @@ c64 {
&ubyte SP7X = $d00e
&ubyte SP7Y = $d00f
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&uword[8] @nosplit SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&ubyte MSIGX = $d010
&ubyte SCROLY = $d011

View File

@ -189,7 +189,7 @@ c64 {
&ubyte SP7X = $d00e
&ubyte SP7Y = $d00f
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&uword[8] @nosplit SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&ubyte MSIGX = $d010
&ubyte SCROLY = $d011

View File

@ -174,7 +174,7 @@ psg {
}
ubyte[16] envelope_states
uword[16] @split envelope_volumes ; scaled by 256
uword[16] envelope_volumes ; scaled by 256
ubyte[16] envelope_attacks
ubyte[16] envelope_sustains
ubyte[16] envelope_releases

View File

@ -318,8 +318,8 @@ containment_bytearray .proc
.pend
containment_wordarray .proc
; -- check if a value exists in a word array.
; parameters: P8ZP_SCRATCH_W1: value to check, P8ZP_SCRATCH_W2: address of the word array, Y = length of array (>=1).
; -- check if a value exists in a linear word array.
; parameters: P8ZP_SCRATCH_W1: value to check, P8ZP_SCRATCH_W2: address of the word array, Y = number of elements in the array (>=1).
; returns boolean 0/1 in A.
dey
tya
@ -343,6 +343,34 @@ _found lda #1
rts
.pend
containment_splitwordarray .proc
; -- check if a value exists in a split lsb/msb word array. (Assuming lsb array comes first, immediately followed by msb array)
; parameters: P8ZP_SCRATCH_W1: value to check, P8ZP_SCRATCH_W2: start address of the lsb word array, Y = number of elements in the array (>=1).
; returns boolean 0/1 in A.
; TODO FIX THIS!
dey
tya
asl a
tay
- lda P8ZP_SCRATCH_W1
cmp (P8ZP_SCRATCH_W2),y
bne +
lda P8ZP_SCRATCH_W1+1
iny
cmp (P8ZP_SCRATCH_W2),y
beq _found
dey
+ dey
dey
cpy #254
bne -
lda #0
rts
_found lda #1
rts
.pend
arraycopy_split_to_normal_words .proc
; P8ZP_SCRATCH_W1 = start of lsb array

View File

@ -66,7 +66,7 @@ private fun compileMain(args: Array<String>): Boolean {
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
val dontSplitWordArrays by cli.option(ArgType.Boolean, fullName = "dontsplitarrays", description = "don't store any word array as split lsb/msb in memory, as if all of those have @nosplit")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")}) (required)")
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
@ -184,7 +184,7 @@ private fun compileMain(args: Array<String>): Boolean {
slabsHighBank,
slabsGolden == true,
compilationTarget!!,
splitWordArrays == true,
dontSplitWordArrays == true,
breakpointCpuInstruction,
printAst1 == true,
printAst2 == true,
@ -267,7 +267,7 @@ private fun compileMain(args: Array<String>): Boolean {
slabsHighBank,
slabsGolden == true,
compilationTarget!!,
splitWordArrays == true,
dontSplitWordArrays == true,
breakpointCpuInstruction,
printAst1 == true,
printAst2 == true,

View File

@ -48,7 +48,7 @@ class CompilerArguments(val filepath: Path,
val slabsHighBank: Int?,
val slabsGolden: Boolean,
val compilationTarget: String,
val splitWordArrays: Boolean,
val dontSplitWordArrays: Boolean,
val breakpointCpuInstruction: String?,
val printAst1: Boolean,
val printAst2: Boolean,
@ -87,7 +87,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
varsGolden = args.varsGolden
slabsHighBank = args.slabsHighBank
slabsGolden = args.slabsGolden
splitWordArrays = args.splitWordArrays
dontSplitWordArrays = args.dontSplitWordArrays
outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs
}

View File

@ -703,7 +703,7 @@ internal class AstChecker(private val program: Program,
if (variable!=null) {
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
errors.err("invalid pointer-of operand type",addressOf.position)
if (variable.splitArray)
if (variable.datatype.isSplitWordArray)
errors.err("cannot take address of split word array",addressOf.position)
}
super.visit(addressOf)
@ -888,8 +888,8 @@ internal class AstChecker(private val program: Program,
}
}
if(decl.splitArray && decl.type==VarDeclType.MEMORY)
err("@split can't be used on memory mapped arrays")
if(decl.datatype.isSplitWordArray && decl.type==VarDeclType.MEMORY)
err("memory mapped word arrays cannot be split, should have @nosplit")
}
if(decl.datatype.isString) {
@ -911,7 +911,7 @@ internal class AstChecker(private val program: Program,
if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE)
err("zeropage usage has been disabled by options")
if(decl.splitArray) {
if(decl.datatype.isSplitWordArray) {
if (!decl.datatype.isWordArray) {
errors.err("split can only be used on word arrays", decl.position)
}
@ -1044,14 +1044,14 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty())
err("missing option directive argument(s)")
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "merge", "verafxmuls", "splitarrays", "no_symbol_prefixing", "ignore_unused")}.any { !it })
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "merge", "verafxmuls", "no_symbol_prefixing", "ignore_unused")}.any { !it })
err("invalid option directive argument(s)")
if(directive.parent is Block) {
if(directive.args.any {it.name !in arrayOf("force_output", "merge", "verafxmuls", "splitarrays", "no_symbol_prefixing", "ignore_unused")})
if(directive.args.any {it.name !in arrayOf("force_output", "merge", "verafxmuls", "no_symbol_prefixing", "ignore_unused")})
err("using an option that is not valid for blocks")
}
if(directive.parent is Module) {
if(directive.args.any {it.name !in arrayOf("enable_floats", "no_sysinit", "splitarrays", "no_symbol_prefixing", "ignore_unused")})
if(directive.args.any {it.name !in arrayOf("enable_floats", "no_sysinit", "no_symbol_prefixing", "ignore_unused")})
err("using an option that is not valid for modules")
}
if(directive.args.any { it.name=="verafxmuls" } && compilerOptions.compTarget.name != Cx16Target.NAME)

View File

@ -112,8 +112,8 @@ class AstPreprocessor(val program: Program,
val replacements = mutableListOf<IAstModification>()
for(decl in vars) {
if(shouldSplitArray(decl))
continue // splitting must be done first
if(shouldUnSplitArray(decl))
continue // unsplitting must be done first
if(decl.type != VarDeclType.VAR) {
movements.add(IAstModification.InsertFirst(decl, parentscope))
replacements.add(IAstModification.Remove(decl, scope))
@ -181,28 +181,21 @@ class AstPreprocessor(val program: Program,
}
}
if(shouldSplitArray(decl)) {
return makeSplitArray(decl)
}
if(decl.datatype.isWordArray) {
if ("splitarrays" in decl.definingBlock.options())
return makeSplitArray(decl)
if ("splitarrays" in decl.definingModule.options())
return makeSplitArray(decl)
if(shouldUnSplitArray(decl)) {
return makeUnSplitArray(decl)
}
return noModifications
}
private fun shouldSplitArray(decl: VarDecl): Boolean =
options.splitWordArrays && (decl.datatype.isWordArray && !decl.datatype.isSplitWordArray) && !decl.definingBlock.isInLibrary
private fun shouldUnSplitArray(decl: VarDecl): Boolean =
options.dontSplitWordArrays && decl.datatype.isSplitWordArray
private fun makeSplitArray(decl: VarDecl): Iterable<IAstModification> {
val splitDt = DataType.arrayFor(decl.datatype.sub!!.dt, true)
private fun makeUnSplitArray(decl: VarDecl): Iterable<IAstModification> {
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.value?.copy(), decl.sharedWithAsm, true, decl.alignment, false, decl.position
decl.value?.copy(), decl.sharedWithAsm, decl.alignment, false, decl.position
)
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))
}

View File

@ -202,7 +202,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
"no_symbol_prefixing" -> noSymbolPrefixing = true
"ignore_unused" -> ignoreUnused = true
"force_output" -> forceOutput = true
"merge", "splitarrays" -> { /* ignore this one */ }
"merge" -> { /* ignore this one */ }
"verafxmuls" -> veraFxMuls = true
else -> throw FatalAstException("weird directive option: ${arg.name}")
}

View File

@ -66,7 +66,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
// turn the array literal it into an identifier reference
val litval2 = array.cast(targetDt.getOrUndef())
if (litval2 != null) {
val vardecl2 = VarDecl.createAuto(litval2, targetDt.getOrUndef().isSplitWordArray)
val vardecl2 = VarDecl.createAuto(litval2)
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
return listOf(
IAstModification.ReplaceNode(array, identifier, parent),
@ -81,7 +81,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
val mods = mutableListOf<IAstModification>()
for(elt in array.value.filterIsInstance<IdentifierReference>()) {
val decl = elt.targetVarDecl(program)
if(decl!=null && decl.splitArray) {
if(decl!=null && decl.datatype.isSplitWordArray) {
// you can't take the adress of a split-word array.
// instead of a fatal error, we give a warning and turn it back into a regular array.
errors.warn("cannot take address of split word array - the array is turned back into a regular word array", decl.position)
@ -158,7 +158,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
val variable=addressOf.identifier.targetVarDecl(program)
if (variable!=null) {
if (variable.splitArray) {
if (variable.datatype.isSplitWordArray) {
// you can't take the adress of a split-word array.
// instead of giving a fatal error, we remove the
// instead of a fatal error, we give a warning and turn it back into a regular array.
@ -174,7 +174,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
val normalDt = DataType.arrayFor(variable.datatype.sub!!.dt, false)
return VarDecl(
variable.type, variable.origin, normalDt, variable.zeropage, variable.arraysize, variable.name, emptyList(),
variable.value?.copy(), variable.sharedWithAsm, false, variable.alignment, variable.dirty, variable.position
variable.value?.copy(), variable.sharedWithAsm, variable.alignment, variable.dirty, variable.position
)
}

View File

@ -222,7 +222,6 @@ internal class StatementReorderer(
emptyList(),
null,
it.sharedWithAsm,
it.splitArray,
it.alignment,
it.dirty,
it.position

View File

@ -15,7 +15,7 @@ import prog8.code.core.*
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
val inheritOptions = block.definingModule.options() intersect setOf("splitarrays", "no_symbol_prefixing", "ignore_unused") subtract block.options()
val inheritOptions = block.definingModule.options() intersect setOf("no_symbol_prefixing", "ignore_unused") subtract block.options()
if(inheritOptions.isNotEmpty()) {
val directive = Directive("%option", inheritOptions.map{ DirectiveArg(null, it, null, block.position) }, block.position)
return listOf(IAstModification.InsertFirst(directive, block))
@ -223,7 +223,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
// replace x==1 or x==2 or x==3 with a containment check x in [1,2,3]
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
val arrayType = DataType.arrayFor(elementType.base)
val arrayType = DataType.arrayFor(elementType.base, true)
val valuesArray = ArrayLiteral(InferredTypes.InferredType.known(arrayType), valueCopies.toTypedArray(), expr.position)
val containment = ContainmentCheck(needle, valuesArray, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent))

View File

@ -39,7 +39,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
slabsHighBank = null,
slabsGolden = false,
compilationTarget = target.name,
splitWordArrays = false,
dontSplitWordArrays = false,
breakpointCpuInstruction = null,
printAst1 = false,
printAst2 = false,

View File

@ -339,6 +339,7 @@ class TestCompilerOnRanges: FunSpec({
str name = "irmen"
ubyte[] values = [1,2,3,4,5,6,7]
uword[] wvalues = [1000,2000,3000]
uword[] @nosplit wnsvalues = [1000,2000,3000]
if 'm' in name {
xx++
@ -376,6 +377,10 @@ class TestCompilerOnRanges: FunSpec({
xx++
}
if ww in wnsvalues {
xx++
}
if xx in 10 to 20 {
xx++
}
@ -387,7 +392,7 @@ class TestCompilerOnRanges: FunSpec({
}""", writeAssembly = true) shouldNotBe null
}
test("containment check in expressions") {
test("containment check expressions") {
compileText(
C64Target(), false, """
main {
@ -397,6 +402,7 @@ class TestCompilerOnRanges: FunSpec({
str name = "irmen"
ubyte[] values = [1,2,3,4,5,6,7]
uword[] wvalues = [1000,2000,3000]
uword[] @nosplit wnsvalues = [1000,2000,3000]
xx = 'm' in name
xx = 5 in values
@ -408,6 +414,7 @@ class TestCompilerOnRanges: FunSpec({
xx = xx in [2,4,6,8]
xx = ww in [9000,8000,7000]
xx = ww in wvalues
xx = ww in wnsvalues
}
}""", writeAssembly = true) shouldNotBe null
}

View File

@ -37,7 +37,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
slabsHighBank = null,
slabsGolden = false,
compilationTarget = Cx16Target.NAME,
splitWordArrays = false,
dontSplitWordArrays = false,
breakpointCpuInstruction = null,
printAst1 = false,
printAst2 = false,

View File

@ -111,7 +111,7 @@ 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, false, 0u, false, Position.DUMMY)
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 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 +149,7 @@ 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, false, 0u, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.BYTE), ZeropageWish.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 +161,7 @@ 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, false, 0u, false, Position.DUMMY)
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 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 +173,7 @@ 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, false, 0u, false, Position.DUMMY)
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 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 +184,7 @@ 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, false, 0u, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.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 +197,7 @@ 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, false, 0u, false, Position.DUMMY)
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 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 +210,7 @@ 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, false, 0u, false, Position.DUMMY)
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 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)

View File

@ -261,9 +261,9 @@ main {
val src="""
main {
sub start() {
&uword[30] wb = ${'$'}2000
&uword[100] array1 = ${'$'}9e00
&uword[30] array2 = &array1[len(wb)]
&uword[30] @nosplit wb = ${'$'}2000
&uword[100] @nosplit array1 = ${'$'}9e00
&uword[30] @nosplit array2 = &array1[len(wb)]
cx16.r0 = &array1 ; ${'$'}9e00
cx16.r1 = &array1[len(wb)] ; ${'$'}9e3c
@ -291,7 +291,7 @@ main {
cx16.r0 = &array
const uword HIGH_MEMORY_START = 40960
&uword[20] @shared wa = HIGH_MEMORY_START
&uword[20] @shared @nosplit wa = HIGH_MEMORY_START
}
}"""
val result = compileText(Cx16Target(), optimize=false, src, writeAssembly=true)!!

View File

@ -87,32 +87,6 @@ main {
}"""
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
}
test("split arrays back to normal when address is taken") {
val src="""
main {
sub start() {
cx16.r0L=0
if cx16.r0L==0 {
uword[] addresses = [scores2, start]
uword[] @split scores1 = [10, 25, 50, 100]
uword[] @split scores2 = [100, 250, 500, 1000]
cx16.r0 = &scores1
cx16.r1 = &scores2
cx16.r2 = &addresses
}
}
}"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target(), optimize=false, src, writeAssembly=true, errors=errors) shouldNotBe null
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 2
errors.warnings[0] shouldContain("address")
errors.warnings[1] shouldContain("address")
errors.warnings[0] shouldContain("split")
errors.warnings[1] shouldContain("split")
}
}
context("alias") {
@ -911,16 +885,12 @@ main {
shouldThrow<NoSuchElementException> {
DataType.arrayFor(BaseDataType.UNDEFINED)
}
shouldThrow<IllegalArgumentException> {
DataType.arrayFor(BaseDataType.UBYTE, true)
}
DataType.arrayFor(BaseDataType.UBYTE, true).isUnsignedByteArray shouldBe true
DataType.arrayFor(BaseDataType.FLOAT).isFloatArray shouldBe true
DataType.arrayFor(BaseDataType.UWORD).isUnsignedWordArray shouldBe true
DataType.arrayFor(BaseDataType.UWORD).isArray shouldBe true
DataType.arrayFor(BaseDataType.UWORD).isSplitWordArray shouldBe false
DataType.arrayFor(BaseDataType.UWORD, true).isArray shouldBe true
DataType.arrayFor(BaseDataType.UWORD, true).isSplitWordArray shouldBe true
DataType.arrayFor(BaseDataType.UWORD).isSplitWordArray shouldBe true
DataType.arrayFor(BaseDataType.UWORD, false).isSplitWordArray shouldBe false
}
test("array of strings becomes array of uword pointers") {

View File

@ -98,8 +98,8 @@ main {
test("split only for word arrays") {
val srcGood = """
main {
uword[10] @split sw
word[10] @split sw2
uword[10] @nosplit sw
word[10] @nosplit sw2
sub start() {
}
@ -108,7 +108,7 @@ main {
val srcWrong1 = """
main {
ubyte[10] @split sb
ubyte[10] @nosplit sb
sub start() {
}
@ -116,12 +116,12 @@ main {
val errors = ErrorReporterForTests()
compileText(C64Target(), false, srcWrong1, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "split can only be used on word arrays"
errors.errors[0] shouldContain "nosplit can only be used on word arrays"
val srcWrong2 = """
%option enable_floats
main {
float[10] @split sf
float[10] @nosplit sf
sub start() {
}
@ -129,15 +129,15 @@ main {
errors.clear()
compileText(C64Target(), false, srcWrong2, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "split can only be used on word arrays"
errors.errors[0] shouldContain "nosplit can only be used on word arrays"
}
test("split word arrays in asm as lsb/msb") {
test("split word arrays in asm as lsb/msb, nosplit as single linear") {
val text = """
main {
uword[10] @split @shared uw
word[10] @split @shared sw
uword[10] @shared normal
uword[10] @shared uw
word[10] @shared sw
uword[10] @shared @nosplit normal
sub start() {
%asm {{
@ -170,7 +170,8 @@ main {
val text = """
main {
sub start() {
uword[3] @zp @split @shared thearray
uword[3] @zp @shared thearray
uword[3] @zp @nosplit @shared thearray2
}
}"""
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
@ -178,6 +179,7 @@ main {
val assembly = assemblyFile.readText()
assembly shouldContain "thearray_lsb"
assembly shouldContain "thearray_msb"
assembly shouldContain "thearray2"
}
test("indexing str or pointervar with expression") {
@ -419,5 +421,31 @@ main {
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
}
test("taking address of split arrays") {
val src="""
main {
sub start() {
cx16.r0L=0
if cx16.r0L==0 {
uword[] addresses = [scores2, start]
uword[] scores1 = [10, 25, 50, 100]
uword[] scores2 = [100, 250, 500, 1000]
cx16.r0 = &scores1
cx16.r1 = &scores2
cx16.r2 = &addresses
}
}
}"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target(), optimize=false, src, writeAssembly=true, errors=errors) shouldNotBe null
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 2
errors.warnings[0] shouldContain("address")
errors.warnings[1] shouldContain("address")
errors.warnings[0] shouldContain("split")
errors.warnings[1] shouldContain("split")
}
})

View File

@ -46,8 +46,8 @@ 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, false, 0u, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.forDt(BaseDataType.UWORD), ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, false, 0u, false, Position.DUMMY)
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 labelInSub = Label("locallabel", Position.DUMMY)
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, false, Position.DUMMY)
@ -63,7 +63,7 @@ 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, false, 0u, false, 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 block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))

View File

@ -39,7 +39,7 @@ internal fun compileFile(
symbolDefs = emptyMap(),
outputDir = outputDir,
errors = errors ?: ErrorReporterForTests(),
splitWordArrays = false,
dontSplitWordArrays = false,
breakpointCpuInstruction = null,
printAst1 = false,
printAst2 = false,

View File

@ -88,7 +88,7 @@ class Program(val name: String,
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,
sharedWithAsm = false, splitArray = false, alignment = 0u, dirty = false, position = string.position
sharedWithAsm = false, alignment = 0u, dirty = false, position = string.position
)
internedStringsBlock.statements.add(decl)
decl.linkParents(internedStringsBlock)

View File

@ -758,20 +758,14 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME()
val name = if(identifiers.size==1) identifiername.text else "<multiple>"
val isArray = ARRAYSIG() != null || arrayindex() != null
val split = options.SPLIT().isNotEmpty()
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 origDt = DataType.forDt(baseDt)
val dt = if(isArray) {
if(split && origDt.isWord)
origDt.elementToArray(split)
else
origDt.elementToArray(false) // type error will be generated later in the ast check
} else origDt
val dt = if(isArray) DataType.arrayFor(baseDt, nosplit!=true) else DataType.forDt(baseDt)
return VarDecl(
type, VarDeclOrigin.USERCODE,
@ -785,7 +779,6 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
},
value,
options.SHARED().isNotEmpty(),
split,
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
options.DIRTY().isNotEmpty(),
toPosition()

View File

@ -91,7 +91,7 @@ object InferredTypes {
}
}
type.isArray -> {
InferredType.known(DataType.arrayFor(type.sub!!.dt))
InferredType.known(DataType.arrayFor(type.sub!!.dt, false))
}
else -> throw IllegalArgumentException("invalid type")
}

View File

@ -247,7 +247,6 @@ class VarDecl(val type: VarDeclType,
val names: List<String>,
var value: Expression?,
val sharedWithAsm: Boolean,
val splitArray: Boolean,
val alignment: UInt,
val dirty: Boolean,
override val position: Position) : Statement(), INamedStatement {
@ -273,27 +272,21 @@ class VarDecl(val type: VarDeclType,
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,
sharedWithAsm = false,
splitArray = false,
alignment = 0u,
dirty = false,
position = param.position
)
}
fun createAuto(array: ArrayLiteral, splitArray: Boolean): VarDecl {
fun createAuto(array: ArrayLiteral): VarDecl {
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array,
sharedWithAsm = false, splitArray = splitArray, alignment = 0u, dirty = false, position = array.position)
sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position)
}
}
init {
if(datatype.isSplitWordArray)
require(splitArray)
}
val isArray: Boolean
get() = datatype.isArray
@ -329,7 +322,7 @@ class VarDecl(val type: VarDeclType,
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(),
sharedWithAsm, splitArray, alignment, dirty, position)
sharedWithAsm, alignment, dirty, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
return copy
}
@ -344,19 +337,19 @@ class VarDecl(val type: VarDeclType,
// 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(),
sharedWithAsm, splitArray, alignment, dirty, position)
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(),
sharedWithAsm, splitArray, alignment, dirty, position)
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(),
sharedWithAsm, splitArray, alignment, dirty, position)
sharedWithAsm, alignment, dirty, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
copy
}

View File

@ -156,6 +156,10 @@ One or more .p8 module files
Add this user-defined symbol directly to the beginning of the generated assembly file.
Can be repeated to define multiple symbols.
``-dontsplitarrays``
Treat all word arrays as tagged with @nosplit so they are all stored linearly in memory,
instead of splitting them in separate lsb and msb arrays.
``-dumpsymbols``
print a dump of the variable declarations and subroutine signatures
@ -219,11 +223,6 @@ One or more .p8 module files
mixed in between the actual generated assembly code.
This can be useful for debugging purposes to see what assembly was generated for what prog8 source code.
``-splitarrays``
Treat all word arrays as tagged with @split so they are all lsb/msb split into memory.
This removes the need to add @split yourself but some programs may fail to compile with
this option as not all array operations are implemented yet on split arrays.
``-srcdirs <pathlist>``
Specify a list of extra paths (separated with ':'), to search in for imported modules.
Useful if you have library modules somewhere that you want to re-use,

View File

@ -403,7 +403,6 @@ Directives
Overriding (monkeypatching) happens only if the signature of the subroutine exactly matches the original subroutine, including the exact names and types of the parameters.
Where blocks with this option are merged into is intricate: it looks for the first other block with the same name that does not have %option merge,
if that can't be found, select the first occurrence regardless. If no other blocks are found, no merge is done. Blocks in libraries are considered first to merge into.
- ``splitarrays`` (block or module) makes all word-arrays in this scope lsb/msb split arrays (as if they all have the @split tag). See Arrays.
- ``no_symbol_prefixing`` (block or module) makes the compiler *not* use symbol-prefixing when translating prog8 code into assembly.
Only use this if you know what you're doing because it could result in invalid assembly code being generated.
This option can be useful when writing library modules that you don't want to be exposing prefixed assembly symbols.

View File

@ -1,9 +1,15 @@
TODO
====
- make word arrays split by default (remove @split tag) and use new @nosplit tag to make an array use the old storage format? Also invert -splitarrays command line option.
- add &< and &> operators to get the address of the lsb-array and msb-array, respectively. Regular & will just return the start of the split array in memory whatever byte comes first.
- DONE: make word arrays split by default (remove @split tag) and use new @nosplit tag to make an array use the old storage format? Also invert -splitarrays command line option.
- DONE: remove "splitarrays" %option switch
- Regular & will just return the start of the split array in memory whatever byte comes first.
- add &< and &> operators to get the address of the lsb-array and msb-array, respectively.
- fix containment_splitwordarray
- fix sprites.pos_batch
- fix anyall.anyw/allw
- update Syntax files + Document all of this (also that word arrays can then have length 256 by default as well, and that @linear will reduce it to half.)
- test all examples
...

View File

@ -61,7 +61,7 @@ Tag Effect
@requirezp force the variable into Zero page. If ZP is full, compilation will fail.
@nozp force the variable to normal system ram, never place it into zeropage.
@shared means the variable is shared with some assembly code and that it cannot be optimized away if not used elsewhere.
@split (only valid on (u)word arrays) Makes the array to be placed in memory as 2 separate byte arrays; one with the LSBs one with the MSBs of the word values. Usually improves performance and code size.
@nosplit (only valid on (u)word arrays) Store the array as a single inear array instead of a separate array for lsb and msb values
@alignword aligns string or array variable on an even memory address
@align64 aligns string or array variable on a 64 byte address interval (example: for C64 sprite data)
@alignpage aligns string or array variable on a 256 byte address interval (example: to avoid page boundaries)

View File

@ -71,7 +71,7 @@ sub print_notes(ubyte n1, ubyte n2) {
]
uword[] @split music_freq_table = [
uword[] music_freq_table = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
732, 778, 826, 876, 928, 978, 1042, 1100, 1170, 1238, 1312, 1390, 1464, 1556,
1652, 1752, 1856, 1956, 2084, 2200, 2340, 2476, 2624, 2780, 2928, 3112, 3304,

View File

@ -8,14 +8,14 @@ main {
const uword height = 200
; vertices
word[] @split xcoor = [ -100, -100, -100, -100, 100, 100, 100, 100 ]
word[] @split ycoor = [ -100, -100, 100, 100, -100, -100, 100, 100 ]
word[] @split zcoor = [ -100, 100, -100, 100, -100, 100, -100, 100 ]
word[] xcoor = [ -100, -100, -100, -100, 100, 100, 100, 100 ]
word[] ycoor = [ -100, -100, 100, 100, -100, -100, 100, 100 ]
word[] zcoor = [ -100, 100, -100, 100, -100, 100, -100, 100 ]
; storage for rotated coordinates
word[len(xcoor)] @split rotatedx
word[len(ycoor)] @split rotatedy
word[len(zcoor)] @split rotatedz
word[len(xcoor)] rotatedx
word[len(ycoor)] rotatedy
word[len(zcoor)] rotatedz
sub start() {

View File

@ -6,14 +6,14 @@
main {
; vertices
word[] @split xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] @split ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] @split zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
; storage for rotated coordinates
word[len(xcoor)] @split rotatedx
word[len(ycoor)] @split rotatedy
word[len(zcoor)] @split rotatedz
word[len(xcoor)] rotatedx
word[len(ycoor)] rotatedy
word[len(zcoor)] rotatedz
sub start() {

View File

@ -7,14 +7,14 @@
main {
; vertices
word[] @split xcoor = [ -100, -100, -100, -100, 100, 100, 100, 100 ]
word[] @split ycoor = [ -100, -100, 100, 100, -100, -100, 100, 100 ]
word[] @split zcoor = [ -100, 100, -100, 100, -100, 100, -100, 100 ]
word[] xcoor = [ -100, -100, -100, -100, 100, 100, 100, 100 ]
word[] ycoor = [ -100, -100, 100, 100, -100, -100, 100, 100 ]
word[] zcoor = [ -100, 100, -100, 100, -100, 100, -100, 100 ]
; storage for rotated coordinates
word[len(xcoor)] @split rotatedx
word[len(ycoor)] @split rotatedy
word[len(zcoor)] @split rotatedz
word[len(xcoor)] rotatedx
word[len(ycoor)] rotatedy
word[len(zcoor)] rotatedz
; edges
ubyte[] edgesFrom = [ 0, 2, 6, 4, 1, 3, 7, 5, 0, 2, 6, 4]

View File

@ -115,7 +115,7 @@ main {
$3532, $322e, $2e29, $2926, $2730, $242c, $2027, $1420
]
uword[] @split vera_freqs = [
uword[] vera_freqs = [
0,0,0,0,0,0,0,0,0,0, ; first 10 notes are not used
120, 127, 135, 143, 152, 160, 170, 180, 191, 203,
215, 227, 240, 255, 270, 287, 304, 320, 341, 360,

View File

@ -5,8 +5,8 @@
main {
const ubyte MAX_NUM_CIRCLES = 80
const ubyte GROWTH_RATE = 2
uword[MAX_NUM_CIRCLES] @split circle_x
uword[MAX_NUM_CIRCLES] @split circle_y
uword[MAX_NUM_CIRCLES] circle_x
uword[MAX_NUM_CIRCLES] circle_y
ubyte[MAX_NUM_CIRCLES] circle_radius
ubyte num_circles = 0
ubyte background_color

View File

@ -165,9 +165,9 @@ matrix_math {
%option verafxmuls ; accellerate all word-multiplications in this block using Vera FX hardware muls
; storage for rotated coordinates
word[shipdata.totalNumberOfPoints] @split rotatedx
word[shipdata.totalNumberOfPoints] @split rotatedy
word[shipdata.totalNumberOfPoints] @split rotatedz
word[shipdata.totalNumberOfPoints] rotatedx
word[shipdata.totalNumberOfPoints] rotatedy
word[shipdata.totalNumberOfPoints] rotatedz
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
; rotate around origin (0,0,0)
@ -230,9 +230,9 @@ const ubyte totalNumberOfFaces = 22
const ubyte totalNumberOfPoints = 34
str shipName = "cobra-mk3"
; vertices
word[totalNumberOfPoints] @split xcoor = [ 32,-32,0,-120,120,-88,88,128,-128,0,-32,32,-36,-8,8,36,36,8,-8,-36,-1,-1,-80,-80,-88,80,88,80,1,1,1,1,-1,-1 ]
word[totalNumberOfPoints] @split ycoor = [ 0,0,26,-3,-3,16,16,-8,-8,26,-24,-24,8,12,12,8,-12,-16,-16,-12,-1,-1,-6,6,0,6,0,-6,-1,-1,1,1,1,1 ]
word[totalNumberOfPoints] @split zcoor = [ 76,76,24,-8,-8,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,76,90,-40,-40,-40,-40,-40,-40,76,90,76,90,76,90 ]
word[totalNumberOfPoints] xcoor = [ 32,-32,0,-120,120,-88,88,128,-128,0,-32,32,-36,-8,8,36,36,8,-8,-36,-1,-1,-80,-80,-88,80,88,80,1,1,1,1,-1,-1 ]
word[totalNumberOfPoints] ycoor = [ 0,0,26,-3,-3,16,16,-8,-8,26,-24,-24,8,12,12,8,-12,-16,-16,-12,-1,-1,-6,6,0,6,0,-6,-1,-1,1,1,1,1 ]
word[totalNumberOfPoints] zcoor = [ 76,76,24,-8,-8,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,76,90,-40,-40,-40,-40,-40,-40,76,90,76,90,76,90 ]
; edges and faces
ubyte[totalNumberOfEdges] edgesFrom = [ 0,1,0,10,1,0,2,0,4,0,4,7,2,1,1,3,8,3,2,5,6,5,6,16,15,14,14,18,13,12,12,26,25,25,22,23,22,20,28,21,20,28,29,30,31,30,32,20,21,20,20 ]
ubyte[totalNumberOfEdges] edgesTo = [ 1,2,2,11,10,11,6,6,6,4,7,11,5,5,3,5,10,8,9,9,9,8,7,17,16,15,17,19,18,13,19,27,26,27,23,24,24,28,29,29,21,30,31,31,33,32,33,32,33,33,29 ]

View File

@ -58,14 +58,14 @@ matrix_math {
%option verafxmuls ; accellerate all word-multiplications in this block using Vera FX hardware muls
; vertices
word[] @split xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] @split ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] @split zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
; storage for rotated coordinates
word[len(xcoor)] @split rotatedx
word[len(ycoor)] @split rotatedy
word[len(zcoor)] @split rotatedz
word[len(xcoor)] rotatedx
word[len(ycoor)] rotatedy
word[len(zcoor)] rotatedz
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
; rotate around origin (0,0,0)

View File

@ -29,7 +29,7 @@ adpcm {
byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
uword[] @split t_step = [
uword[] t_step = [
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,

View File

@ -316,8 +316,8 @@ main {
cx16.VERA_DATA0 = msb(fill_rgb)
}
uword[22] @split outline_color = [$090,$190,$290,$390,$490,$590,$690,$790,$990,$980,$970,$960,$950,$940,$a30,$a00,$a00,$a00,$a00,$a00, $111, $148]
uword[22] @split fill_color = [$0f0,$2f0,$4f0,$6f0,$8f0,$af0,$cf0,$ef0,$ff0,$fe0,$fc0,$fa0,$f80,$f60,$f40,$f00,$f01,$f02,$f03,$f04, $000, $28f]
uword[22] outline_color = [$090,$190,$290,$390,$490,$590,$690,$790,$990,$980,$970,$960,$950,$940,$a30,$a00,$a00,$a00,$a00,$a00, $111, $148]
uword[22] fill_color = [$0f0,$2f0,$4f0,$6f0,$8f0,$af0,$cf0,$ef0,$ff0,$fe0,$fc0,$fa0,$f80,$f60,$f40,$f00,$f01,$f02,$f03,$f04, $000, $28f]
}
}

View File

@ -5,9 +5,9 @@ main {
sub start() {
gfx_lores.graphics_mode()
uword[128] @split flakes1_xx
uword[128] flakes1_xx
ubyte[128] flakes1_yy
uword[128] @split flakes2_xx
uword[128] flakes2_xx
ubyte[128] flakes2_yy
ubyte @zp idx

View File

@ -18,10 +18,10 @@ main {
const ubyte CIRCLE_SKIP = 256/NUM_STARS
ubyte[NUM_STARS] color
uword[NUM_STARS] @split radius
uword[NUM_STARS] @split accel
uword[NUM_STARS] @split speed
uword[NUM_STARS] @split prev_x
uword[NUM_STARS] radius
uword[NUM_STARS] accel
uword[NUM_STARS] speed
uword[NUM_STARS] prev_x
ubyte[NUM_STARS] prev_y
gfx_lores.graphics_mode()

View File

@ -1,18 +1,15 @@
%import textio
%zeropage basicsafe
main {
uword[] array = [1000, 2000, 9000, 8000, 5000]
sub start() {
goto $3000
goto labeltje
goto cx16.r0
goto cx16.r0+cx16.r1
if cx16.r0==0
goto cx16.r0+cx16.r1
if cx16.r0>2000
goto cx16.r0+cx16.r1
labeltje:
txt.print_bool(1000 in array)
txt.spc()
txt.print_bool(9000 in array)
txt.spc()
txt.print_bool(5000 in array)
txt.spc()
txt.print_bool(9001 in array)
}
}

View File

@ -5,8 +5,8 @@
main {
sub start() {
word[128] @split particleX
word[128] @split particleY
word[128] particleX
word[128] particleY
byte[128] particleDX
byte[128] particleDY

View File

@ -289,7 +289,7 @@ class IRFileReader {
val (name, size, align) = match.destructured
val dummyNode = PtVariable(
name,
DataType.arrayFor(BaseDataType.UBYTE),
DataType.arrayFor(BaseDataType.UBYTE, false),
ZeropageWish.NOT_IN_ZEROPAGE,
0u,
null,
@ -514,14 +514,12 @@ class IRFileReader {
private fun parseDatatype(type: String, isArray: Boolean): DataType {
if(isArray) {
return when(type) {
"bool" -> DataType.arrayFor(BaseDataType.BOOL)
"byte" -> DataType.arrayFor(BaseDataType.BYTE)
"ubyte", "str" -> DataType.arrayFor(BaseDataType.UBYTE)
"word" -> DataType.arrayFor(BaseDataType.WORD)
"uword" -> DataType.arrayFor(BaseDataType.UWORD)
"float" -> DataType.arrayFor(BaseDataType.FLOAT)
"uword_split" -> DataType.arrayFor(BaseDataType.UWORD, true)
"word_split" -> DataType.arrayFor(BaseDataType.WORD, true)
"bool" -> DataType.arrayFor(BaseDataType.BOOL, false)
"byte" -> DataType.arrayFor(BaseDataType.BYTE, false)
"ubyte", "str" -> DataType.arrayFor(BaseDataType.UBYTE, false)
"word" -> DataType.arrayFor(BaseDataType.WORD, false)
"uword" -> DataType.arrayFor(BaseDataType.UWORD, false)
"float" -> DataType.arrayFor(BaseDataType.FLOAT, false)
else -> throw IRParseException("invalid dt $type")
}
} else {

View File

@ -30,8 +30,8 @@ fun DataType.typeString(length: Int?): String {
}
BaseDataType.ARRAY_SPLITW -> {
when(this.sub?.dt) {
BaseDataType.UWORD -> "@split uword[$lengthStr]" // should be 2 separate byte arrays by now really?
BaseDataType.WORD -> "@split word[$lengthStr]" // should be 2 separate byte arrays by now really?
BaseDataType.UWORD -> "uword[$lengthStr]" // should be 2 separate byte arrays by now really?
BaseDataType.WORD -> "word[$lengthStr]" // should be 2 separate byte arrays by now really?
else -> throw IllegalArgumentException("invalid sub type")
}
}

View File

@ -61,7 +61,7 @@ ZEROPAGENOT: '@nozp' ;
SHARED : '@shared' ;
SPLIT: '@split' ;
NOSPLIT: '@nosplit' ;
ALIGNWORD: '@alignword' ;
@ -159,7 +159,7 @@ directivearg : stringliteral | identifier | integerliteral ;
vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ;
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | SPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | DIRTY)* ;
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | NOSPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | DIRTY)* ;
varinitializer : vardecl '=' expression ;