From e9edffa9f03d9658af80c15934a780ecd2f10c86 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 13 Oct 2024 19:37:25 +0200 Subject: [PATCH] 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. --- .../src/prog8/code/core/BuiltinFunctions.kt | 1 - .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 107 ----------- .../codegen/intermediate/BuiltinFuncGen.kt | 62 ------ .../compiler/astprocessing/AstChecker.kt | 13 +- .../compiler/astprocessing/CodeDesugarer.kt | 16 -- .../test/codegeneration/TestArrayThings.kt | 181 ------------------ compiler/test/codegeneration/TestVariables.kt | 18 -- docs/source/programming.rst | 15 +- docs/source/syntaxreference.rst | 2 - docs/source/todo.rst | 4 +- examples/cx16/automatons.p8 | 2 +- examples/test.p8 | 33 +++- .../src/prog8/intermediate/IMSyscall.kt | 4 +- virtualmachine/src/prog8/vm/SysCalls.kt | 34 +--- .../src/prog8/vm/VmProgramLoader.kt | 2 - 15 files changed, 48 insertions(+), 446 deletions(-) diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index 926b82046..7e2553c1c 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -79,7 +79,6 @@ val BuiltinFunctions: Map = mapOf( // 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), "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_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), diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index a924993bd..0a582b8f1 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -1,7 +1,5 @@ package prog8.codegen.cpu6502 -import prog8.code.StMemVar -import prog8.code.StStaticVariable import prog8.code.ast.* import prog8.code.core.* import prog8.codegen.cpu6502.assignment.* @@ -71,101 +69,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, "prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister) "prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, 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}") } 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?) { // square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine. 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?) { val signature = BuiltinFunctions.getValue(call.name) val callConv = signature.callConvention(call.args.map { it.type}) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 4b9c9fcc1..979eb853a 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -44,72 +44,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "prog8_lib_stringcompare" -> funcStringCompare(call) "prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE) "prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD) - "prog8_lib_arraycopy" -> funcArrayCopy(call) 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() - 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 { val result = mutableListOf() val valueTr = exprGen.translateExpression(call.args[0]) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index fbe4695b7..0d0dc945f 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1813,14 +1813,9 @@ internal class AstChecker(private val program: Program, sourceValue: Expression) : Boolean { val position = sourceValue.position - if(sourceValue is ArrayLiteral && targetDatatype in ArrayDatatypes) { - val vardecl=target.identifier?.targetVarDecl(program) - val targetSize = vardecl?.arraysize?.constIndex() - if(targetSize!=null) { - if(sourceValue.value.size != targetSize) { - errors.err("array size mismatch (expecting $targetSize, got ${sourceValue.value.size})", sourceValue.position) - } - } + if(sourceValue is ArrayLiteral || targetDatatype in ArrayDatatypes) { + errors.err("cannot assign arrays directly. Maybe use sys.memcopy(src, tgt, sizeof(tgt)) instead.", target.position) + return false } if(sourceValue is RangeExpression) { @@ -1841,7 +1836,7 @@ internal class AstChecker(private val program: Program, DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD DataType.FLOAT -> sourceDatatype in NumericDatatypes DataType.STR -> sourceDatatype == DataType.STR - else -> targetDatatype in ArrayDatatypes && sourceValue is ArrayLiteral + else -> false } if(result) diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index 29620e9ce..07b4f2ed3 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -23,22 +23,6 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep // - pointer[word] replaced by @(pointer+word) // - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word // - 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 { - 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 { fun jumpAfter(stmt: Statement): Iterable { diff --git a/compiler/test/codegeneration/TestArrayThings.kt b/compiler/test/codegeneration/TestArrayThings.kt index f9fd7ba8a..a09cde735 100644 --- a/compiler/test/codegeneration/TestArrayThings.kt +++ b/compiler/test/codegeneration/TestArrayThings.kt @@ -5,10 +5,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.types.instanceOf -import prog8.code.StStaticVariable -import prog8.code.SymbolTableMaker import prog8.code.ast.* -import prog8.code.core.* import prog8.code.target.C64Target import prog8.code.target.VMTarget import prog8tests.helpers.ErrorReporterForTests @@ -156,25 +153,6 @@ main { 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") { val text = """ main { @@ -333,81 +311,6 @@ main { 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") { val src=""" %option enable_floats @@ -497,89 +400,5 @@ label: errors.errors[0] 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 - } - }) diff --git a/compiler/test/codegeneration/TestVariables.kt b/compiler/test/codegeneration/TestVariables.kt index 6c143cbad..cb65c6575 100644 --- a/compiler/test/codegeneration/TestVariables.kt +++ b/compiler/test/codegeneration/TestVariables.kt @@ -5,7 +5,6 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldContain import prog8.code.target.C64Target -import prog8.code.target.VMTarget import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText @@ -42,23 +41,6 @@ class TestVariables: FunSpec({ 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") { val text = """ main { diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 15f831d9b..d55d97c93 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -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) 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) - flags = [false, true] ; reset all flags in the array .. note:: 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. 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.) can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte`` 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, 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 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. -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):: +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):: str string1 = "first part" + "second part" str string2 = "hello!" * 10 diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 12ae61f64..853dabb39 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -511,8 +511,6 @@ the downto variant to avoid having to specify the step as well:: xx = 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 { ; i loops 0, 1, 2, ... 127 } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 3febf0695..cd8555edb 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,9 +1,7 @@ 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. -- should we add a cleararray builtin function that can efficiently set every element in the array to the given value - +- fixup syscall list UNUSED_SYSCALL_1 and 2 (numbers shift!) 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 diff --git a/examples/cx16/automatons.p8 b/examples/cx16/automatons.p8 index 179fe7b48..181ead3ca 100644 --- a/examples/cx16/automatons.p8 +++ b/examples/cx16/automatons.p8 @@ -25,7 +25,7 @@ main { for y in 32 to 199+32 { cx16.FB_cursor_position((320-len(cells))/2,y) cx16.FB_set_pixels(cells, len(cells)) - cells_previous = cells + sys.memcopy(cells, cells_previous, sizeof(cells)) ubyte @zp x for x in 0 to len(cells)-1 { cells[x] = generate(x) ; next generation diff --git a/examples/test.p8 b/examples/test.p8 index d4bd6a495..cf1349a12 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,11 +1,30 @@ +%import floats +%import textio +%option no_sysinit +%zeropage basicsafe + main { + sub start() { -label: - str @shared name = "name" - ubyte @shared bytevar - uword[] @shared array = [name, label, start, main, 9999] - uword[] @shared array2 = [&name, &label, &start, &main, 9999] - uword[] @shared array3 = [cx16.r0] ; error, is variables - uword[] @shared array4 = [bytevar] ; error, is variables + uword[4] words1 = [1,2,3,4] + uword[4] words2 = [99,88,77,66] + + for cx16.r0 in words1 { + txt.print_uw(cx16.r0) + txt.spc() + } + 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() } } diff --git a/intermediate/src/prog8/intermediate/IMSyscall.kt b/intermediate/src/prog8/intermediate/IMSyscall.kt index e3548e542..4b33373e2 100644 --- a/intermediate/src/prog8/intermediate/IMSyscall.kt +++ b/intermediate/src/prog8/intermediate/IMSyscall.kt @@ -18,7 +18,5 @@ enum class IMSyscall(val number: Int) { CALLFAR(0x1017), CALLFAR2(0x1018), MEMCOPY(0x1019), - MEMCOPY_SMALL(0x101a), - ARRAYCOPY_SPLITW_TO_NORMAL(0x101b), - ARRAYCOPY_NORMAL_TO_SPLITW(0x101c), + MEMCOPY_SMALL(0x101a) } diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 0747d35bb..9c9ad1e9e 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -50,8 +50,8 @@ SYSCALLS: 37 = memset 38 = memsetw 39 = stringcopy -40 = ARRAYCOPY_SPLITW_TO_NORMAL -41 = ARRAYCOPY_NORMAL_TO_SPLITW +40 = ...unused... +41 = ...unused... 42 = memcopy_small 43 = load 44 = load_raw @@ -103,8 +103,8 @@ enum class Syscall { MEMSET, MEMSETW, STRINGCOPY, - ARRAYCOPY_SPLITW_TO_NORMAL, - ARRAYCOPY_NORMAL_TO_SPLITW, + UNUSED_SYSCALL_1, // TODO fixup + UNUSED_SYSCALL_2, // TODO fixup MEMCOPY_SMALL, LOAD, LOAD_RAW, @@ -444,29 +444,6 @@ object SysCalls { vm.memory.setString(target, string, true) 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.. { - 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.. { val (filenameA, addrA) = getArgValues(callspec.arguments, vm) 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 } + + Syscall.UNUSED_SYSCALL_1 -> TODO("remove this") + Syscall.UNUSED_SYSCALL_2 -> TODO("remove this") } } } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 60d3363fc..300c0e136 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -117,8 +117,6 @@ class VmProgramLoader { IMSyscall.CALLFAR2.number -> throw IRParseException("vm doesn't support the callfar2() syscall") IMSyscall.MEMCOPY.number -> Syscall.MEMCOPY 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 }