remove support for array-to-array assignments (other than initialization of variable declaration)

Just use an explicit sys.memcopy(src, dest, sizeof(dest))  or assign array members individually.
This commit is contained in:
Irmen de Jong 2024-10-13 19:37:25 +02:00
parent aef211e5f3
commit e9edffa9f0
15 changed files with 48 additions and 446 deletions

View File

@ -79,7 +79,6 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
// cmp returns a status in the carry flag, but not a proper return value // cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null), "cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), "prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE), "prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD), "prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
"prog8_ifelse_bittest_set" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL), "prog8_ifelse_bittest_set" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),

View File

@ -1,7 +1,5 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.StMemVar
import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
@ -71,101 +69,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister) "prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister) "prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister) "prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
"prog8_lib_arraycopy" -> funcArrayCopy(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}") else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
} }
return BuiltinFunctions.getValue(fcall.name).returnType return BuiltinFunctions.getValue(fcall.name).returnType
} }
private fun funcArrayCopy(fcall: PtBuiltinFunctionCall) {
val source = fcall.args[0] as PtIdentifier
val target = fcall.args[1] as PtIdentifier
val numElements = when(val sourceSymbol = asmgen.symbolTable.lookup(source.name)) {
is StStaticVariable -> sourceSymbol.length!!
is StMemVar -> sourceSymbol.length!!
else -> 0
}
val sourceAsm = asmgen.asmVariableName(source)
val targetAsm = asmgen.asmVariableName(target)
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
// split -> split words (copy lsb and msb arrays separately)
asmgen.out("""
lda #<${sourceAsm}_lsb
ldy #>${sourceAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_lsb
ldy #>${targetAsm}_lsb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numElements and 255}
jsr prog8_lib.memcopy_small
lda #<${sourceAsm}_msb
ldy #>${sourceAsm}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_msb
ldy #>${targetAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numElements and 255}
jsr prog8_lib.memcopy_small""")
}
else if(source.type in SplitWordArrayTypes) {
// split word array to normal word array (copy lsb and msb arrays separately)
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
asmgen.out("""
lda #<${sourceAsm}_lsb
ldy #>${sourceAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${sourceAsm}_msb
ldy #>${sourceAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<${targetAsm}
ldy #>${targetAsm}
ldx #${numElements and 255}
jsr prog8_lib.arraycopy_split_to_normal_words""")
}
else if(target.type in SplitWordArrayTypes) {
// normal word array to split array
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
asmgen.out("""
lda #<${targetAsm}_lsb
ldy #>${targetAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_msb
ldy #>${targetAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<${sourceAsm}
ldy #>${sourceAsm}
ldx #${numElements and 255}
jsr prog8_lib.arraycopy_normal_to_split_words""")
}
else {
// normal array to array copy, various element types
val eltsize = asmgen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
val numBytes = numElements * eltsize
asmgen.out("""
lda #<${sourceAsm}
ldy #>${sourceAsm}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}
ldy #>${targetAsm}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numBytes and 255}
jsr prog8_lib.memcopy_small""")
}
}
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) { private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) {
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine. // square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
when (resultType) { when (resultType) {
@ -1357,22 +1266,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
private fun outputAddressAndLengthOfArray(arg: PtIdentifier) {
// address goes in P8ZP_SCRATCH_W1, number of elements in A
val numElements = when(val symbol = asmgen.symbolTable.lookup(arg.name)) {
is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!!
else -> 0
}
val identifierName = asmgen.asmVariableName(arg)
asmgen.out("""
lda #<$identifierName
ldy #>$identifierName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #${numElements and 255}""")
}
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) { private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
val signature = BuiltinFunctions.getValue(call.name) val signature = BuiltinFunctions.getValue(call.name)
val callConv = signature.callConvention(call.args.map { it.type}) val callConv = signature.callConvention(call.args.map { it.type})

View File

@ -44,72 +44,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"prog8_lib_stringcompare" -> funcStringCompare(call) "prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE) "prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD) "prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
"prog8_lib_arraycopy" -> funcArrayCopy(call)
else -> throw AssemblyError("missing builtinfunc for ${call.name}") else -> throw AssemblyError("missing builtinfunc for ${call.name}")
} }
} }
private fun funcArrayCopy(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val source = call.args[0] as PtIdentifier
val target = call.args[1] as PtIdentifier
val sourceLength = codeGen.symbolTable.getLength(source.name)!!
val targetLength = codeGen.symbolTable.getLength(target.name)!!
require(sourceLength==targetLength)
val result = mutableListOf<IRCodeChunkBase>()
val fromReg = codeGen.registers.nextFree()
val toReg = codeGen.registers.nextFree()
val countReg = codeGen.registers.nextFree()
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
// split words -> split words, copy lsb and msb arrays separately
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
}
else if(source.type in SplitWordArrayTypes) {
// split -> normal words
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
val fromRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromRegMsb, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_SPLITW_TO_NORMAL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to fromRegMsb, IRDataType.WORD to toReg, IRDataType.BYTE to countReg), returns = null)
}
else if(target.type in SplitWordArrayTypes) {
// normal -> split words
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
val toRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toRegMsb, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_NORMAL_TO_SPLITW, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to toRegMsb, IRDataType.BYTE to countReg), returns = null)
}
else {
// normal array to array copy (various element types)
val eltsize = codeGen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength * eltsize)
}
result += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult { private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val valueTr = exprGen.translateExpression(call.args[0]) val valueTr = exprGen.translateExpression(call.args[0])

View File

@ -1813,14 +1813,9 @@ internal class AstChecker(private val program: Program,
sourceValue: Expression) : Boolean { sourceValue: Expression) : Boolean {
val position = sourceValue.position val position = sourceValue.position
if(sourceValue is ArrayLiteral && targetDatatype in ArrayDatatypes) { if(sourceValue is ArrayLiteral || targetDatatype in ArrayDatatypes) {
val vardecl=target.identifier?.targetVarDecl(program) errors.err("cannot assign arrays directly. Maybe use sys.memcopy(src, tgt, sizeof(tgt)) instead.", target.position)
val targetSize = vardecl?.arraysize?.constIndex() return false
if(targetSize!=null) {
if(sourceValue.value.size != targetSize) {
errors.err("array size mismatch (expecting $targetSize, got ${sourceValue.value.size})", sourceValue.position)
}
}
} }
if(sourceValue is RangeExpression) { if(sourceValue is RangeExpression) {
@ -1841,7 +1836,7 @@ internal class AstChecker(private val program: Program,
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD
DataType.FLOAT -> sourceDatatype in NumericDatatypes DataType.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype == DataType.STR DataType.STR -> sourceDatatype == DataType.STR
else -> targetDatatype in ArrayDatatypes && sourceValue is ArrayLiteral else -> false
} }
if(result) if(result)

View File

@ -23,22 +23,6 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
// - pointer[word] replaced by @(pointer+word) // - pointer[word] replaced by @(pointer+word)
// - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word // - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word
// - flatten chained assignments // - flatten chained assignments
// - replace array assignments by a call to the builtin function that does this: prog8_lib_arraycopy
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val targetArray = assignment.target.identifier?.targetVarDecl(program)
val sourceArray = (assignment.value as? IdentifierReference)?.targetVarDecl(program)
if(targetArray?.isArray==true && sourceArray?.isArray==true) {
val copy = FunctionCallStatement(
IdentifierReference(listOf("prog8_lib_arraycopy"), assignment.position),
mutableListOf(
IdentifierReference(sourceArray.scopedName, assignment.position),
IdentifierReference(targetArray.scopedName, assignment.position)
), false, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, copy, parent))
}
return noModifications
}
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> { override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
fun jumpAfter(stmt: Statement): Iterable<IAstModification> { fun jumpAfter(stmt: Statement): Iterable<IAstModification> {

View File

@ -5,10 +5,7 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.types.instanceOf import io.kotest.matchers.types.instanceOf
import prog8.code.StStaticVariable
import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
@ -156,25 +153,6 @@ main {
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
} }
test("split array assignments") {
val text = """
main {
sub start() {
str name1 = "name1"
str name2 = "name2"
uword[] @split names = [name1, name2, "name3"]
uword[] @split names2 = [name1, name2, "name3"]
uword[] addresses = [0,0,0]
names = [1111,2222,3333]
addresses = names
names = addresses
names2 = names
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("array target with expression for index") { test("array target with expression for index") {
val text = """ val text = """
main { main {
@ -333,81 +311,6 @@ main {
errors.errors[2] shouldContain "out of bounds" errors.errors[2] shouldContain "out of bounds"
} }
test("array assignments should check for number of elements and element type correctness") {
val src="""
%option enable_floats
main {
sub start() {
ubyte[] array = 1 to 4
ubyte[] array2 = [1,2,3,4]
str[] names = ["apple", "banana", "tomato"]
array = [10,11,12,13] ; ok!
array = 20 to 23 ; ok!
names = ["x1", "x2", "x3"] ; ok!
ubyte[] array3 = [1,2,3,4000] ; error: element type
array = 10 to 15 ; error: array size
array = 1000 to 1003 ; error: element type
names = ["x1", "x2", "x3", "x4"] ; error: array size
names = [1.1, 2.2, 3.3, 4.4] ; error: array size AND element type
names = [1.1, 2.2, 999999.9] ; error: element type
names = [1.1, 2.2, 9.9] ; error: element type
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, writeAssembly = true, errors = errors) shouldBe null
errors.errors.size shouldBe 8
errors.errors[0] shouldContain "incompatible type"
errors.errors[1] shouldContain "array size mismatch"
errors.errors[2] shouldContain "array element out of range"
errors.errors[3] shouldContain "array size mismatch"
errors.errors[4] shouldContain "array size mismatch"
errors.errors[5] shouldContain "value has incompatible type"
errors.errors[6] shouldContain "value has incompatible type"
errors.errors[7] shouldContain "value has incompatible type"
}
test("array assignments should work via array copy call") {
val src="""
%option enable_floats
main {
sub start() {
ubyte[] array = [1,2,3]
ubyte[3] array2
float[] flarray = [1.1, 2.2, 3.3]
float[3] flarray2
word[] warray = [-2222,42,3333]
word[3] warray2
str[] names = ["apple", "banana", "tomato"]
str[3] names2
; 8 array assignments -> 8 arraycopies:
array = [8,7,6]
array = array2
flarray = [99.9, 88.8, 77.7]
flarray = flarray2
warray = [4444,5555,6666]
warray = warray2
names = ["x1", "x2", "x3"]
names = names2
}
}"""
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
val x = result.codegenAst!!.entrypoint()!!
(x.children[12] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[13] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[14] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[15] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[16] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[17] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[18] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[19] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
}
test("array and string initializer with multiplication") { test("array and string initializer with multiplication") {
val src=""" val src="""
%option enable_floats %option enable_floats
@ -497,89 +400,5 @@ label:
errors.errors[0] shouldContain "contains non-constant" errors.errors[0] shouldContain "contains non-constant"
errors.errors[1] shouldContain "contains non-constant" errors.errors[1] shouldContain "contains non-constant"
} }
fun getTestOptions(): CompilationOptions {
val target = VMTarget()
return CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
zpReserved = emptyList(),
zpAllowed = CompilationOptions.AllZeropageAllowed,
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
}
test("array assignments with ranges and multiplications") {
val src="""
%option enable_floats
main {
sub start() {
bool[4] boolarray3
ubyte[4] bytearray3
uword[4] wordarray3
float[4] floatarray3
boolarray3 = [true] *4
bytearray3 = [42]*4
wordarray3 = [999]*4
wordarray3 = [&bytearray3]*4
wordarray3 = [bytearray3]*4
floatarray3 = [99.77]*4
bytearray3 = 10 to 13
wordarray3 = 5000 to 5003
floatarray3 = 100 to 103
}
}"""
val ast = compileText(C64Target(), false, src, writeAssembly = true)!!.codegenAst!!
val x = ast.entrypoint()!!
x.children.size shouldBe 23
val assign1value = (x.children[13] as PtBuiltinFunctionCall).args[1]
val assign2value = (x.children[14] as PtBuiltinFunctionCall).args[1]
val assign3value = (x.children[15] as PtBuiltinFunctionCall).args[1]
val assign4value = (x.children[16] as PtBuiltinFunctionCall).args[1]
val assign5value = (x.children[17] as PtBuiltinFunctionCall).args[1]
val assign6value = (x.children[18] as PtBuiltinFunctionCall).args[1]
val assign7value = (x.children[19] as PtBuiltinFunctionCall).args[1]
val assign8value = (x.children[20] as PtBuiltinFunctionCall).args[1]
val assign9value = (x.children[21] as PtBuiltinFunctionCall).args[1]
val options = getTestOptions()
val st = SymbolTableMaker(ast, options).make()
val heapvar1 = st.lookup((assign1value as PtIdentifier).name) as StStaticVariable
val heapvar2 = st.lookup((assign2value as PtIdentifier).name) as StStaticVariable
val heapvar3 = st.lookup((assign3value as PtIdentifier).name) as StStaticVariable
val heapvar4 = st.lookup((assign4value as PtIdentifier).name) as StStaticVariable
val heapvar5 = st.lookup((assign5value as PtIdentifier).name) as StStaticVariable
val heapvar6 = st.lookup((assign6value as PtIdentifier).name) as StStaticVariable
val heapvar7 = st.lookup((assign7value as PtIdentifier).name) as StStaticVariable
val heapvar8 = st.lookup((assign8value as PtIdentifier).name) as StStaticVariable
val heapvar9 = st.lookup((assign9value as PtIdentifier).name) as StStaticVariable
heapvar1.length shouldBe 4
heapvar2.length shouldBe 4
heapvar3.length shouldBe 4
heapvar4.length shouldBe 4
heapvar5.length shouldBe 4
heapvar6.length shouldBe 4
heapvar7.length shouldBe 4
heapvar8.length shouldBe 4
heapvar9.length shouldBe 4
heapvar1.dt shouldBe DataType.ARRAY_BOOL
heapvar2.dt shouldBe DataType.ARRAY_UB
heapvar3.dt shouldBe DataType.ARRAY_UW
heapvar4.dt shouldBe DataType.ARRAY_UW
heapvar5.dt shouldBe DataType.ARRAY_UW
heapvar6.dt shouldBe DataType.ARRAY_F
heapvar7.dt shouldBe DataType.ARRAY_UB
heapvar8.dt shouldBe DataType.ARRAY_UW
heapvar9.dt shouldBe DataType.ARRAY_F
}
}) })

View File

@ -5,7 +5,6 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldContain
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
@ -42,23 +41,6 @@ class TestVariables: FunSpec({
compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null
} }
test("array initialization with array var assignment") {
val text = """
main {
sub start() {
ubyte[3] @shared arrayvar=main.values1
arrayvar = main.values2
}
ubyte[] values1 = [1,2,3]
ubyte[] values2 = [1,2,3]
}
"""
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("pipe character in string literal") { test("pipe character in string literal") {
val text = """ val text = """
main { main {

View File

@ -299,7 +299,6 @@ always have to be constants. Here are some examples of arrays::
value = array[3] ; the fourth value in the array (index is 0-based) value = array[3] ; the fourth value in the array (index is 0-based)
char = string[4] ; the fifth character (=byte) in the string char = string[4] ; the fifth character (=byte) in the string
char = string[-2] ; the second-to-last character in the string (Python-style indexing from the end) char = string[-2] ; the second-to-last character in the string (Python-style indexing from the end)
flags = [false, true] ; reset all flags in the array
.. note:: .. note::
Right now, the array should be small enough to be indexable by a single byte index. Right now, the array should be small enough to be indexable by a single byte index.
@ -309,14 +308,16 @@ always have to be constants. Here are some examples of arrays::
Arrays can be initialized with a range expression or an array literal value. Arrays can be initialized with a range expression or an array literal value.
You can write out such an initializer value over several lines if you want to improve readability. You can write out such an initializer value over several lines if you want to improve readability.
You can assign a new value to an element in the array, but you can't assign a whole
new array to another array at once. This is usually a costly operation. If you really
need this you have to write it out depending on the use case: you can copy the memory using
``sys.memcopy(sourcearray, targetarray, sizeof(targetarray))``. Or perhaps use ``sys.memset`` instead to
set it all to the same value, or maybe even simply assign the individual elements.
Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.) 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`` can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
for instance. for instance.
It is possible to assign an array (variable or array literal) to another array; this will overwrite all elements in the target
array with those in the source array. The number of elements in the arrays and the data types have to match.
For large arrays this is a slow operation because all values are copied over.
Using the ``in`` operator you can easily check if a value is present in an array, Using the ``in`` operator you can easily check if a value is present in an array,
example: ``if choice in [1,2,3,4] {....}`` example: ``if choice in [1,2,3,4] {....}``
@ -378,8 +379,8 @@ You can concatenate two string literals using '+', which can be useful to
split long strings over separate lines. But remember that the length split long strings over separate lines. But remember that the length
of the total string still cannot exceed 255 characters. of the total string still cannot exceed 255 characters.
A string literal can also be repeated a given number of times using '*', where the repeat number must be a constant value. A string literal can also be repeated a given number of times using '*', where the repeat number must be a constant value.
And a new string value can be assigned to another string, but no bounds check is done And a new string value can be assigned to another string, but no bounds check is done!
so be sure the destination string is large enough to contain the new value (it is overwritten in memory):: So be sure the destination string is large enough to contain the new value (it is overwritten in memory)::
str string1 = "first part" + "second part" str string1 = "first part" + "second part"
str string2 = "hello!" * 10 str string2 = "hello!" * 10

View File

@ -511,8 +511,6 @@ the downto variant to avoid having to specify the step as well::
xx = 10 xx = 10
aa to xx ; range of 5, 6, 7, 8, 9, 10 aa to xx ; range of 5, 6, 7, 8, 9, 10
byte[] array = 10 to 13 ; sets the array to [10, 11, 12, 13]
for i in 0 to 127 { for i in 0 to 127 {
; i loops 0, 1, 2, ... 127 ; i loops 0, 1, 2, ... 127
} }

View File

@ -1,9 +1,7 @@
TODO TODO
==== ====
- should the array-to-array assignment support be removed and instead require an explicit copy function call? What prog8_lib_arraycopy() now does. Or just use memcopy. - fixup syscall list UNUSED_SYSCALL_1 and 2 (numbers shift!)
- should we add a cleararray builtin function that can efficiently set every element in the array to the given value
Improve register load order in subroutine call args assignments: Improve register load order in subroutine call args assignments:
in certain situations, the "wrong" order of evaluation of function call arguments is done which results in certain situations, the "wrong" order of evaluation of function call arguments is done which results

View File

@ -25,7 +25,7 @@ main {
for y in 32 to 199+32 { for y in 32 to 199+32 {
cx16.FB_cursor_position((320-len(cells))/2,y) cx16.FB_cursor_position((320-len(cells))/2,y)
cx16.FB_set_pixels(cells, len(cells)) cx16.FB_set_pixels(cells, len(cells))
cells_previous = cells sys.memcopy(cells, cells_previous, sizeof(cells))
ubyte @zp x ubyte @zp x
for x in 0 to len(cells)-1 { for x in 0 to len(cells)-1 {
cells[x] = generate(x) ; next generation cells[x] = generate(x) ; next generation

View File

@ -1,11 +1,30 @@
%import floats
%import textio
%option no_sysinit
%zeropage basicsafe
main { main {
sub start() { sub start() {
label: uword[4] words1 = [1,2,3,4]
str @shared name = "name" uword[4] words2 = [99,88,77,66]
ubyte @shared bytevar
uword[] @shared array = [name, label, start, main, 9999] for cx16.r0 in words1 {
uword[] @shared array2 = [&name, &label, &start, &main, 9999] txt.print_uw(cx16.r0)
uword[] @shared array3 = [cx16.r0] ; error, is variables txt.spc()
uword[] @shared array4 = [bytevar] ; error, is variables }
txt.nl()
sys.memcopy(words2, words1, sizeof(words1))
for cx16.r0 in words1 {
txt.print_uw(cx16.r0)
txt.spc()
}
txt.nl()
sys.memcopy([2222,3333,4444,5555], words1, sizeof(words1))
for cx16.r0 in words1 {
txt.print_uw(cx16.r0)
txt.spc()
}
txt.nl()
} }
} }

View File

@ -18,7 +18,5 @@ enum class IMSyscall(val number: Int) {
CALLFAR(0x1017), CALLFAR(0x1017),
CALLFAR2(0x1018), CALLFAR2(0x1018),
MEMCOPY(0x1019), MEMCOPY(0x1019),
MEMCOPY_SMALL(0x101a), MEMCOPY_SMALL(0x101a)
ARRAYCOPY_SPLITW_TO_NORMAL(0x101b),
ARRAYCOPY_NORMAL_TO_SPLITW(0x101c),
} }

View File

@ -50,8 +50,8 @@ SYSCALLS:
37 = memset 37 = memset
38 = memsetw 38 = memsetw
39 = stringcopy 39 = stringcopy
40 = ARRAYCOPY_SPLITW_TO_NORMAL 40 = ...unused...
41 = ARRAYCOPY_NORMAL_TO_SPLITW 41 = ...unused...
42 = memcopy_small 42 = memcopy_small
43 = load 43 = load
44 = load_raw 44 = load_raw
@ -103,8 +103,8 @@ enum class Syscall {
MEMSET, MEMSET,
MEMSETW, MEMSETW,
STRINGCOPY, STRINGCOPY,
ARRAYCOPY_SPLITW_TO_NORMAL, UNUSED_SYSCALL_1, // TODO fixup
ARRAYCOPY_NORMAL_TO_SPLITW, UNUSED_SYSCALL_2, // TODO fixup
MEMCOPY_SMALL, MEMCOPY_SMALL,
LOAD, LOAD,
LOAD_RAW, LOAD_RAW,
@ -444,29 +444,6 @@ object SysCalls {
vm.memory.setString(target, string, true) vm.memory.setString(target, string, true)
returnValue(callspec.returns.single(), string.length, vm) returnValue(callspec.returns.single(), string.length, vm)
} }
Syscall.ARRAYCOPY_SPLITW_TO_NORMAL -> {
val (fromLsbA, fromMsbA, targetA, bytecountA) = getArgValues(callspec.arguments, vm)
val fromLsb = (fromLsbA as UShort).toInt()
val fromMsb = (fromMsbA as UShort).toInt()
val target = (targetA as UShort).toInt()
val bytecount = (bytecountA as UByte).toInt()
for(offset in 0..<bytecount) {
vm.memory.setUB(target+offset*2, vm.memory.getUB(fromLsb+offset))
vm.memory.setUB(target+offset*2+1, vm.memory.getUB(fromMsb+offset))
}
}
Syscall.ARRAYCOPY_NORMAL_TO_SPLITW -> {
val (fromA, targetLsbA, targetMsbA, bytecountA) = getArgValues(callspec.arguments, vm)
val from = (fromA as UShort).toInt()
val targetLsb = (targetLsbA as UShort).toInt()
val targetMsb = (targetMsbA as UShort).toInt()
val bytecount = (bytecountA as UByte).toInt()
for(offset in 0..<bytecount) {
vm.memory.setUB(targetLsb+offset, vm.memory.getUB(from+offset*2))
vm.memory.setUB(targetMsb+offset, vm.memory.getUB(from+offset*2+1))
}
}
Syscall.LOAD -> { Syscall.LOAD -> {
val (filenameA, addrA) = getArgValues(callspec.arguments, vm) val (filenameA, addrA) = getArgValues(callspec.arguments, vm)
val filename = vm.memory.getString((filenameA as UShort).toInt()) val filename = vm.memory.getString((filenameA as UShort).toInt())
@ -572,6 +549,9 @@ object SysCalls {
} }
return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30 return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30
} }
Syscall.UNUSED_SYSCALL_1 -> TODO("remove this")
Syscall.UNUSED_SYSCALL_2 -> TODO("remove this")
} }
} }
} }

View File

@ -117,8 +117,6 @@ class VmProgramLoader {
IMSyscall.CALLFAR2.number -> throw IRParseException("vm doesn't support the callfar2() syscall") IMSyscall.CALLFAR2.number -> throw IRParseException("vm doesn't support the callfar2() syscall")
IMSyscall.MEMCOPY.number -> Syscall.MEMCOPY IMSyscall.MEMCOPY.number -> Syscall.MEMCOPY
IMSyscall.MEMCOPY_SMALL.number -> Syscall.MEMCOPY_SMALL IMSyscall.MEMCOPY_SMALL.number -> Syscall.MEMCOPY_SMALL
IMSyscall.ARRAYCOPY_SPLITW_TO_NORMAL.number -> Syscall.ARRAYCOPY_SPLITW_TO_NORMAL
IMSyscall.ARRAYCOPY_NORMAL_TO_SPLITW.number -> Syscall.ARRAYCOPY_NORMAL_TO_SPLITW
else -> null else -> null
} }