From ceb2c9e4f80ae24ac7fec726165c9c2f0e14ee91 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 5 Jun 2020 23:19:06 +0200 Subject: [PATCH] added string value assignment, leftstr, rightstr, substr functions --- compiler/res/prog8lib/prog8lib.asm | 124 ++++++++++++++++++ compiler/res/version.txt | 2 +- .../target/c64/codegen/AssignmentAsmGen.kt | 15 +++ .../src/prog8/functions/BuiltinFunctions.kt | 15 ++- docs/source/index.rst | 2 +- docs/source/programming.rst | 29 +++- docs/source/todo.rst | 1 - examples/strings.p8 | 36 ----- 8 files changed, 181 insertions(+), 43 deletions(-) delete mode 100644 examples/strings.p8 diff --git a/compiler/res/prog8lib/prog8lib.asm b/compiler/res/prog8lib/prog8lib.asm index bd4fee39d..c80f8ef4f 100644 --- a/compiler/res/prog8lib/prog8lib.asm +++ b/compiler/res/prog8lib/prog8lib.asm @@ -2078,3 +2078,127 @@ ror2_array_uw .proc sta (c64.SCRATCH_ZPWORD1),y + rts .pend + + +strcpy .proc + ; copy a string (0-terminated) from A/Y to (ZPWORD1) + ; it is assumed the target string is large enough. + sta c64.SCRATCH_ZPWORD2 + sty c64.SCRATCH_ZPWORD2+1 + ldy #$ff +- iny + lda (c64.SCRATCH_ZPWORD2),y + sta (c64.SCRATCH_ZPWORD1),y + bne - + rts + .pend + + +func_leftstr .proc + ; leftstr(source, target, length) with params on stack + inx + lda c64.ESTACK_LO,x + tay ; length + inx + lda c64.ESTACK_LO,x + sta c64.SCRATCH_ZPWORD2 + lda c64.ESTACK_HI,x + sta c64.SCRATCH_ZPWORD2+1 + inx + lda c64.ESTACK_LO,x + sta c64.SCRATCH_ZPWORD1 + lda c64.ESTACK_HI,x + sta c64.SCRATCH_ZPWORD1+1 + lda #0 + sta (c64.SCRATCH_ZPWORD2),y +- dey + cpy #$ff + bne + + rts ++ lda (c64.SCRATCH_ZPWORD1),y + sta (c64.SCRATCH_ZPWORD2),y + jmp - + .pend + +func_rightstr .proc + ; rightstr(source, target, length) with params on stack + ; make place for the 4 parameters for substr() + dex + dex + dex + dex + ; X-> . + ; x+1 -> length of segment + ; x+2 -> start index + ; X+3 -> target LO+HI + ; X+4 -> source LO+HI + ; original parameters: + ; x+5 -> original length LO + ; x+6 -> original targetLO + HI + ; x+7 -> original sourceLO + HI + ; replicate paramters: + lda c64.ESTACK_LO+5,x + sta c64.ESTACK_LO+1,x + lda c64.ESTACK_LO+6,x + sta c64.ESTACK_LO+3,x + lda c64.ESTACK_HI+6,x + sta c64.ESTACK_HI+3,x + lda c64.ESTACK_LO+7,x + sta c64.ESTACK_LO+4,x + sta c64.SCRATCH_ZPWORD1 + lda c64.ESTACK_HI+7,x + sta c64.ESTACK_HI+4,x + sta c64.SCRATCH_ZPWORD1+1 + ; determine string length + ldy #0 +- lda (c64.SCRATCH_ZPWORD1),y + beq + + iny + bne - ++ tya + sec + sbc c64.ESTACK_LO+1,x ; start index = strlen - segment length + sta c64.ESTACK_LO+2,x + jsr func_substr + ; unwind original params + inx + inx + inx + rts + .pend + +func_substr .proc + ; substr(source, target, start, length) with params on stack + inx + ldy c64.ESTACK_LO,x ; length + inx + lda c64.ESTACK_LO,x ; start + sta c64.SCRATCH_ZPB1 + inx + lda c64.ESTACK_LO,x + sta c64.SCRATCH_ZPWORD2 + lda c64.ESTACK_HI,x + sta c64.SCRATCH_ZPWORD2+1 + inx + lda c64.ESTACK_LO,x + sta c64.SCRATCH_ZPWORD1 + lda c64.ESTACK_HI,x + sta c64.SCRATCH_ZPWORD1+1 + ; adjust src location + clc + lda c64.SCRATCH_ZPWORD1 + adc c64.SCRATCH_ZPB1 + sta c64.SCRATCH_ZPWORD1 + bcc + + inc c64.SCRATCH_ZPWORD1+1 ++ lda #0 + sta (c64.SCRATCH_ZPWORD2),y + jmp _startloop +- lda (c64.SCRATCH_ZPWORD1),y + sta (c64.SCRATCH_ZPWORD2),y +_startloop dey + cpy #$ff + bne - + rts + + .pend diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 879b416e6..f7873fe01 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -2.1 +2.2-SNAPSHOT diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 374f0106b..ca2c0508f 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -773,6 +773,21 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } } } + DataType.STR -> { + val identifier = assign.value as? IdentifierReference + ?: throw AssemblyError("string value assignment expects identifier value") + val sourceName = asmgen.asmIdentifierName(identifier) + asmgen.out(""" + lda #<$targetName + sta ${C64Zeropage.SCRATCH_W1} + lda #>$targetName + sta ${C64Zeropage.SCRATCH_W1+1} + lda #<$sourceName + ldy #>$sourceName + jsr prog8_lib.strcpy + """) + return true + } else -> throw AssemblyError("assignment to identifier: invalid target datatype: $targetType") } return false diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 44514b804..9dde118d7 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -87,7 +87,20 @@ val BuiltinFunctions = mapOf( FParam("address", IterableDatatypes + DataType.UWORD), FParam("numwords", setOf(DataType.UWORD)), FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null), - "strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen) + "strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen), + "substr" to FSignature(false, listOf( + FParam("source", IterableDatatypes + DataType.UWORD), + FParam("target", IterableDatatypes + DataType.UWORD), + FParam("start", setOf(DataType.UBYTE)), + FParam("length", setOf(DataType.UBYTE))), null), + "leftstr" to FSignature(false, listOf( + FParam("source", IterableDatatypes + DataType.UWORD), + FParam("target", IterableDatatypes + DataType.UWORD), + FParam("length", setOf(DataType.UBYTE))), null), + "rightstr" to FSignature(false, listOf( + FParam("source", IterableDatatypes + DataType.UWORD), + FParam("target", IterableDatatypes + DataType.UWORD), + FParam("length", setOf(DataType.UBYTE))), null) ) fun builtinMax(array: List): Number = array.maxBy { it.toDouble() }!! diff --git a/docs/source/index.rst b/docs/source/index.rst index c9fbbb730..8d5c221be 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -156,7 +156,7 @@ Design principles and features - The compiler tries to optimize the program and generated code a bit, but hand-tuning of the performance or space-critical parts will likely still be required. This is supported by the ability to easily write embedded assembly code directly in the program source code. -- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse`` +- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others) - Assembling the generated code into a program wil be done by an external cross-assembler tool. diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 4f441c179..a8fbd3c47 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -279,16 +279,23 @@ This @-prefix can also be used for character byte values. You can concatenate two string literals using '+' (not very useful though) or repeat -a string literal a given number of times using '*':: +a string literal a given number of times using '*'. You can also assign a new string +value to another string. No bounds check is done so be sure the destination string is +large enough to contain the new value:: str string1 = "first part" + "second part" str string2 = "hello!" * 10 + string1 = string2 + string1 = "new value" + .. caution:: - Avoid changing strings after they've been created. + It's probably best to avoid changing strings after they've been created. This + includes changing certain letters by index, or by assigning a new value, or by + modifying the string via other means for example ``substr`` function and its cousins. This is because if your program exits and is restarted (without loading it again), - it will then start working with the changed strings instead of the original ones. + it will then start working with the changed strings instead of the original ones! The same is true for arrays. @@ -802,6 +809,22 @@ memsetw(address, numwords, wordvalue) Efficiently set a part of memory to the given (u)word value. But the most efficient will always be to write a specialized fill routine in assembly yourself! +leftstr(source, target, length) + Copies the left side of the source string of the given length to target string. + It is assumed the target string buffer is large enough to contain the result. + Modifies in-place, doesn't return a value (so can't be used in an expression). + +rightstr(source, target, length) + Copies the right side of the source string of the given length to target string. + It is assumed the target string buffer is large enough to contain the result. + Modifies in-place, doesn't return a value (so can't be used in an expression). + +substr(source, target, start, length) + Copies a segment from the source string, starting at the given index, + and of the given length to target string. + It is assumed the target string buffer is large enough to contain the result. + Modifies in-place, doesn't return a value (so can't be used in an expression). + swap(x, y) Swap the values of numerical variables (or memory locations) x and y in a fast way. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 48a5b6786..ee39a50c4 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO ==== - finalize (most) of the still missing "new" assignment asm code generation - - aliases for imported symbols for example perhaps '%alias print = c64scr.print' - option to load library files from a directory instead of the embedded ones (easier library development/debugging) - investigate support for 8bitguy's Commander X16 platform https://murray2.com/forums/commander-x16.9/ and https://github.com/commanderx16/x16-docs diff --git a/examples/strings.p8 b/examples/strings.p8 deleted file mode 100644 index f899061a0..000000000 --- a/examples/strings.p8 +++ /dev/null @@ -1,36 +0,0 @@ -%import c64lib -%zeropage basicsafe - -main { - -sub start() { - - str s1 = "apple" - str s2 = "banana" - byte[] a1 = [66,77,88,0] - ubyte i1 = 101 - uword w1 = 000 - - c64.STROUT(s1) - c64.CHROUT('\n') - c64.STROUT(a1) - c64.CHROUT('\n') - - c64scr.print("bla\n") - -; c64scr.print_uwhex(s1, true) -; w1 = &s1 -; c64scr.print_uwhex(w1, true) -; -; c64scr.print_uwhex(a1, true) -; w1 = &a1 -; c64scr.print_uwhex(w1, true) -; -; s1 = s1 -; s1 = s2 -; s2 = "zzz" - -} -} - -