use split word arrays in various examples, fix codegen issue, docs

This commit is contained in:
Irmen de Jong 2023-05-28 23:19:01 +02:00
parent 9896bc110e
commit bbc02752c9
17 changed files with 141 additions and 75 deletions

View File

@ -96,13 +96,14 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
str
}
is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] ${node.name}"
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
}
else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] ${node.name}"
"${eltType.name.lowercase()}[] $split ${node.name}"
}
else
"${node.type.name.lowercase()} ${node.name}"

View File

@ -352,6 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda #$numElements
jsr floats.func_reverse_f""")
}
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw AssemblyError("weird type")
}
}
@ -398,6 +399,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
jsr prog8_lib.func_sort_ub""")
}
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw AssemblyError("weird type")
}
} else
@ -664,6 +666,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt")
}
} else {
@ -671,6 +674,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)

View File

@ -1469,8 +1469,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
// return
// }
TargetStorageKind.ARRAY -> {
if(target.array!!.splitWords)
TODO("assign type casted byte into split words ${target.position}")
// byte to word, just assign to registers first, then assign into array
assignExpressionToRegister(value, RegisterOrPair.AY, targetDt==DataType.WORD)
assignRegisterpairWord(target, RegisterOrPair.AY)
@ -1868,21 +1866,25 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
in WordDatatypes -> {
if(target.array!!.splitWords)
TODO("assign word stack value into split words ${target.position}")
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta ${target.asmVarname}+$scaledIdx
lda P8ESTACK_HI,x
sta ${target.asmVarname}+$scaledIdx+1
""")
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta ${target.asmVarname}_lsb+$scaledIdx
lda P8ESTACK_HI,x
sta ${target.asmVarname}_msb+$scaledIdx""")
else
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta ${target.asmVarname}+$scaledIdx
lda P8ESTACK_HI,x
sta ${target.asmVarname}+$scaledIdx+1""")
}
DataType.FLOAT -> {
asmgen.out("""
lda #<(${target.asmVarname}+$scaledIdx)
ldy #>(${target.asmVarname}+$scaledIdx)
jsr floats.pop_float
""")
jsr floats.pop_float""")
}
else -> throw AssemblyError("weird target variable type ${target.datatype}")
}
@ -2933,9 +2935,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
throw AssemblyError("memory is bytes not words")
}
TargetStorageKind.ARRAY -> {
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
if(target.array.splitWords)
asmgen.out("""
lda #0
@ -2992,9 +2992,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
}
TargetStorageKind.ARRAY -> {
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
if(target.array.splitWords)
asmgen.out("""
lda #<${word.toHex()}
@ -3392,8 +3390,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
if(wordtarget.array!!.splitWords)
TODO("assign mem byte into split words ${wordtarget.position}")
asmgen.out(" lda ${address.toHex()} | ldy #0")
assignRegisterpairWord(wordtarget, RegisterOrPair.AY)
}
@ -3423,8 +3419,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
if(wordtarget.array!!.splitWords)
TODO("assign mem byte into split words ${wordtarget.position}")
asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" ldy #0")
assignRegisterpairWord(wordtarget, RegisterOrPair.AY)

View File

@ -193,11 +193,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
TargetStorageKind.ARRAY -> {
val indexNum = target.array!!.index as? PtNumber
val indexVar = target.array.index as? PtIdentifier
if(target.array.splitWords)
TODO("in-place assign split words ${target.position}")
when {
indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
val targetVarName = if(target.array.splitWords)
"${target.asmVarname} + ${indexNum.number.toInt()}"
else
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
when (target.datatype) {
in ByteDatatypes -> {
when(value.kind) {
@ -284,8 +285,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1")
if(target.array.splitWords) {
asmgen.out(" lda ${target.array.variable.name}_lsb,y | sta P8ZP_SCRATCH_W1")
asmgen.out(" lda ${target.array.variable.name}_msb,y | sta P8ZP_SCRATCH_W1+1")
} else {
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1")
}
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_word_litval_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt())
@ -305,8 +311,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird source type ${value.kind}")
}
asmgen.restoreRegisterLocal(CpuRegister.Y)
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y")
if(target.array.splitWords) {
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name}_lsb,y")
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}_msb,y")
} else {
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y")
}
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)

View File

@ -1,12 +1,16 @@
package prog8tests.codegeneration
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.code.ast.printAst
import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
class TestArrayInplaceAssign: FunSpec({
class TestArrayThings: FunSpec({
test("assign prefix var to array should compile fine and is not split into inplace array modification") {
val text = """
main {
@ -88,5 +92,48 @@ main {
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("split only for word arrays") {
val text = """
main {
ubyte[10] @split sb
uword[10] @split sw
word[10] @split sw2
float[10] @split sf
sub start() {
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors.forEach {
it shouldContain "split"
it shouldContain "word arrays"
}
}
test("split word arrays in asm as lsb/msb") {
val text = """
main {
uword[10] @split @shared uw
word[10] @split @shared sw
uword[10] @shared normal
sub start() {
%asm {{
lda normal
lda uw_lsb
lda uw_msb
lda sw_lsb
lda sw_msb
}}
}
}"""
val result6502 = compileText(C64Target(), false, text, writeAssembly = true)!!.codegenAst!!
val resultVm = compileText(VMTarget(), false, text, writeAssembly = true)!!.codegenAst!!
printAst(result6502, true, ::println)
printAst(resultVm, true, ::println)
}
})

View File

@ -290,10 +290,10 @@ Here are some examples of arrays::
.. note::
Right now, the array should be small enough to be indexable by a single byte index.
This means byte arrays should be <= 256 elements, word arrays <= 128 elements, and float
arrays <= 51 elements.
This means byte arrays should be <= 256 elements, word arrays <= 128 elements (256 if
it's a split array - see below), and float arrays <= 51 elements.
You can split an array initializer list over several lines if you want.
You can write out an array initializer list over several lines if you want to improve readability.
Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.)
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
@ -323,6 +323,21 @@ dynamic, location. You can use array indexing on a pointer variable to use it as
a dynamic location in memory: currently this is equivalent to directly referencing the bytes in
memory at the given index. See also :ref:`pointervars_programming`
**LSB/MSB split word arrays:**
For (u)word arrays, you can make the compiler layout the array in memory as two separate arrays,
one with the LSBs and one with the MSBs of the word values. This is more efficient when storing
and reading words from the array (the index can be used twice).
Add the ``@split`` tag to the variable declaration to do this.
In the assembly code, the array will be generated as two byte arrays namely ``name_lsb`` and ``name_msb``.
Note that the maximum length of a split word array is 256! (regular word arrays are limited to 128 elements).
.. caution::
Not all array operations are supported yet on "split word arrays".
The compiler may give an unpleasant error or crash when you hit such a case in your code.
If this happens simply revert to a regular word array and please report the issue,
so that more support can be added in the future where it is needed.
Strings
^^^^^^^

View File

@ -274,17 +274,22 @@ Variable declarations
Variables should be declared with their exact type and size so the compiler can allocate storage
for them. You can give them an initial value as well. That value can be a simple literal value,
or an expression. If you don't provide an initial value yourself, zero will be used.
You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
Add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
when selecting variables to be put into zeropage (but no guarantees). If the ZP is full,
the variable will be allocated in normal memory elsewhere.
Use the ``@requirezp`` tag to force the variable in zeropage, but if the ZP is full,
Add a ``@requirezp`` tag to force the variable in zeropage, but if the ZP is full,
the compilation will fail.
You can add a ``@shared`` shared-tag, to tell the compiler that the variable is shared
Add a ``@shared`` shared-tag, to tell the compiler that the variable is shared
with some assembly code and that it should not be optimized away if not used elsewhere.
For (u)word arrays, add ``@split`` to make the array layout in memory as 2 split arrays
one with the LSBs one with the MSBs of the word values.
The syntax is::
<datatype> [ @shared ] [ @zp ] [ @requirezp ] <variable name> [ = <initial value> ]
<datatype> [ @type-tag ] <variable name> [ = <initial value> ]
where type-tag is one of the tags mentioned earlier.
Various examples::
word thing = 0

View File

@ -16,14 +16,7 @@ For 9.0 major changes
- DONE: (on cx16) added diskio.save_raw() to save without the 2 byte prg header
- DONE: added sys.irqsafe_xxx irqd routines
- DONE: added gfx2.fill() flood fill routine
- [much work:] add special (u)word array type (or modifier such as @fast or @split? ) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction....
So maybe only allow the bare essentials? (store, get, ++/--/+/-, bitwise operations?)
- TODO: fix the remaining 'simple' split words TODO cases (expression assignments)
- TODO: change more library and examples to use more @split arrays
- TODO: splitarrays unit tests
- DONE: added @split storage class for (u)word arrays to store them as split lsb/msb arrays which is more efficient (but doesn't yet support all array operations)
- [much work:] more support for (64tass) SEGMENTS ?
- (What, how, isn't current BSS support enough?)

View File

@ -71,7 +71,7 @@ sub print_notes(ubyte n1, ubyte n2) {
]
uword[] music_freq_table = [
uword[] @split 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

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

View File

@ -72,9 +72,9 @@ main {
word[] @split zcoor = [ -100, 100, -100, 100, -100, 100, -100, 100 ]
; storage for rotated coordinates
word[len(xcoor)] rotatedx
word[len(ycoor)] rotatedy
word[len(zcoor)] rotatedz
word[len(xcoor)] @split rotatedx
word[len(ycoor)] @split rotatedy
word[len(zcoor)] @split rotatedz
sub start() {

View File

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

View File

@ -111,7 +111,7 @@ main {
$3532, $322e, $2e29, $2926, $2730, $242c, $2027, $1420
]
uword[] vera_freqs = [
uword[] @split 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

@ -8,9 +8,9 @@
main {
; storage for rotated coordinates
word[shipdata.totalNumberOfPoints] rotatedx
word[shipdata.totalNumberOfPoints] rotatedy
word[shipdata.totalNumberOfPoints] rotatedz
word[shipdata.totalNumberOfPoints] @split rotatedx
word[shipdata.totalNumberOfPoints] @split rotatedy
word[shipdata.totalNumberOfPoints] @split rotatedz
sub start() {
uword anglex

View File

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

View File

@ -5,10 +5,10 @@ main {
sub start() {
gfx2.screen_mode(4)
uword[128] flakes1_xx
uword[128] flakes1_yy
uword[128] flakes2_xx
uword[128] flakes2_yy
uword[128] @split flakes1_xx
uword[128] @split flakes1_yy
uword[128] @split flakes2_xx
uword[128] @split flakes2_yy
ubyte @zp idx
for idx in 0 to 127 {

View File

@ -1,18 +1,14 @@
%import floats
%import textio
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
uword[] @split split_uwords = [12345, 60000, 4096]
ubyte xx=1
txt.print_ub(lsb(split_uwords[xx]))
txt.spc()
txt.print_ub(msb(split_uwords[xx]))
txt.spc()
split_uwords[1] = mkword($11, xx) ; TODO fix this in codegen
txt.print_uw(split_uwords[1])
txt.print_ub(len(split_uwords))
txt.nl()
}
sub start22() {