fix many split array issues

This commit is contained in:
Irmen de Jong 2024-12-15 15:55:48 +01:00
parent d2827a7431
commit 80d88b3c61
25 changed files with 172 additions and 105 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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++

View File

@ -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)

View File

@ -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?) {

View File

@ -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

View File

@ -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 -> {

View File

@ -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("""

View File

@ -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)
}

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.
; 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) {

View File

@ -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)

View File

@ -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)
}

View File

@ -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))

View File

@ -77,18 +77,6 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
}
}
if(array.type.isArray) {
val mods = mutableListOf<IAstModification>()
for(elt in array.value.filterIsInstance<IdentifierReference>()) {
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<IAstModification> {
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
}
}

View File

@ -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
}
})

View File

@ -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 {

View File

@ -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 "<multiple>"
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()

View File

@ -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)

View File

@ -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????)
...

View File

@ -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

View File

@ -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

View File

@ -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++
}
}
}

View File

@ -68,7 +68,7 @@ class IRSymbolTable {
val newArray = mutableListOf<IRStArrayElement>()
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))

View File

@ -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 ;