diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index f2c31d629..80271044c 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -70,6 +70,8 @@ class FSignature(val pure: Boolean, // does it have side effects? val BuiltinFunctions: Map = mapOf( // this set of function have no return value and operate in-place: + "setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null), + "setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null), "rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), @@ -131,4 +133,4 @@ val BuiltinFunctions: Map = mapOf( "callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD), ) -val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse") +val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 1bea01aac..c2aeda7e5 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -41,6 +41,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, "rol2" -> funcRol2(fcall) "ror" -> funcRor(fcall) "ror2" -> funcRor2(fcall) + "setlsb" -> funcSetLsbMsb(fcall, false) + "setmsb" -> funcSetLsbMsb(fcall, true) "sort" -> funcSort(fcall) "reverse" -> funcReverse(fcall) "memory" -> funcMemory(fcall, discardResult, resultRegister) @@ -609,6 +611,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE) } + private fun funcSetLsbMsb(fcall: PtBuiltinFunctionCall, msb: Boolean) { + TODO("setlsb/setmsb for $fcall") + } + private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { translateArguments(fcall, scope) val dt = fcall.args.single().type diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index e0d84d044..6e19ce88f 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -40,6 +40,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call) "sort" -> funcSort(call) "reverse" -> funcReverse(call) + "setlsb" -> funcSetLsbMsb(call, false) + "setmsb" -> funcSetLsbMsb(call, true) "rol" -> funcRolRor(Opcode.ROXL, call) "ror" -> funcRolRor(Opcode.ROXR, call) "rol2" -> funcRolRor(Opcode.ROL, call) @@ -576,6 +578,78 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe return ExpressionCodeResult(result, vmDt, -1, -1) } + private fun funcSetLsbMsb(call: PtBuiltinFunctionCall, msb: Boolean): ExpressionCodeResult { + val result = mutableListOf() + val target = call.args[0] + when(target) { + is PtIdentifier -> { + val valueTr = exprGen.translateExpression(call.args[1]) + addToResult(result, valueTr, valueTr.resultReg, -1) + result += IRCodeChunk(null, null).also { + val pointerReg = codeGen.registers.nextFree() + it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=pointerReg, labelSymbol = target.name) + if(msb) + it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1=pointerReg) + it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=pointerReg) + // TODO use STOREZI if the value is zero + } + } + is PtArrayIndexer -> { + require(!target.usesPointerVariable) + if(target.splitWords) { + val varName = target.variable.name + if(msb) "_msb" else "_lsb" + val valueTr = exprGen.translateExpression(call.args[1]) + addToResult(result, valueTr, valueTr.resultReg, -1) + val constIndex = target.index.asConstInteger() + if(constIndex!=null) { + val offsetReg = codeGen.registers.nextFree() + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex) + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName) + // TODO: use STOREZX if the value is zero + } + } else { + val indexTr = exprGen.translateExpression(target.index) + addToResult(result, indexTr, indexTr.resultReg, -1) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = varName) + // TODO: use STOREZX if the value is zero + } + } + } + else { + val valueTr = exprGen.translateExpression(call.args[1]) + addToResult(result, valueTr, valueTr.resultReg, -1) + val eltSize = codeGen.program.memsizer.memorySize(target.type) + val constIndex = target.index.asConstInteger() + if(constIndex!=null) { + val offsetReg = codeGen.registers.nextFree() + val offset = eltSize*constIndex + if(msb) 1 else 0 + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset) + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name) + // TODO: use STOREZX if the value is zero + } + } else { + val indexTr = exprGen.translateExpression(target.index) + addToResult(result, indexTr, indexTr.resultReg, -1) + result += IRCodeChunk(null, null).also { + if(eltSize>1) + it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize) + if(msb) + it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg) + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name) + // TODO: use STOREZX if the value is zero + } + } + } + } + else -> throw AssemblyError("weird target for setlsb/setmsb: $target") + } + return ExpressionCodeResult(result, IRDataType.WORD, -1, -1) + } + + private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks { val assignment = PtAssignment(target.position) val assignTarget = PtAssignTarget(target.position) diff --git a/compiler/src/prog8/buildversion/BuildVersion.kt b/compiler/src/prog8/buildversion/BuildVersion.kt index 3087b34ae..e875d0151 100644 --- a/compiler/src/prog8/buildversion/BuildVersion.kt +++ b/compiler/src/prog8/buildversion/BuildVersion.kt @@ -6,10 +6,10 @@ package prog8.buildversion const val MAVEN_GROUP = "prog8" const val MAVEN_NAME = "compiler" const val VERSION = "9.5-SNAPSHOT" -const val GIT_REVISION = 4097 -const val GIT_SHA = "UNKNOWN" -const val GIT_DATE = "2023-09-08T19:29:49Z" +const val GIT_REVISION = 4101 +const val GIT_SHA = "31c132c2ebfef803ae197489fc7eaf56642501ac" +const val GIT_DATE = "2023-09-14T21:04:23Z" const val GIT_BRANCH = "master" -const val BUILD_DATE = "2023-09-12T19:47:42Z" -const val BUILD_UNIX_TIME = 1694548062963L +const val BUILD_DATE = "2023-09-14T21:06:25Z" +const val BUILD_UNIX_TIME = 1694725585234L const val DIRTY = 1 diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index ff7d2773d..00a5068a9 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1183,8 +1183,13 @@ internal class AstChecker(private val program: Program, if(funcName[0] in InplaceModifyingBuiltinFunctions) { // in-place modification, can't be done on literals - 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) + 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", functionCallStatement.args.first().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/programming.rst b/docs/source/programming.rst index 289c64f90..54abdbaf0 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -909,6 +909,12 @@ ror2 (x) Modifies in-place, doesn't return a value (so can't be used in an expression). You can ror a memory location directly by using the direct memory access syntax, so like ``ror2(@($5000))`` +setlsb (x, value) + Sets the least significant byte of word variable x to a new value. Leaves the MSB untouched. + +setmsb (x, value) + Sets the most significant byte of word variable x to a new value. Leaves the LSB untouched. + sizeof (name) Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of the object. For instance, for a variable of type uword, the sizeof is 2. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 742fdf76c..4c5bb6325 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,8 @@ TODO ==== +- allow taking address of array variable (now gives parser error) + - [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... - IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction - IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified! diff --git a/examples/test.p8 b/examples/test.p8 index 9eec7cce7..682ead226 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,20 +3,31 @@ main { sub start() { - uword zz = 0 + uword zz = $ea45 + txt.print_uwhex(zz, true) + txt.nl() - @(&zz) = 1 - txt.print_uw(zz) + setlsb(zz, $11) + txt.print_uwhex(zz, true) txt.nl() - @(&zz+1) = 2 - txt.print_uw(zz) + setmsb(zz, $22) + txt.print_uwhex(zz, true) txt.nl() - ubyte bb - bb = @(&zz) - txt.print_ub(bb) txt.nl() - bb = @(&zz+1) - txt.print_ub(bb) + + uword[] array = [$1234,$5678,$abcd] ; TODO also with @split + + ubyte one = 1 + ubyte two = 2 + txt.print_uwhex(array[1], true) + txt.nl() + txt.print_uwhex(array[2], true) + txt.nl() + setlsb(array[one],$ff) + setmsb(array[two],$00) + txt.print_uwhex(array[1], true) + txt.nl() + txt.print_uwhex(array[2], true) txt.nl() } } diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index cdbfaba25..a664e7131 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -189,6 +189,7 @@ expression : arrayindexed: scoped_identifier arrayindex // TODO to allow chained array indexing: | arrayindexed arrayindex + // TODO or even to allow array indexing on any uword address value: | expression arrayindex ; diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index b8f3b5169..a304a545c 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -14,7 +14,7 @@ - +