From ae2d96c4558085da2559d321a2a7a4d15993f5fa Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 27 Nov 2023 23:26:31 +0100 Subject: [PATCH] added `peekf` and `pokef` builtin functions. Fixed sizeof() to allow number argument as well. --- .../src/prog8/code/core/BuiltinFunctions.kt | 2 + .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 26 ++++ .../codegen/intermediate/BuiltinFuncGen.kt | 115 +++++++----------- .../src/prog8/compiler/BuiltinFunctions.kt | 9 +- .../compiler/astprocessing/AstChecker.kt | 7 ++ docs/source/programming.rst | 11 +- docs/source/todo.rst | 4 + examples/test.p8 | 56 +++++---- syntax-files/IDEA/Prog8.xml | 2 +- syntax-files/NotepadPlusPlus/Prog8.xml | 2 +- syntax-files/Vim/prog8_builtins.vim | 2 +- 11 files changed, 133 insertions(+), 103 deletions(-) diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index 878eb4c1d..1bf78e375 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -120,8 +120,10 @@ val BuiltinFunctions: Map = mapOf( "max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD), "peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), "peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), + "peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT), "poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), "pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), + "pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null), "pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), "pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null), "popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null), diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 0adfed014..1ce45d592 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -47,8 +47,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, "reverse" -> funcReverse(fcall) "memory" -> funcMemory(fcall, discardResult, resultRegister) "peekw" -> funcPeekW(fcall, resultRegister) + "peekf" -> funcPeekF(fcall, resultRegister) "peek" -> throw AssemblyError("peek() should have been replaced by @()") "pokew" -> funcPokeW(fcall) + "pokef" -> funcPokeF(fcall) "pokemon" -> { val memread = PtMemoryByte(fcall.position) memread.add(fcall.args[0]) @@ -764,6 +766,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } } + private fun funcPokeF(fcall: PtBuiltinFunctionCall) { + val tempvar = asmgen.getTempVarName(DataType.FLOAT) + asmgen.assignExpressionTo(fcall.args[1], + AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, tempvar, null, null, null, null)) + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) + asmgen.out(""" + pha + lda #<$tempvar + sta P8ZP_SCRATCH_W1 + lda #>$tempvar + sta P8ZP_SCRATCH_W1+1 + pla + jsr floats.copy_float""") + } + private fun funcPokeW(fcall: PtBuiltinFunctionCall) { when(val addrExpr = fcall.args[0]) { is PtNumber -> { @@ -820,6 +837,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, asmgen.out(" jsr prog8_lib.func_pokew") } + private fun funcPeekF(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) + asmgen.out(" jsr floats.MOVFM") + if(resultRegister!=null) { + assignAsmGen.assignFAC1float( + AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null)) + } + } + private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { fun fallback() { asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 13b67fbfa..742536daf 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -29,10 +29,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "msb" -> funcMsb(call) "lsb" -> funcLsb(call) "memory" -> funcMemory(call) - "peek" -> funcPeek(call) - "peekw" -> funcPeekW(call) - "poke" -> funcPoke(call) - "pokew" -> funcPokeW(call) + "peek" -> funcPeek(call, IRDataType.BYTE) + "peekw" -> funcPeek(call, IRDataType.WORD) + "peekf" -> funcPeek(call, IRDataType.FLOAT) + "poke" -> funcPoke(call, IRDataType.BYTE) + "pokew" -> funcPoke(call, IRDataType.WORD) + "pokef" -> funcPoke(call, IRDataType.FLOAT) "pokemon" -> funcPokemon(call) "mkword" -> funcMkword(call) "clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call) @@ -440,116 +442,93 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe return ExpressionCodeResult(result, type, leftTr.resultReg, -1) } - private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult { + private fun funcPoke(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult { val result = mutableListOf() if(codeGen.isZero(call.args[1])) { if (call.args[0] is PtNumber) { val address = (call.args[0] as PtNumber).number.toInt() result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, address = address) + it += IRInstruction(Opcode.STOREZM, dt, address = address) } } else { val tr = exprGen.translateExpression(call.args[0]) addToResult(result, tr, tr.resultReg, -1) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = tr.resultReg) + it += IRInstruction(Opcode.STOREZI, dt, reg1 = tr.resultReg) } } } else { if (call.args[0] is PtNumber) { val address = (call.args[0] as PtNumber).number.toInt() val tr = exprGen.translateExpression(call.args[1]) - addToResult(result, tr, tr.resultReg, -1) - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, address = address) + if(dt==IRDataType.FLOAT) { + addToResult(result, tr, -1, tr.resultFpReg) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.STOREM, dt, fpReg1 = tr.resultFpReg, address = address) + } + } else { + addToResult(result, tr, tr.resultReg, -1) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.STOREM, dt, reg1 = tr.resultReg, address = address) + } } } else { val addressTr = exprGen.translateExpression(call.args[0]) addToResult(result, addressTr, addressTr.resultReg, -1) val valueTr = exprGen.translateExpression(call.args[1]) - addToResult(result, valueTr, valueTr.resultReg, -1) - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg) + if(dt==IRDataType.FLOAT) { + addToResult(result, valueTr, -1, valueTr.resultFpReg) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.STOREI, IRDataType.FLOAT, reg1 = addressTr.resultReg, fpReg1 = valueTr.resultFpReg) + } + } else { + addToResult(result, valueTr, valueTr.resultReg, -1) + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.STOREI, dt, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg) + } } } } return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) } - private fun funcPoke(call: PtBuiltinFunctionCall): ExpressionCodeResult { + private fun funcPeek(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult { val result = mutableListOf() - if(codeGen.isZero(call.args[1])) { - if (call.args[0] is PtNumber) { + return if(dt==IRDataType.FLOAT) { + if(call.args[0] is PtNumber) { + val resultFpRegister = codeGen.registers.nextFreeFloat() val address = (call.args[0] as PtNumber).number.toInt() result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address) + it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address) } + ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpRegister) } else { - val tr = exprGen.translateExpression(call.args[0]) + val tr = exprGen.translateExpression(call.args.single()) addToResult(result, tr, tr.resultReg, -1) + val resultFpReg = codeGen.registers.nextFreeFloat() result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = tr.resultReg) + it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg) } + ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg) } } else { if (call.args[0] is PtNumber) { + val resultRegister = codeGen.registers.nextFree() val address = (call.args[0] as PtNumber).number.toInt() - val tr = exprGen.translateExpression(call.args[1]) - addToResult(result, tr, tr.resultReg, -1) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address) + it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address) } + ExpressionCodeResult(result, dt, resultRegister, -1) } else { - val addressTr = exprGen.translateExpression(call.args[0]) - addToResult(result, addressTr, addressTr.resultReg, -1) - val valueTr = exprGen.translateExpression(call.args[1]) - addToResult(result, valueTr, valueTr.resultReg, -1) + val tr = exprGen.translateExpression(call.args.single()) + addToResult(result, tr, tr.resultReg, -1) + val resultReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg) + it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg) } + ExpressionCodeResult(result, dt, resultReg, -1) } } - return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) - } - - private fun funcPeekW(call: PtBuiltinFunctionCall): ExpressionCodeResult { - val result = mutableListOf() - return if(call.args[0] is PtNumber) { - val resultRegister = codeGen.registers.nextFree() - val address = (call.args[0] as PtNumber).number.toInt() - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, address = address) - } - ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) - } else { - val tr = exprGen.translateExpression(call.args.single()) - addToResult(result, tr, tr.resultReg, -1) - val resultReg = codeGen.registers.nextFree() - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg) - } - ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1) - } - } - - private fun funcPeek(call: PtBuiltinFunctionCall): ExpressionCodeResult { - val result = mutableListOf() - return if(call.args[0] is PtNumber) { - val resultRegister = codeGen.registers.nextFree() - val address = (call.args[0] as PtNumber).number.toInt() - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = address) - } - ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) - } else { - val tr = exprGen.translateExpression(call.args.single()) - addToResult(result, tr, tr.resultReg, -1) - val resultReg = codeGen.registers.nextFree() - result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg) - } - ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) - } } private fun funcPokemon(call: PtBuiltinFunctionCall): ExpressionCodeResult { diff --git a/compiler/src/prog8/compiler/BuiltinFunctions.kt b/compiler/src/prog8/compiler/BuiltinFunctions.kt index 20f77c83d..f6073da7c 100644 --- a/compiler/src/prog8/compiler/BuiltinFunctions.kt +++ b/compiler/src/prog8/compiler/BuiltinFunctions.kt @@ -105,14 +105,17 @@ private fun builtinAbs(args: List, position: Position, program: Prog } private fun builtinSizeof(args: List, position: Position, program: Program): NumericLiteral { - // 1 arg, type = anything, result type = ubyte + // 1 arg, type = anything, result type = ubyte or uword if(args.size!=1) throw SyntaxError("sizeof requires one argument", position) - if(args[0] !is IdentifierReference) - throw SyntaxError("sizeof argument should be an identifier", position) + if(args[0] !is IdentifierReference && args[0] !is NumericLiteral) + throw SyntaxError("sizeof argument should be an identifier or number", position) val dt = args[0].inferType(program) if(dt.isKnown) { + if(args[0] is NumericLiteral) + return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOr(DataType.UNDEFINED)), position) + val target = (args[0] as IdentifierReference).targetStatement(program) ?: throw CannotEvaluateException("sizeof", "no target") diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index d736734f5..26d25f51b 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1216,6 +1216,13 @@ internal class AstChecker(private val program: Program, } private fun checkFunctionCall(target: Statement, args: List, position: Position) { + if(target is BuiltinFunctionPlaceholder) { + if(!compilerOptions.floats) { + if (target.name == "peekf" || target.name == "pokef") + errors.err("floating point used, but that is not enabled via options", position) + } + } + if(target is Label && args.isNotEmpty()) errors.err("cannot use arguments when calling a label", position) diff --git a/docs/source/programming.rst b/docs/source/programming.rst index a4ef38775..543861d15 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -861,12 +861,18 @@ peek (address) peekw (address) reads the word value at the given address in memory. Word is read as usual little-endian lsb/msb byte order. +peekf (address) + reads the float value at the given address in memory. On CBM machines, this reads 5 bytes. + poke (address, value) same as @(address)=value - writes the byte value at the given address in memory. pokew (address, value) writes the word value at the given address in memory, in usual little-endian lsb/msb byte order. +pokef (address, value) + writes the float value at the given address in memory. On CBM machines, this writes 5 bytes. + pokemon (address, value) Like poke(), but also returns the previous value in the given address. Also doesn't have anything to do with a certain video game. @@ -919,8 +925,9 @@ setlsb (x, value) 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 +sizeof (name) ; sizeof (number) + Number of bytes that the object 'name', or the number 'number' 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. For an 10 element array of floats, it is 50 (on the C64, where a float is 5 bytes). Note: usually you will be interested in the number of elements in an array, use len() for that. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 3e13a8890..6b7a485ec 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,6 +2,10 @@ TODO ==== +- fix floats.parse_f - did the routine move in kernal rom? + VAL at $ddef. Looks like it's now $deb3 (see basic.sym from x16-rom) + so assuming its internals hasen't changed you need to change the $ddf2 to $deb6? + - [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... ... diff --git a/examples/test.p8 b/examples/test.p8 index 6392e6319..132d8a4af 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,37 +4,39 @@ main { sub start() { - float[51] p1 - float[51] p2 - float[51] p3 - float[51] p4 + ubyte[100] storage = 0 - ubyte idx = 2 - float fl = 3.455 - p1[idx+1] = fl - floats.print_f(p1[idx+1]) - p1[idx+1] = 0.0 - floats.print_f(p1[idx+1]) - - store_prime(1, 2.987654321) - store_prime(52, 3.14159) - - floats.print_f(p1[1]) - txt.nl() - floats.print_f(p2[2]) + txt.print("sizeof float = ") + txt.print_ub(sizeof(0.0)) + txt.print("\nsizeof word = ") + txt.print_ub(sizeof($0000)) + txt.print("\nsizeof byte = ") + txt.print_ub(sizeof($00)) + txt.print("\nsizeof bool = ") + txt.print_ub(sizeof(true)) txt.nl() + poke(&storage+10, 123) + pokew(&storage+11, 54321) + txt.print_ub(peek(&storage+10)) + txt.spc() + txt.print_uw(peekw(&storage+11)) + txt.nl() - sub store_prime(ubyte idx, float pr) { - if idx >= 150 { - p4[idx - 150] = pr - } else if idx >= 100 { - p3[idx - 100] = pr - } else if idx >= 50 { - p2[idx - 50] = pr - } else { - p1[idx] = pr - } + pokef(&storage+10, 3.14) + pokef($4000, 123.456) + floats.print_f(peekf(&storage+10)) + txt.nl() + floats.print_f(peekf($4000)) + txt.nl() + pokef(&storage+10, 3.1415927) + floats.print_f(peekf(&storage+10)) + txt.nl() + + for cx16.r2L in 0 to 20 { + txt.print_ubhex(storage[cx16.r2L], false) + txt.spc() } + txt.nl() } } diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index a197516af..b918ebe0d 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -14,7 +14,7 @@ - + diff --git a/syntax-files/NotepadPlusPlus/Prog8.xml b/syntax-files/NotepadPlusPlus/Prog8.xml index 9847d6318..d8856415e 100644 --- a/syntax-files/NotepadPlusPlus/Prog8.xml +++ b/syntax-files/NotepadPlusPlus/Prog8.xml @@ -27,7 +27,7 @@ void const str byte ubyte bool word uword float zp shared split requirezp %address %asm %ir %asmbinary %asminclude %breakpoint %import %launcher %option %output %zeropage %zpreserved %zpallowed inline sub asmsub romsub clobbers asm if when else if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z for in step do while repeat unroll break continue return goto - abs all any callfar clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw poke pokew push pushw pop popw rsave rsavex rrestore rrestorex reverse rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sort sqrtw swap + abs all any callfar clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef push pushw pop popw rsave rsavex rrestore rrestorex reverse rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sort sqrtw swap true false not and or xor as to downto |> diff --git a/syntax-files/Vim/prog8_builtins.vim b/syntax-files/Vim/prog8_builtins.vim index 0847d77a3..e37aeb919 100644 --- a/syntax-files/Vim/prog8_builtins.vim +++ b/syntax-files/Vim/prog8_builtins.vim @@ -13,7 +13,7 @@ syn keyword prog8BuiltInFunc sgn sqrtw syn keyword prog8BuiltInFunc any all len reverse sort " Miscellaneous functions -syn keyword prog8BuiltInFunc cmp divmod lsb msb mkword min max peek peekw poke pokew push pushw pop popw rsave rsavex rrestore rrestorex +syn keyword prog8BuiltInFunc cmp divmod lsb msb mkword min max peek peekw peekf poke pokew pokef push pushw pop popw rsave rsavex rrestore rrestorex syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof setlsb setmsb syn keyword prog8BuiltInFunc swap memory callfar clamp