From fe011de934bb2fdbd73e76ee5b932d8601d99e2a Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 25 Dec 2024 16:07:09 +0100 Subject: [PATCH] fix the missing cases in certain expressions that need the address of a split word array --- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 26 ---------- .../src/prog8/codegen/cpu6502/IfElseAsmGen.kt | 26 +++++----- .../cpu6502/assignment/AssignmentAsmGen.kt | 51 +++++++++++-------- .../codegen/intermediate/AssignmentGen.kt | 4 +- .../codegen/intermediate/ExpressionGen.kt | 3 +- .../compiler/astprocessing/AstChecker.kt | 13 +++-- docs/source/todo.rst | 6 ++- examples/test.p8 | 17 +++---- 8 files changed, 70 insertions(+), 76 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index bc464b32f..44efe3328 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -644,32 +644,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else "" target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname) } - is PtNumber -> { - val num = (fcall.args[0] as PtNumber).number + if(msb) 1 else 0 - val mem = PtMemoryByte(fcall.position) - mem.add(PtNumber(BaseDataType.UBYTE, num, fcall.position)) - target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem) - } - is PtAddressOf -> { - val addrof = fcall.args[0] as PtAddressOf - if(addrof.identifier.type.isSplitWordArray) { - TODO("address of split word array") - } else { - 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) - } - - } is PtArrayIndexer -> { val indexer = fcall.args[0] as PtArrayIndexer val elementSize: Int diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt index 9febd85e7..b9fe6c9e8 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt @@ -808,11 +808,11 @@ _jump jmp (${target.asmLabel}) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true) asmgen.out(" cpy #0") } else { + var varname = asmgen.asmVariableName(value.identifier) if(value.identifier.type.isSplitWordArray) { - TODO("address of split word array") - } else { - asmgen.out(" lda #>${asmgen.asmVariableName(value.identifier)}") + varname += if(value.isMsbForSplitArray) "_msb" else "_lsb" } + asmgen.out(" lda #>$varname") } } else -> { @@ -1607,13 +1607,13 @@ _jump jmp (${target.asmLabel}) if(left.isFromArrayElement) fallbackTranslateForSimpleCondition(stmt) else { - if(left.identifier.type.isSplitWordArray) { - TODO("address of split word array") + val varname = if(left.identifier.type.isSplitWordArray) { + if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb" } else { - asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) - val varname = left.identifier.name - translateAYNotEquals("#<$varname", "#>$varname") + left.identifier.name } + asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) + translateAYNotEquals("#<$varname", "#>$varname") } } else -> { @@ -1659,13 +1659,13 @@ _jump jmp (${target.asmLabel}) if(left.isFromArrayElement) fallbackTranslateForSimpleCondition(stmt) else { - if(left.identifier.type.isSplitWordArray) { - TODO("address of split word array") + val varname = if(left.identifier.type.isSplitWordArray) { + if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb" } else { - asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) - val varname = left.identifier.name - translateAYEquals("#<$varname", "#>$varname") + left.identifier.name } + asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) + translateAYEquals("#<$varname", "#>$varname") } } else -> { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index a1080d3fb..08a539253 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -1341,13 +1341,12 @@ internal class AssignmentAsmGen( when (right) { is PtAddressOf -> { - val symbol = asmgen.asmVariableName(right.identifier) + var symbol = asmgen.asmVariableName(right.identifier) 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 + symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb" } assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned) if(expr.operator=="+") @@ -2579,7 +2578,8 @@ $endLabel""") private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, arrayDt: DataType?, arrayIndexExpr: PtExpression?) { if(arrayIndexExpr!=null) { - require(arrayDt?.isSplitWordArray!=true) + if(arrayDt?.isSplitWordArray==true) + TODO("address of element of a split word array") val constIndex = arrayIndexExpr.asConstInteger() if(constIndex!=null) { if (arrayDt?.isUnsignedWord==true) { @@ -2930,14 +2930,12 @@ $endLabel""") storeRegisterAInMemoryAddress(target.memory!!) } TargetStorageKind.ARRAY -> { - if(target.array!!.splitWords) - TODO("assign into split words ${target.position}") if (target.constArrayIndexValue!=null) { val scaledIdx = program.memsizer.memorySize(target.datatype, target.constArrayIndexValue!!.toInt()) asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") } else { - asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y) + asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y) asmgen.out(" lda $sourceName | sta ${target.asmVarname},y") } } @@ -2979,19 +2977,33 @@ $endLabel""") """) } TargetStorageKind.ARRAY -> { - if(wordtarget.array!!.splitWords) - TODO("assign byte into split words ${wordtarget.position}") - if (wordtarget.constArrayIndexValue!=null) { - val scaledIdx = wordtarget.constArrayIndexValue!! * 2u - asmgen.out(" lda $sourceName") - asmgen.signExtendAYlsb(BaseDataType.BYTE) - asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1") + if(wordtarget.array!!.splitWords) { + // signed byte, we must sign-extend + if (wordtarget.constArrayIndexValue!=null) { + val scaledIdx = wordtarget.constArrayIndexValue!! + asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}_lsb+$scaledIdx") + asmgen.signExtendAYlsb(BaseDataType.BYTE) + asmgen.out(" sty ${wordtarget.asmVarname}_msb+$scaledIdx") + } + else { + asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X) + asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}_msb,x") + asmgen.signExtendAYlsb(BaseDataType.BYTE) + asmgen.out(" tya | sta ${wordtarget.asmVarname}_msb,x") + } } else { - asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X) - asmgen.out(" lda $sourceName") - asmgen.signExtendAYlsb(BaseDataType.BYTE) - asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x") + if (wordtarget.constArrayIndexValue != null) { + val scaledIdx = wordtarget.constArrayIndexValue!! * 2u + asmgen.out(" lda $sourceName") + asmgen.signExtendAYlsb(BaseDataType.BYTE) + asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1") + } else { + asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X) + asmgen.out(" lda $sourceName") + asmgen.signExtendAYlsb(BaseDataType.BYTE) + asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x") + } } } TargetStorageKind.REGISTER -> { @@ -3700,8 +3712,7 @@ $endLabel""") storeRegisterAInMemoryAddress(target.memory!!) } TargetStorageKind.ARRAY -> { - if(target.array!!.splitWords) - TODO("assign into split words ${target.position}") + require(!target.array!!.splitWords) if (target.constArrayIndexValue!=null) { val indexValue = target.constArrayIndexValue!! asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue") diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index 85b365a4c..38c2a1ca9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -904,10 +904,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express } return result } else { - return null // TODO("inplace split word array +") + return null // TODO("inplace split word array -") } } - return null // TODO("inplace split word array +") + return null // TODO("inplace split word array -") } private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 0743c4d4a..d5324eca9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -133,7 +133,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val result = mutableListOf() val resultRegister = codeGen.registers.nextFree() if(expr.isFromArrayElement) { - require(!expr.identifier.type.isSplitWordArray) + if(expr.identifier.type.isSplitWordArray) + TODO("address of element of a split word array") addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null) val indexTr2 = translateExpression(expr.arrayIndexExpr!!) addToResult(result, indexTr2, indexTr2.resultReg, -1) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index ec4119704..4d5367f87 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -703,7 +703,12 @@ 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(addressOf.arrayIndex!=null && variable.datatype.isSplitWordArray) { + errors.err("cannot take the adress of a word element that is in a split-word array", addressOf.position) + } } + super.visit(addressOf) } @@ -1467,18 +1472,18 @@ internal class AstChecker(private val program: Program, } if(funcName[0] in InplaceModifyingBuiltinFunctions) { - // in-place modification, can't be done on literals + // in-place modification, can be done on specific types of arguments only (variables, array elements) if(funcName[0]=="setlsb" || funcName[0]=="setmsb") { val firstArg = functionCallStatement.args[0] if(firstArg !is IdentifierReference && firstArg !is ArrayIndexedExpression) - errors.err("invalid argument to a in-place modifying function", firstArg.position) + errors.err("this function can only act on an identifier or array element", firstArg.position) } else if(funcName[0]=="divmod" || funcName[0].startsWith("divmod__")) { val thirdArg = functionCallStatement.args[2] val fourthArg = functionCallStatement.args[3] if(thirdArg !is IdentifierReference && thirdArg !is ArrayIndexedExpression) - errors.err("invalid argument to a in-place modifying function", thirdArg.position) + errors.err("this function can only act on an identifier or array element", thirdArg.position) if(fourthArg !is IdentifierReference && fourthArg !is ArrayIndexedExpression) - errors.err("invalid argument to a in-place modifying function", fourthArg.position) + errors.err("this function can only act on an identifier or array element", fourthArg.position) } else { if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 448b09ed3..d5d7e67e6 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -10,6 +10,9 @@ TODO Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ +- support &, &< and &> on array elements from split word arrays too not just the array as a whole (to get rid of the error "&< is only valid on array variables" + and "cannot take the adress of a word element that is in a split-word array" and the TODOS "address of element of a split word array") +- fix leftover asmgen split word array todo's - Kotlin: can we use inline value classes in certain spots? - Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions @@ -28,7 +31,6 @@ Future Things and Ideas - Allow normal subroutines to return multiple values as well (just as asmsubs already can) - Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first) -- Fix missing cases where regular & has to return the start of the split array in memory whatever byte comes first. Search TODO("address of split word array") - something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using '? - Improve register load order in subroutine call args assignments: in certain situations (need examples!), the "wrong" order of evaluation of function call arguments is done which results @@ -47,6 +49,8 @@ Future Things and Ideas IR/VM ----- +- fix TODO("IR rol/ror on split words array") +- fix "<< in array" / ">> in array" - implement missing operators in AssignmentGen (array shifts etc) - support %align on code chunks - fix call() return value handling diff --git a/examples/test.p8 b/examples/test.p8 index 512efea47..a14e0be18 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,18 +5,17 @@ main { sub start() { - &ubyte mmvar = $2000 + uword[2] array1 - txt.print_ub(@($2000)) + array1[1] = $0122 + txt.print_uwhex(array1[1], true) txt.nl() - @($2000) = 123 - txt.print_ub(@($2000)) + rol(array1[1]) + txt.print_uwhex(array1[1], true) txt.nl() - - mmvar = 42 - txt.print_ub(@($2000)) + sys.set_carry() + ror(array1[1]) + txt.print_uwhex(array1[1], true) txt.nl() - - cx16.r0 = 123 } }