From 80d88b3c615232293342228693913f46308a60a8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 15 Dec 2024 15:55:48 +0100 Subject: [PATCH] fix many split array issues --- benchmark-program/b_3d.p8 | 12 ++--- benchmark-program/b_adpcm.p8 | 6 +-- benchmark-program/b_circles.p8 | 4 +- benchmark-program/b_textelite.p8 | 15 +++--- codeCore/src/prog8/code/SymbolTableMaker.kt | 5 +- codeCore/src/prog8/code/core/Enumerations.kt | 12 ++--- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 26 ++++++---- .../src/prog8/codegen/cpu6502/IfElseAsmGen.kt | 26 +++++++--- .../cpu6502/assignment/AssignmentAsmGen.kt | 9 +++- .../codegen/intermediate/ExpressionGen.kt | 6 ++- compiler/res/prog8lib/anyall.p8 | 4 +- compiler/res/prog8lib/cx16/sprites.p8 | 2 +- .../compiler/astprocessing/AstChecker.kt | 6 +-- .../astprocessing/IntermediateAstMaker.kt | 10 +++- .../astprocessing/LiteralsToAutoVars.kt | 23 --------- .../test/codegeneration/TestArrayThings.kt | 8 +--- compiler/test/vm/TestCompilerVirtual.kt | 47 ++++++++++++++++++- .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 2 + .../src/prog8/ast/statements/AstStatements.kt | 10 +++- docs/source/todo.rst | 10 ++-- examples/cx16/pcmaudio/play-adpcm.p8 | 8 ++-- examples/cx16/pcmaudio/stream-wav.p8 | 8 ++-- examples/test.p8 | 12 ++--- .../src/prog8/intermediate/IRSymbolTable.kt | 2 +- parser/src/main/antlr/Prog8ANTLR.g4 | 4 +- 25 files changed, 172 insertions(+), 105 deletions(-) diff --git a/benchmark-program/b_3d.p8 b/benchmark-program/b_3d.p8 index 1a4dd5937..d8ddfeec3 100644 --- a/benchmark-program/b_3d.p8 +++ b/benchmark-program/b_3d.p8 @@ -64,14 +64,14 @@ rotate3d { matrix_math { ; 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) diff --git a/benchmark-program/b_adpcm.p8 b/benchmark-program/b_adpcm.p8 index 38b6bd5c5..3c15cb087 100644 --- a/benchmark-program/b_adpcm.p8 +++ b/benchmark-program/b_adpcm.p8 @@ -25,8 +25,8 @@ adpcm { ; IMA ADPCM decoder. Supports mono and stereo streams. - ubyte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8] - uword[] @split t_step = [ + byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8] + 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, @@ -79,7 +79,7 @@ adpcm { ; elif predicted < -32767: ; predicted = - 32767 - index += t_index[nibble] + index += t_index[nibble] as ubyte if_neg index = 0 else if index >= len(t_step)-1 diff --git a/benchmark-program/b_circles.p8 b/benchmark-program/b_circles.p8 index a30cdb934..7b7734f1b 100644 --- a/benchmark-program/b_circles.p8 +++ b/benchmark-program/b_circles.p8 @@ -4,8 +4,8 @@ circles { const ubyte MAX_NUM_CIRCLES = 80 const ubyte GROWTH_RATE = 4 - 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 color uword total_num_circles diff --git a/benchmark-program/b_textelite.p8 b/benchmark-program/b_textelite.p8 index c171cb83d..f49e31e59 100644 --- a/benchmark-program/b_textelite.p8 +++ b/benchmark-program/b_textelite.p8 @@ -1,7 +1,6 @@ %import textio %import conv -%import string -%import string +%import strings textelite { @@ -172,7 +171,7 @@ textelite { sub next_input(str buffer) -> ubyte { input_index++ - return string.copy(inputs[input_index], buffer) + return strings.copy(inputs[input_index], buffer) } } @@ -571,7 +570,7 @@ elite_galaxy { ubyte distance = elite_planet.distance(px, py) if distance <= max_distance { elite_planet.name = make_current_planet_name() - elite_planet.name[0] = string.upperchar(elite_planet.name[0]) + elite_planet.name[0] = strings.upperchar(elite_planet.name[0]) uword tx = elite_planet.x uword ty = elite_planet.y if local { @@ -840,7 +839,7 @@ elite_planet { } } randname[nx] = 0 - randname[0] = string.upperchar(randname[0]) + randname[0] = strings.upperchar(randname[0]) return randname } @@ -912,12 +911,12 @@ elite_planet { source_ptr = source_stack[stack_ptr] } else { if c == $b0 { - @(result_ptr) = string.upperchar(name[0]) + @(result_ptr) = strings.upperchar(name[0]) result_ptr++ concat_string(&name + 1) } else if c == $b1 { - @(result_ptr) = string.upperchar(name[0]) + @(result_ptr) = strings.upperchar(name[0]) result_ptr++ ubyte ni for ni in 1 to len(name) { @@ -981,7 +980,7 @@ elite_util { if pc == 0 return true ; to lowercase for case insensitive compare: - if string.lowerchar(pc)!=string.lowerchar(sc) + if strings.lowerchar(pc)!=strings.lowerchar(sc) return false prefixptr++ stringptr++ diff --git a/codeCore/src/prog8/code/SymbolTableMaker.kt b/codeCore/src/prog8/code/SymbolTableMaker.kt index a1dd8574f..fc72ae25c 100644 --- a/codeCore/src/prog8/code/SymbolTableMaker.kt +++ b/codeCore/src/prog8/code/SymbolTableMaker.kt @@ -136,7 +136,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp is PtAddressOf -> { if(it.isFromArrayElement) TODO("address-of array element $it in initial array value") - StArrayElement(null, it.identifier.name, null) + if(it.identifier.type.isSplitWordArray) + StArrayElement(null, it.identifier.name + "_lsb", null) // the _lsb split array comes first in memory + else + StArrayElement(null, it.identifier.name, null) } is PtNumber -> StArrayElement(it.number, null, null) is PtBool -> StArrayElement(null, null, it.value) diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index fcd42dac0..829e99de8 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -72,12 +72,12 @@ sealed class SubType(val dt: BaseDataType) { } } -private data object SubUnsignedByte: SubType(BaseDataType.UBYTE) -private data object SubSignedByte: SubType(BaseDataType.BYTE) -private data object SubUnsignedWord: SubType(BaseDataType.UWORD) -private data object SubSignedWord: SubType(BaseDataType.WORD) -private data object SubBool: SubType(BaseDataType.BOOL) -private data object SubFloat: SubType(BaseDataType.FLOAT) +data object SubUnsignedByte: SubType(BaseDataType.UBYTE) +data object SubSignedByte: SubType(BaseDataType.BYTE) +data object SubUnsignedWord: SubType(BaseDataType.UWORD) +data object SubSignedWord: SubType(BaseDataType.WORD) +data object SubBool: SubType(BaseDataType.BOOL) +data object SubFloat: SubType(BaseDataType.FLOAT) class DataType private constructor(val base: BaseDataType, val sub: SubType?) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 1ce5bbfd8..bc464b32f 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -651,18 +651,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem) } is PtAddressOf -> { - val mem = PtMemoryByte(fcall.position) - if((fcall.args[0] as PtAddressOf).isFromArrayElement) - TODO("address-of arrayelement") - if(msb) { - val address = PtBinaryExpression("+", DataType.forDt(BaseDataType.UWORD), fcall.args[0].position) - address.add(fcall.args[0]) - address.add(PtNumber(address.type.base, 1.0, fcall.args[0].position)) - mem.add(address) + val addrof = fcall.args[0] as PtAddressOf + if(addrof.identifier.type.isSplitWordArray) { + TODO("address of split word array") } else { - mem.add(fcall.args[0]) + val mem = PtMemoryByte(fcall.position) + if(addrof.isFromArrayElement) + TODO("address-of arrayelement") + if(msb) { + val address = PtBinaryExpression("+", DataType.forDt(BaseDataType.UWORD), addrof.position) + address.add(addrof) + address.add(PtNumber(address.type.base, 1.0, addrof.position)) + mem.add(address) + } else { + mem.add(addrof) + } + target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem) } - target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem) + } is PtArrayIndexer -> { val indexer = fcall.args[0] as PtArrayIndexer diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt index 67a3284e1..9febd85e7 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt @@ -808,7 +808,11 @@ _jump jmp (${target.asmLabel}) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true) asmgen.out(" cpy #0") } else { - asmgen.out(" lda #>${asmgen.asmVariableName(value.identifier)}") + if(value.identifier.type.isSplitWordArray) { + TODO("address of split word array") + } else { + asmgen.out(" lda #>${asmgen.asmVariableName(value.identifier)}") + } } } else -> { @@ -1603,9 +1607,13 @@ _jump jmp (${target.asmLabel}) if(left.isFromArrayElement) fallbackTranslateForSimpleCondition(stmt) else { - asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) - val varname = left.identifier.name - translateAYNotEquals("#<$varname", "#>$varname") + if(left.identifier.type.isSplitWordArray) { + TODO("address of split word array") + } else { + asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) + val varname = left.identifier.name + translateAYNotEquals("#<$varname", "#>$varname") + } } } else -> { @@ -1651,9 +1659,13 @@ _jump jmp (${target.asmLabel}) if(left.isFromArrayElement) fallbackTranslateForSimpleCondition(stmt) else { - asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) - val varname = left.identifier.name - translateAYEquals("#<$varname", "#>$varname") + if(left.identifier.type.isSplitWordArray) { + TODO("address of split word array") + } else { + asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) + val varname = left.identifier.name + translateAYEquals("#<$varname", "#>$varname") + } } } else -> { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 273484e8e..7910a7745 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -439,8 +439,11 @@ internal class AssignmentAsmGen( private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) { when(val value = assign.source.expression!!) { is PtAddressOf -> { - val sourceName = asmgen.asmSymbolName(value.identifier) val arrayDt = value.identifier.type + val sourceName = if(arrayDt.isSplitWordArray) + asmgen.asmSymbolName(value.identifier) + "_lsb" // the _lsb split array comes first in memory + else + asmgen.asmSymbolName(value.identifier) assignAddressOf(assign.target, sourceName, arrayDt, value.arrayIndexExpr) } is PtBool -> throw AssemblyError("source kind should have been literalboolean") @@ -1339,6 +1342,10 @@ internal class AssignmentAsmGen( if(right.isFromArrayElement) { TODO("address-of array element $symbol at ${right.position}") } else { + if(right.identifier.type.isSplitWordArray) { + TODO("address of split word array") + return true + } assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned) if(expr.operator=="+") asmgen.out(""" diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 3e9b35503..71ad6a798 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -154,7 +154,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } } } else { - addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null) + if(expr.identifier.type.isSplitWordArray) { + // the _lsb split array comes first in memory + addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol+"_lsb"), null) + } else + addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null) } return ExpressionCodeResult(result, vmDt, resultRegister, -1) } diff --git a/compiler/res/prog8lib/anyall.p8 b/compiler/res/prog8lib/anyall.p8 index 40bca3dbf..acd4b4c82 100644 --- a/compiler/res/prog8lib/anyall.p8 +++ b/compiler/res/prog8lib/anyall.p8 @@ -42,7 +42,7 @@ anyall { sub anyw(uword arrayptr, uword num_elements) -> bool { ; -- returns true if any word in the array is not zero. - ; TODO FIX: doesn't work on @split arrays. + ; TODO FIX: doesn't work on split arrays. Just always test every byte ! 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. - ; TODO FIX: doesn't work on @split arrays. + ; TODO FIX: doesn't work on split arrays. Just always test every byte ! cx16.r1 = arrayptr if msb(num_elements)==0 { repeat lsb(num_elements) { diff --git a/compiler/res/prog8lib/cx16/sprites.p8 b/compiler/res/prog8lib/cx16/sprites.p8 index e4ee38b7b..946aebdc6 100644 --- a/compiler/res/prog8lib/cx16/sprites.p8 +++ b/compiler/res/prog8lib/cx16/sprites.p8 @@ -83,7 +83,7 @@ sprites { } sub pos_batch(ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr) { - ; -- note: the x and y positions word arrays must be regular arrays, they cannot be @split arrays! + ; -- note: the x and y positions word arrays must be regular arrays, they cannot be split arrays! TODO FIX THIS sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008 cx16.vaddr_autoincr(1, sprite_reg, 0, 8) cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 9273ff2f9..9148ab40f 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -223,9 +223,9 @@ internal class AstChecker(private val program: Program, errors.err("byte loop variable can only loop over bytes", forLoop.position) } BaseDataType.WORD -> { - if(!iterableDt.isSignedByte && !iterableDt.isSignedWord && // TODO remove byte and word check? + if(!iterableDt.isSignedByte && !iterableDt.isSignedWord && !iterableDt.isSignedByteArray && !iterableDt.isUnsignedByteArray && - !iterableDt.isSignedWordArray && !iterableDt.isSplitWordArray) + !iterableDt.isSignedWordArray && !iterableDt.isUnsignedWordArray) errors.err("word loop variable can only loop over bytes or words", forLoop.position) } BaseDataType.FLOAT -> { @@ -703,8 +703,6 @@ 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.datatype.isSplitWordArray) - errors.err("cannot take address of split word array",addressOf.position) } super.visit(addressOf) } diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index 8ff02a299..097b5dde2 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -730,7 +730,15 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr private fun transform(srcRange: RangeExpression): PtRange { require(srcRange.from.inferType(program)==srcRange.to.inferType(program)) - val type = srcRange.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + var type = srcRange.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + if(type.isSplitWordArray) { + // ranges are never a split word array! + when(type.sub) { + is SubSignedWord -> type = DataType.arrayFor(BaseDataType.WORD, false) + is SubUnsignedWord -> type = DataType.arrayFor(BaseDataType.UWORD, false) + else -> { } + } + } val range=PtRange(type, srcRange.position) range.add(transformExpression(srcRange.from)) range.add(transformExpression(srcRange.to)) diff --git a/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt b/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt index 980a218e6..6fba1580f 100644 --- a/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt +++ b/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt @@ -77,18 +77,6 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro } } - if(array.type.isArray) { - val mods = mutableListOf() - for(elt in array.value.filterIsInstance()) { - val decl = elt.targetVarDecl(program) - if(decl!=null && decl.datatype.isSplitWordArray) { - // you can't take the adress of a split-word array. - errors.err("cannot take address of split word array", decl.position) - } - } - return mods - } - return noModifications } @@ -151,15 +139,4 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro // } return noModifications } - - override fun after(addressOf: AddressOf, parent: Node): Iterable { - val variable=addressOf.identifier.targetVarDecl(program) - if (variable!=null) { - if (variable.datatype.isSplitWordArray) { - // you can't take the adress of a split-word array. - errors.err("cannot take address of split word array", addressOf.position) - } - } - return noModifications - } } diff --git a/compiler/test/codegeneration/TestArrayThings.kt b/compiler/test/codegeneration/TestArrayThings.kt index 297b6e3a1..c9e4685f0 100644 --- a/compiler/test/codegeneration/TestArrayThings.kt +++ b/compiler/test/codegeneration/TestArrayThings.kt @@ -385,7 +385,7 @@ main { compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null } - test("taking address of split arrays") { + test("taking address of split arrays works") { val src=""" main { sub start() { @@ -404,11 +404,7 @@ main { 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") + errors.warnings.size shouldBe 0 } }) diff --git a/compiler/test/vm/TestCompilerVirtual.kt b/compiler/test/vm/TestCompilerVirtual.kt index e6bfe07a4..1e389d59a 100644 --- a/compiler/test/vm/TestCompilerVirtual.kt +++ b/compiler/test/vm/TestCompilerVirtual.kt @@ -16,11 +16,32 @@ import prog8.intermediate.IRFileReader import prog8.intermediate.IRSubroutine import prog8.intermediate.Opcode import prog8.vm.VmRunner +import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText import kotlin.io.path.readText class TestCompilerVirtual: FunSpec({ - test("compile virtual: array with pointers") { + test("linear words array with pointers") { + val src = """ +main { + sub start() { + str localstr = "hello" + ubyte[] otherarray = [1,2,3] + uword[] @nosplit words = [1111,2222,"three",&localstr,&otherarray] + uword @shared zz = &words + bool result = 2222 in words + zz = words[2] + zz++ + zz = words[3] + } +}""" + val target = VMTarget() + val result = compileText(target, false, src, writeAssembly = true)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") + VmRunner().runProgram(virtfile.readText()) + } + + test("split words array with pointers") { val src = """ main { sub start() { @@ -35,11 +56,33 @@ main { } }""" val target = VMTarget() - val result = compileText(target, true, src, writeAssembly = true)!! + val result = compileText(target, false, src, writeAssembly = true)!! val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") VmRunner().runProgram(virtfile.readText()) } + test("taking address of split arrays works") { + 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(VMTarget(), optimize=false, src, writeAssembly=true, errors=errors) shouldNotBe null + errors.errors.size shouldBe 0 + errors.warnings.size shouldBe 0 + } + test("compile virtual: str args and return type, and global var init") { val src = """ main { diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index d51b0f2a0..35dc5deb1 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -758,6 +758,8 @@ 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 "" 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() diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 4eec17d75..a51c6c761 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -280,7 +280,15 @@ class VarDecl(val type: VarDeclType, fun createAuto(array: ArrayLiteral): VarDecl { val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}" - val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") } + var arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") } + if(arrayDt.isSplitWordArray) { + // autovars for array literals are NOT stored as a split word array! + when(arrayDt.sub) { + is SubSignedWord -> arrayDt = DataType.arrayFor(BaseDataType.WORD, false) + is SubUnsignedWord -> arrayDt = DataType.arrayFor(BaseDataType.UWORD, false) + else -> { } + } + } val arraysize = ArrayIndex.forArray(array) return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array, sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 1132e0297..282458d20 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,12 +3,16 @@ 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? 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. Search "cannot take address of split word array" +- fix anyall.anyw/allw , optimize any/all in asm? make sure it still works for virtual +- fix IR compilation errors +- Regular & will just return the start of the split array in memory whatever byte comes first. Search TODO("address of split word array") +- check this for 6502 codegen: split word arrays, both _msb and _lsb arrays are tagged with an alignment. This is not what's intended; only the one put in memory first should be aligned (the other one should follow straight after it) - add &< and &> operators to get the address of the lsb-array and msb-array, respectively. - 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 +- test all examples and projects (paint has wrong palette colors) +- benchmark program became slower!? (did get smaller, just slower????) + ... diff --git a/examples/cx16/pcmaudio/play-adpcm.p8 b/examples/cx16/pcmaudio/play-adpcm.p8 index 63f1f9c79..9b34f965f 100644 --- a/examples/cx16/pcmaudio/play-adpcm.p8 +++ b/examples/cx16/pcmaudio/play-adpcm.p8 @@ -269,13 +269,13 @@ stereo { %asm {{ ; copy to vera PSG fifo buffer ldy #0 -- lda p8v_left,y +- lda p8v_left_lsb,y sta cx16.VERA_AUDIO_DATA - lda p8v_left+1,y + lda p8v_left_msb,y sta cx16.VERA_AUDIO_DATA - lda p8v_right,y + lda p8v_right_lsb,y sta cx16.VERA_AUDIO_DATA - lda p8v_right+1,y + lda p8v_right_msb,y sta cx16.VERA_AUDIO_DATA iny iny diff --git a/examples/cx16/pcmaudio/stream-wav.p8 b/examples/cx16/pcmaudio/stream-wav.p8 index e0a0d0bcd..60edfdc5e 100644 --- a/examples/cx16/pcmaudio/stream-wav.p8 +++ b/examples/cx16/pcmaudio/stream-wav.p8 @@ -382,13 +382,13 @@ _lp2 lda $ffff,y %asm {{ ; copy to vera PSG fifo buffer ldy #0 -- lda p8v_left,y +- lda p8v_left_lsb,y sta cx16.VERA_AUDIO_DATA - lda p8v_left+1,y + lda p8v_left_msb,y sta cx16.VERA_AUDIO_DATA - lda p8v_right,y + lda p8v_right_lsb,y sta cx16.VERA_AUDIO_DATA - lda p8v_right+1,y + lda p8v_right_msb,y sta cx16.VERA_AUDIO_DATA iny iny diff --git a/examples/test.p8 b/examples/test.p8 index 3d37b8ce1..4a9422fc7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -2,13 +2,11 @@ %zeropage basicsafe main { - sub start() { - uword[] addresses = [scores2, start] - uword[] scores1 = [10, 25, 50, 100] - uword[] scores2 = [100, 250, 500, 1000] + uword large = memory("large", 20000, 256) - cx16.r0 = &scores1 - cx16.r1 = &scores2 - cx16.r2 = &addresses + sub start() { + for cx16.r1 in large to large+20000-1 { + cx16.r0++ + } } } diff --git a/intermediate/src/prog8/intermediate/IRSymbolTable.kt b/intermediate/src/prog8/intermediate/IRSymbolTable.kt index d219ebbb2..76c50690a 100644 --- a/intermediate/src/prog8/intermediate/IRSymbolTable.kt +++ b/intermediate/src/prog8/intermediate/IRSymbolTable.kt @@ -68,7 +68,7 @@ class IRSymbolTable { val newArray = mutableListOf() array.forEach { if(it.addressOfSymbol!=null) { - val target = variable.lookup(it.addressOfSymbol!!)!! + val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}") newArray.add(IRStArrayElement(null, null, target.scopedName)) } else { newArray.add(IRStArrayElement.from(it)) diff --git a/parser/src/main/antlr/Prog8ANTLR.g4 b/parser/src/main/antlr/Prog8ANTLR.g4 index 9e3c6cbd6..88c1bc0a5 100644 --- a/parser/src/main/antlr/Prog8ANTLR.g4 +++ b/parser/src/main/antlr/Prog8ANTLR.g4 @@ -61,6 +61,8 @@ ZEROPAGENOT: '@nozp' ; SHARED : '@shared' ; +SPLIT: '@split' ; + NOSPLIT: '@nosplit' ; ALIGNWORD: '@alignword' ; @@ -159,7 +161,7 @@ directivearg : stringliteral | identifier | integerliteral ; vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ; -decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | NOSPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | DIRTY)* ; +decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | NOSPLIT | SPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | DIRTY)* ; varinitializer : vardecl '=' expression ;