From 58f37513e7e7eeb9f0e4006bc4674e08f5bfa9b5 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 7 Jan 2021 20:01:11 +0100 Subject: [PATCH] removed all string related builtin functions and moved them to separate routines in new 'string' library module --- compiler/res/.editorconfig | 4 +- compiler/res/prog8lib/diskio.p8 | 15 +- compiler/res/prog8lib/prog8_funcs.asm | 202 ------------------ compiler/res/prog8lib/string.p8 | 184 ++++++++++++++++ .../c64/codegen/BuiltinFunctionsAsmGen.kt | 51 ----- .../src/prog8/functions/BuiltinFunctions.kt | 36 +--- docs/source/libraries.rst | 43 ++++ docs/source/programming.rst | 38 ---- docs/source/todo.rst | 2 + examples/.editorconfig | 4 +- examples/arithmetic/aggregates.p8 | 5 +- examples/arithmetic/bitshift.p8 | 1 + examples/arithmetic/builtins.p8 | 78 +------ examples/compilerperformance/perf.p8 | 3 +- examples/compilerperformance/perf10.p8 | 3 +- examples/compilerperformance/perf2.p8 | 3 +- examples/compilerperformance/perf3.p8 | 3 +- examples/compilerperformance/perf4.p8 | 3 +- examples/compilerperformance/perf5.p8 | 3 +- examples/compilerperformance/perf6.p8 | 3 +- examples/compilerperformance/perf7.p8 | 3 +- examples/compilerperformance/perf8.p8 | 3 +- examples/compilerperformance/perf9.p8 | 3 +- examples/cx16/assembler/assem.p8 | 7 +- examples/cx16/imageviewer/imageviewer.p8 | 17 +- examples/test.p8 | 98 ++++++++- examples/textelite.p8 | 7 +- 27 files changed, 375 insertions(+), 447 deletions(-) diff --git a/compiler/res/.editorconfig b/compiler/res/.editorconfig index 161e7859f..f7ef40516 100644 --- a/compiler/res/.editorconfig +++ b/compiler/res/.editorconfig @@ -7,16 +7,14 @@ indent_size = 4 indent_style = space insert_final_newline = true max_line_length = 120 -tab_width = 4 +tab_width = 8 trim_trailing_whitespace = true ij_smart_tabs = true [*.p8] -tab_width = 4 indent_size = 4 indent_style = space [*.asm] -tab_width = 8 indent_size = 8 indent_style = tab diff --git a/compiler/res/prog8lib/diskio.p8 b/compiler/res/prog8lib/diskio.p8 index 2f3cb968b..85a8062e7 100644 --- a/compiler/res/prog8lib/diskio.p8 +++ b/compiler/res/prog8lib/diskio.p8 @@ -3,6 +3,7 @@ ; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 %import textio +%import string %import syslib diskio { @@ -84,7 +85,7 @@ io_error: name_ptrs++ @(name_ptrs) = msb(names_buffer) name_ptrs++ - names_buffer += strcopy(diskio.list_filename, names_buffer) + 1 + names_buffer += string.copy(diskio.list_filename, names_buffer) + 1 files_found++ if names_buffer - buffer_start > 512-18 break @@ -204,7 +205,7 @@ close_end: ; note: only a single iteration loop can be active at a time! f_close() - c64.SETNAM(strlen(filenameptr), filenameptr) + c64.SETNAM(string.length(filenameptr), filenameptr) c64.SETLFS(11, drivenumber, 3) void c64.OPEN() ; open 11,8,0,"filename" if_cc { @@ -373,7 +374,7 @@ io_error: sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte { - c64.SETNAM(strlen(filenameptr), filenameptr) + c64.SETNAM(string.length(filenameptr), filenameptr) c64.SETLFS(1, drivenumber, 0) uword end_address = address + size @@ -403,7 +404,7 @@ io_error: } sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword { - c64.SETNAM(strlen(filenameptr), filenameptr) + c64.SETNAM(string.length(filenameptr), filenameptr) ubyte secondary = 1 uword end_of_load = 0 if address_override @@ -435,7 +436,7 @@ io_error: sub delete(ubyte drivenumber, uword filenameptr) { ; -- delete a file on the drive - ubyte flen = strlen(filenameptr) + ubyte flen = string.length(filenameptr) filename[0] = 's' filename[1] = ':' memcopy(filenameptr, &filename+2, flen+1) @@ -448,8 +449,8 @@ io_error: sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) { ; -- rename a file on the drive - ubyte flen_old = strlen(oldfileptr) - ubyte flen_new = strlen(newfileptr) + ubyte flen_old = string.length(oldfileptr) + ubyte flen_new = string.length(newfileptr) filename[0] = 'r' filename[1] = ':' memcopy(newfileptr, &filename+2, flen_new) diff --git a/compiler/res/prog8lib/prog8_funcs.asm b/compiler/res/prog8lib/prog8_funcs.asm index 6018d2cea..0806cfefb 100644 --- a/compiler/res/prog8lib/prog8_funcs.asm +++ b/compiler/res/prog8lib/prog8_funcs.asm @@ -1204,205 +1204,3 @@ func_memcopy255 .proc ldx P8ZP_SCRATCH_REG rts .pend - - -func_leftstr .proc - ; leftstr(source, target, length) - lda _arg_source - ldy _arg_source+1 - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda _arg_target - ldy _arg_target+1 - sta P8ZP_SCRATCH_W2 - sty P8ZP_SCRATCH_W2+1 - ldy _arg_length - lda #0 - sta (P8ZP_SCRATCH_W2),y - cpy #0 - bne _loop - rts -_loop dey - lda (P8ZP_SCRATCH_W1),y - sta (P8ZP_SCRATCH_W2),y - cpy #0 - bne _loop -+ rts - -_arg_source .word 0 -_arg_target .word 0 -_arg_length .byte 0 - .pend - - -func_rightstr .proc - ; rightstr(source, target, length) - lda _arg_source - ldy _arg_source+1 - jsr func_strlen_into_A - sec - sbc _arg_length - sta P8ZP_SCRATCH_B1 - lda _arg_source - ldy _arg_source+1 - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda _arg_target - ldy _arg_target+1 - sta P8ZP_SCRATCH_W2 - sty P8ZP_SCRATCH_W2+1 - ldy #0 - sty P8ZP_SCRATCH_REG -- ldy P8ZP_SCRATCH_B1 - lda (P8ZP_SCRATCH_W1),y - inc P8ZP_SCRATCH_B1 - ldy P8ZP_SCRATCH_REG - sta (P8ZP_SCRATCH_W2),y - inc P8ZP_SCRATCH_REG - cmp #0 - bne - - rts - -_arg_source .word 0 -_arg_target .word 0 -_arg_length .byte 0 - .pend - - -func_strlen_into_A .proc - ; -- put length of 0-terminated string in A/Y into A - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy #0 -- lda (P8ZP_SCRATCH_W1),y - beq + - iny - bne - -+ tya - rts - .pend - -func_strlen_stack .proc - jsr func_strlen_into_A - sta P8ESTACK_LO,x - dex - rts - .pend - - -func_substr .proc - ; substr(source, target, start, length) - lda _arg_source - ldy _arg_source+1 - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda _arg_target - ldy _arg_target+1 - sta P8ZP_SCRATCH_W2 - sty P8ZP_SCRATCH_W2+1 - ldy _arg_length - lda _arg_start - sta P8ZP_SCRATCH_B1 - - ; adjust src location - clc - lda P8ZP_SCRATCH_W1 - adc P8ZP_SCRATCH_B1 - sta P8ZP_SCRATCH_W1 - bcc + - inc P8ZP_SCRATCH_W1+1 -+ lda #0 - sta (P8ZP_SCRATCH_W2),y - jmp _startloop -- lda (P8ZP_SCRATCH_W1),y - sta (P8ZP_SCRATCH_W2),y -_startloop dey - cpy #$ff - bne - - rts - -_arg_source .word 0 -_arg_target .word 0 -_arg_start .byte 0 -_arg_length .byte 0 - .pend - - -func_strcmp .proc - ; -- compare 2 strings -> A - lda _arg_s2 - ldy _arg_s2+1 - sta P8ZP_SCRATCH_W2 - sty P8ZP_SCRATCH_W2+1 - lda _arg_s1 - ldy _arg_s1+1 - jmp strcmp_mem -_arg_s1 .word 0 -_arg_s2 .word 0 - .pend - -func_strcmp_stack .proc - jsr func_strcmp - sta P8ESTACK_LO,x - dex - rts - .pend - -func_strcopy .proc - lda _arg_to - ldy _arg_to+1 - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda _arg_from - ldy _arg_from+1 - jsr strcpy - tya - rts -_arg_from .word 0 -_arg_to .word 0 - .pend - -func_strcopy_to_stack .proc - jsr func_strcopy - sta P8ESTACK_LO,x - dex - rts - .pend - - -func_strfind .proc - lda _arg_string - ldy _arg_string+1 - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy #0 -- lda (P8ZP_SCRATCH_W1),y - beq _notfound - cmp _arg_char - beq _found - iny - bne - -_notfound lda #0 - ldy #0 - rts -_found sty P8ZP_SCRATCH_B1 - lda P8ZP_SCRATCH_W1 - clc - adc P8ZP_SCRATCH_B1 - sta P8ZP_SCRATCH_W1 - bcc + - inc P8ZP_SCRATCH_W1+1 -+ ldy P8ZP_SCRATCH_W1+1 - rts - -_arg_string .word 0 -_arg_char .byte 0 - .pend - -func_strfind_stack .proc - jsr func_strfind - sta P8ESTACK_LO,x - sty P8ESTACK_HI,x - dex - rts - .pend diff --git a/compiler/res/prog8lib/string.p8 b/compiler/res/prog8lib/string.p8 index 2e5bb42c7..7d853cef7 100644 --- a/compiler/res/prog8lib/string.p8 +++ b/compiler/res/prog8lib/string.p8 @@ -5,5 +5,189 @@ string { + asmsub length(uword string @AY) clobbers(A) -> ubyte @Y { + ; Returns the number of bytes in the string. + ; This value is determined during runtime and counts upto the first terminating 0 byte in the string, + ; regardless of the size of the string during compilation time. Don’t confuse this with len and sizeof! + + %asm {{ + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + ldy #0 +- lda (P8ZP_SCRATCH_W1),y + beq + + iny + bne - ++ rts + }} + } + + asmsub left(uword source @R0, ubyte length @A, uword target @R1) clobbers(A, Y) { + ; 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. + ; Also, you have to make sure yourself that length is smaller or equal to the length of the source string. + ; Modifies in-place, doesn’t return a value (so can’t be used in an expression). + %asm {{ + ; need to copy the the cx16 virtual registers to zeropage to be compatible with C64... + ldy cx16.r0 + sty P8ZP_SCRATCH_W1 + ldy cx16.r0+1 + sty P8ZP_SCRATCH_W1+1 + ldy cx16.r1 + sty P8ZP_SCRATCH_W2 + ldy cx16.r1+1 + sty P8ZP_SCRATCH_W2+1 + tay + lda #0 + sta (P8ZP_SCRATCH_W2),y + cpy #0 + bne _loop + rts +_loop dey + lda (P8ZP_SCRATCH_W1),y + sta (P8ZP_SCRATCH_W2),y + cpy #0 + bne _loop ++ rts + }} +; asmgen.out(" jsr prog8_lib.func_leftstr") + } + + asmsub right(uword source @R0, ubyte length @A, uword target @R1) clobbers(A,Y) { + ; 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. + ; Also, you have to make sure yourself that length is smaller or equal to the length of the source string. + ; Modifies in-place, doesn’t return a value (so can’t be used in an expression). + %asm {{ + ; need to copy the the cx16 virtual registers to zeropage to be compatible with C64... + sta P8ZP_SCRATCH_B1 + lda cx16.r0 + ldy cx16.r0+1 + jsr string.length + tya + sec + sbc P8ZP_SCRATCH_B1 + clc + adc cx16.r0 + sta P8ZP_SCRATCH_W1 + lda cx16.r0+1 + adc #0 + sta P8ZP_SCRATCH_W1+1 + ldy cx16.r1 + sty P8ZP_SCRATCH_W2 + ldy cx16.r1+1 + sty P8ZP_SCRATCH_W2+1 + ldy P8ZP_SCRATCH_B1 + lda #0 + sta (P8ZP_SCRATCH_W2),y + cpy #0 + bne _loop + rts +_loop dey + lda (P8ZP_SCRATCH_W1),y + sta (P8ZP_SCRATCH_W2),y + cpy #0 + bne _loop ++ rts + }} + } + + asmsub slice(uword source @R0, ubyte start @A, ubyte length @Y, uword target @R1) clobbers(A, Y) { + ; 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. + ; Also, you have to make sure yourself that start and length are within bounds of the strings. + ; Modifies in-place, doesn’t return a value (so can’t be used in an expression). + %asm {{ + ; need to copy the the cx16 virtual registers to zeropage to be compatible with C64... + ; substr(source, target, start, length) + sta P8ZP_SCRATCH_B1 + lda cx16.r0 + sta P8ZP_SCRATCH_W1 + lda cx16.r0+1 + sta P8ZP_SCRATCH_W1+1 + lda cx16.r1 + sta P8ZP_SCRATCH_W2 + lda cx16.r1+1 + sta P8ZP_SCRATCH_W2+1 + + ; adjust src location + clc + lda P8ZP_SCRATCH_W1 + adc P8ZP_SCRATCH_B1 + sta P8ZP_SCRATCH_W1 + bcc + + inc P8ZP_SCRATCH_W1+1 ++ lda #0 + sta (P8ZP_SCRATCH_W2),y + beq _startloop +- lda (P8ZP_SCRATCH_W1),y + sta (P8ZP_SCRATCH_W2),y +_startloop dey + cpy #$ff + bne - + rts + }} + } + + asmsub find(uword string @R0, ubyte character @A) -> uword @AY { + ; Locates the first position of the given character in the string, + ; returns the string starting with this character or $0000 if the character is not found. + %asm {{ + ; need to copy the the cx16 virtual registers to zeropage to be compatible with C64... + sta P8ZP_SCRATCH_B1 + lda cx16.r0 + ldy cx16.r0+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + ldy #0 +- lda (P8ZP_SCRATCH_W1),y + beq _notfound + cmp P8ZP_SCRATCH_B1 + beq _found + iny + bne - +_notfound lda #0 + ldy #0 + rts +_found sty P8ZP_SCRATCH_B1 + ldy P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_W1 + clc + adc P8ZP_SCRATCH_B1 + bcc + + iny ++ rts + + }} + } + + asmsub copy(uword source @R0, uword target @AY) clobbers(A) -> ubyte @Y { + ; Copy a string to another, overwriting that one. + ; Returns the length of the string that was copied. + ; Often you don’t have to call this explicitly and can just write string1 = string2 + ; but this function is useful if you’re dealing with addresses for instance. + %asm {{ + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda cx16.r0 + ldy cx16.r0+1 + jmp prog8_lib.strcpy + }} + } + + asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> ubyte @A { + ; Compares two strings for sorting. + ; Returns -1 (255), 0 or 1 depeding on wether string1 sorts before, equal or after string2. + ; Note that you can also directly compare strings and string values with eachother using + ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically). + %asm {{ + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + lda cx16.r0 + ldy cx16.r0+1 + jmp prog8_lib.strcmp_mem + }} + } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 784c21d41..019ab3359 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -79,21 +79,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val "set_carry" -> asmgen.out(" sec") "clear_irqd" -> asmgen.out(" cli") "set_irqd" -> asmgen.out(" sei") - "strlen" -> funcStrlen(fcall, resultToStack) - "strfind" -> funcStrfind(fcall, func, resultToStack, sscope) - "strcmp" -> funcStrcmp(fcall, func, resultToStack, sscope) - "strcopy" -> { - translateArguments(fcall.args, func, sscope) - if(resultToStack) - asmgen.out(" jsr prog8_lib.func_strcopy_to_stack") - else - asmgen.out(" jsr prog8_lib.func_strcopy") - } "memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, sscope) - "substr", "leftstr", "rightstr" -> { - translateArguments(fcall.args, func, sscope) - asmgen.out(" jsr prog8_lib.func_${func.name}") - } "exit" -> { translateArguments(fcall.args, func, sscope) asmgen.out(" jmp prog8_lib.func_exit") @@ -198,22 +184,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } } - private fun funcStrfind(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { - translateArguments(fcall.args, func, scope) - if(resultToStack) - asmgen.out(" jsr prog8_lib.func_strfind_stack") - else - asmgen.out(" jsr prog8_lib.func_strfind") - } - - private fun funcStrcmp(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { - translateArguments(fcall.args, func, scope) - if(resultToStack) - asmgen.out(" jsr prog8_lib.func_strcmp_stack") - else - asmgen.out(" jsr prog8_lib.func_strcmp") - } - private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { translateArguments(fcall.args, func, scope) if(resultToStack) @@ -588,27 +558,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } } - private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) { - if (fcall.args[0] is IdentifierReference) { - // use the address of the variable - val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference) - val type = fcall.args[0].inferType(program) - when { - type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name") - type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1") - else -> throw AssemblyError("strlen requires str or uword arg") - } - } - else { - // use the expression value as address of the string - asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) - } - if (resultToStack) - asmgen.out(" jsr prog8_lib.func_strlen_stack") - else - asmgen.out(" jsr prog8_lib.func_strlen_into_A") - } - private fun funcSwap(fcall: IFunctionCall) { val first = fcall.args[0] val second = fcall.args[1] diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index a38a1d2ce..ea71aba71 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -159,24 +159,7 @@ private val functionSignatures: List = listOf( FSignature("memsetw" , false, listOf( FParam("address", IterableDatatypes + DataType.UWORD), FParam("numwords", setOf(DataType.UWORD)), - FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null), - FSignature("strlen" , true, listOf(FParam("string", StringlyDatatypes)), DataType.UBYTE, ::builtinStrlen), - FSignature("strcopy" , false, listOf(FParam("from", StringlyDatatypes), FParam("to", StringlyDatatypes)), DataType.UBYTE), - FSignature("substr" , false, listOf( - FParam("source", StringlyDatatypes), - FParam("target", StringlyDatatypes), - FParam("start", setOf(DataType.UBYTE)), - FParam("length", setOf(DataType.UBYTE))), null), - FSignature("leftstr" , false, listOf( - FParam("source", StringlyDatatypes), - FParam("target", StringlyDatatypes), - FParam("length", setOf(DataType.UBYTE))), null), - FSignature("rightstr" , false, listOf( - FParam("source", StringlyDatatypes), - FParam("target", StringlyDatatypes), - FParam("length", setOf(DataType.UBYTE))), null), - FSignature("strcmp" , true, listOf(FParam("s1", StringlyDatatypes), FParam("s2", StringlyDatatypes)), DataType.BYTE, null), - FSignature("strfind" , true, listOf(FParam("string", StringlyDatatypes), FParam("char", setOf(DataType.UBYTE))), DataType.STR, null) + FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null) ) val BuiltinFunctions = functionSignatures.associateBy { it.name } @@ -355,23 +338,6 @@ private fun builtinSizeof(args: List, position: Position, program: P } } -private fun builtinStrlen(args: List, position: Position, program: Program): NumericLiteralValue { - if (args.size != 1) - throw SyntaxError("strlen requires one argument", position) - val argument=args[0] - if(argument is StringLiteralValue) - return NumericLiteralValue.optimalInteger(argument.value.length, argument.position) - val vardecl = (argument as? IdentifierReference)?.targetVarDecl(program.namespace) - if(vardecl!=null) { - if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD) - throw SyntaxError("strlen must have string argument", position) - if(vardecl.autogeneratedDontRemove && vardecl.value!=null) { - return NumericLiteralValue.optimalInteger((vardecl.value as StringLiteralValue).value.length, argument.position) - } - } - throw NotConstArgumentException() -} - private fun builtinLen(args: List, position: Position, program: Program): NumericLiteralValue { // note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE. if(args.size!=1) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 17475385c..6ce83c11b 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -65,6 +65,49 @@ Provides several routines that deal with disk drive I/O, such as: - delete and rename files on the disk +string +------ +Provides string manipulation routines. + +length(str) -> ubyte length + Number of bytes in the string. This value is determined during runtime and counts upto + the first terminating 0 byte in the string, regardless of the size of the string during compilation time. + Don't confuse this with ``len`` and ``sizeof`` + +left(source, length, target) + 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. + Also, you have to make sure yourself that length is smaller or equal to the length of the source string. + Modifies in-place, doesn't return a value (so can't be used in an expression). + +right(source, length, target) + 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. + Also, you have to make sure yourself that length is smaller or equal to the length of the source string. + Modifies in-place, doesn't return a value (so can't be used in an expression). + +slice(source, start, length, target) + 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. + Also, you have to make sure yourself that start and length are within bounds of the strings. + Modifies in-place, doesn't return a value (so can't be used in an expression). + +find(string, char) -> uword address + Locates the first position of the given character in the string, returns the string starting + with this character or $0000 if the character is not found. + +compare(string1, string2) -> ubyte result + Returns -1, 0 or 1 depeding on wether string1 sorts before, equal or after string2. + Note that you can also directly compare strings and string values with eachother + using ``==``, ``<`` etcetera (it will use string.compare for you under water automatically). + +copy(from, to) -> ubyte length + Copy a string to another, overwriting that one. Returns the length of the string that was copied. + Often you don't have to call this explicitly and can just write ``string1 = string2`` + but this function is useful if you're dealing with addresses for instance. + + floats ------ Provides definitions for the ROM/kernel subroutines and utility routines dealing with floating diff --git a/docs/source/programming.rst b/docs/source/programming.rst index c646dea90..7cfe5ea99 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -799,44 +799,6 @@ 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. - Also, you have to make sure yourself that length is smaller or equal to the length of the source string. - 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. - Also, you have to make sure yourself that length is smaller or equal to the length of the source string. - Modifies in-place, doesn't return a value (so can't be used in an expression). - -strlen(str) - Number of bytes in the string. This value is determined during runtime and counts upto - the first terminating 0 byte in the string, regardless of the size of the string during compilation time. - Don't confuse this with ``len`` and ``sizeof`` - -strcmp(string1, string2) - Returns -1, 0 or 1 depeding on wether string1 sorts before, equal or after string2. - Note that you can also directly compare strings and string values with eachother - using ``==``, ``<`` etcetera (it will use strcmp for you under water automatically). - -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. - Also, you have to make sure yourself that start and length are within bounds of the strings. - Modifies in-place, doesn't return a value (so can't be used in an expression). - -strcopy(from, to) - Copy a string to another, overwriting that one. Returns the length of the string that was copied. - Often you don't have to call this explicitly and can just write ``string1 = string2`` - but this function is useful if you're dealing with addresses for instance. - -strfind(string, char) - Locates the first position of the given character in the string, returns the string starting - with this character or $0000 if the character is not found. - Miscellaneous ^^^^^^^^^^^^^ diff --git a/docs/source/todo.rst b/docs/source/todo.rst index d9c7fc0b2..5d56cfcc9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,10 @@ TODO ==== - move all str* builtin functions to a strings library module, mem* to the sys module. update docs. +- move target() builtin to sys.target constant - use (zp) addressing mode on 65c02 specific code rather than ldy#0 / lda (zp),y - optimize pointer access code @(pointer)? use a subroutine? macro? 65c02 vs 6502? +- allow byte return type with single register for asmsubs, for instance string.compare - can we get rid of the --longOptionName command line options and only keep the short versions? https://github.com/Kotlin/kotlinx-cli/issues/50 - optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation - hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) diff --git a/examples/.editorconfig b/examples/.editorconfig index 161e7859f..f7ef40516 100644 --- a/examples/.editorconfig +++ b/examples/.editorconfig @@ -7,16 +7,14 @@ indent_size = 4 indent_style = space insert_final_newline = true max_line_length = 120 -tab_width = 4 +tab_width = 8 trim_trailing_whitespace = true ij_smart_tabs = true [*.p8] -tab_width = 4 indent_size = 4 indent_style = space [*.asm] -tab_width = 8 indent_size = 8 indent_style = tab diff --git a/examples/arithmetic/aggregates.p8 b/examples/arithmetic/aggregates.p8 index ee56eba6d..a7afc5089 100644 --- a/examples/arithmetic/aggregates.p8 +++ b/examples/arithmetic/aggregates.p8 @@ -1,5 +1,6 @@ %import floats %import textio +%import string %zeropage basicsafe main { @@ -22,10 +23,10 @@ main { if length!=5 txt.print("error len1\n") length = len(uwarr) if length!=5 txt.print("error len2\n") - length=strlen(name) + length=string.length(name) if length!=5 txt.print("error strlen1\n") name[3] = 0 - length=strlen(name) + length=string.length(name) if length!=3 txt.print("error strlen2\n") ; MAX diff --git a/examples/arithmetic/bitshift.p8 b/examples/arithmetic/bitshift.p8 index e536117c0..1ab91b5bd 100644 --- a/examples/arithmetic/bitshift.p8 +++ b/examples/arithmetic/bitshift.p8 @@ -477,6 +477,7 @@ main { + ; @TODO fix type errors sub shiftrsw0() -> word { word q = -12345 diff --git a/examples/arithmetic/builtins.p8 b/examples/arithmetic/builtins.p8 index c3198c941..4fb9d0fb2 100644 --- a/examples/arithmetic/builtins.p8 +++ b/examples/arithmetic/builtins.p8 @@ -1,5 +1,6 @@ %import textio %import floats +%import string %import syslib %import test_stack %zeropage basicsafe @@ -8,7 +9,6 @@ main { sub start() { rotations() - strings() integers() floatingpoint() @@ -256,82 +256,6 @@ main { } - sub strings() { - const uword ADDR = $8400 - const uword ADDR2 = $8000 - - memset(ADDR2, 40*25, '*') - memset(ADDR2, 40, '1') - memset(ADDR2+24*40, 39, '2') - memsetw(ADDR2, 40*25/2, $3132) - memsetw(ADDR2, 20, $4142) - memsetw(ADDR2+24*40, 19, $4241) - memcopy(ADDR2, ADDR, 200) - - str result = "?" *10 - str s1 = "irmen" - str s2 = "hello" - str dots = "....." - - ubyte ub - byte bb - ubyte zero=0 - - bb = strcmp(s1, s2) - txt.print_b(bb) - txt.chrout('\n') - bb = strcmp(s2, s1) - txt.print_b(bb) - txt.chrout('\n') - txt.print_ub(s1==s2) - txt.chrout('\n') - txt.print_ub(s1s2) - txt.chrout('\n') - bb = zero+strcmp(s1,s2)*1+zero - txt.print_b(bb) - txt.chrout('\n') - bb = zero+strcmp(s2,s1)*1+zero - txt.print_b(bb) - txt.chrout('\n') - - ub = strlen(s1) - txt.print_ub(ub) - txt.chrout('\n') - ub = zero+strlen(s1)*1+zero - txt.print_ub(ub) - txt.chrout('\n') - - leftstr(s1, result, 3) - txt.print(result) - txt.chrout('\n') - leftstr(s1, result, len(s1)) - txt.print(result) - txt.chrout('\n') - txt.chrout('\n') - - result = "x"*8 - rightstr(s2, result, 3) - txt.print(result) - txt.chrout('\n') - rightstr(s2, result, len(s1)) - txt.print(result) - txt.chrout('\n') - - result = "y"*10 - substr(s2, result, 1, 3) - txt.print(result) - txt.chrout('\n') - - void strcopy(s2, s1) - txt.print_ub(99+strcopy(s2,s1)) - txt.chrout('\n') - - test_stack.test() - - } - sub integers() { ubyte[] ubarr = [1,2,3,4,5,0,4,3,2,1, 255, 255, 255] byte[] barr = [1,2,3,4,5,-4,0,-3,2,1, -128, -128, -127] diff --git a/examples/compilerperformance/perf.p8 b/examples/compilerperformance/perf.p8 index f6afe096b..9f46cb05c 100644 --- a/examples/compilerperformance/perf.p8 +++ b/examples/compilerperformance/perf.p8 @@ -6,6 +6,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %import perf2 %import perf3 @@ -954,7 +955,7 @@ util { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf10.p8 b/examples/compilerperformance/perf10.p8 index 411d909d7..b19268dff 100644 --- a/examples/compilerperformance/perf10.p8 +++ b/examples/compilerperformance/perf10.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util10 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf2.p8 b/examples/compilerperformance/perf2.p8 index 1fa4e341f..fff519503 100644 --- a/examples/compilerperformance/perf2.p8 +++ b/examples/compilerperformance/perf2.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util2 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf3.p8 b/examples/compilerperformance/perf3.p8 index a022a1869..0abab8eb3 100644 --- a/examples/compilerperformance/perf3.p8 +++ b/examples/compilerperformance/perf3.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util3 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf4.p8 b/examples/compilerperformance/perf4.p8 index 15e43f3d8..4cb593509 100644 --- a/examples/compilerperformance/perf4.p8 +++ b/examples/compilerperformance/perf4.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util4 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf5.p8 b/examples/compilerperformance/perf5.p8 index 7ce686b90..8f356eaaf 100644 --- a/examples/compilerperformance/perf5.p8 +++ b/examples/compilerperformance/perf5.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util5 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf6.p8 b/examples/compilerperformance/perf6.p8 index b64943712..dcddcb603 100644 --- a/examples/compilerperformance/perf6.p8 +++ b/examples/compilerperformance/perf6.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util6 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf7.p8 b/examples/compilerperformance/perf7.p8 index 92389ed1a..b5ec30781 100644 --- a/examples/compilerperformance/perf7.p8 +++ b/examples/compilerperformance/perf7.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util7 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf8.p8 b/examples/compilerperformance/perf8.p8 index 19fcebea3..197c2a30e 100644 --- a/examples/compilerperformance/perf8.p8 +++ b/examples/compilerperformance/perf8.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util8 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/compilerperformance/perf9.p8 b/examples/compilerperformance/perf9.p8 index 733684595..50aef3c3b 100644 --- a/examples/compilerperformance/perf9.p8 +++ b/examples/compilerperformance/perf9.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -944,7 +945,7 @@ util9 { } sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + repeat width - string.length(string) { txt.chrout(' ') } txt.print(string) diff --git a/examples/cx16/assembler/assem.p8 b/examples/cx16/assembler/assem.p8 index 3df32fb1d..3d5e051c7 100644 --- a/examples/cx16/assembler/assem.p8 +++ b/examples/cx16/assembler/assem.p8 @@ -1,6 +1,7 @@ %target cx16 %import test_stack %import textio +%import string %zeropage basicsafe %option no_sysinit @@ -61,7 +62,7 @@ textparse { } uword value = conv.any2uword(word_addrs[2]) - if strcmp("*", word_addrs[0])==0 { + if word_addrs[0] == "*" { ; TODO does this string compare work? program_counter = value } else { set_symbol(word_addrs[0], value) @@ -96,7 +97,7 @@ textparse { } if label_ptr { - uword lastlabelchar = label_ptr + strlen(label_ptr)-1 + uword lastlabelchar = label_ptr + string.length(label_ptr)-1 if @(lastlabelchar) == ':' @(lastlabelchar) = 0 if instructions.match(label_ptr) { @@ -346,7 +347,7 @@ textparse { } if changed { @(dest)=0 - void strcopy(input_line2, src) + string.copy(input_line2, src) } } } diff --git a/examples/cx16/imageviewer/imageviewer.p8 b/examples/cx16/imageviewer/imageviewer.p8 index f64b320ab..8d4bfcd93 100644 --- a/examples/cx16/imageviewer/imageviewer.p8 +++ b/examples/cx16/imageviewer/imageviewer.p8 @@ -2,6 +2,7 @@ %import gfx2 %import textio %import diskio +%import string %import koala_module %import iff_module %import pcx_module @@ -14,7 +15,7 @@ main { sub start() { ; trick to check if we're running on sdcard or host system shared folder txt.print("\nimage viewer for commander x16\nformats supported: .iff, .pcx, .bmp, .koa (c64 koala)\n\n") - if strlen(diskio.status(8)) { + if string.length(diskio.status(8)) { txt.print("enter image file name or just enter for all on disk: ") ubyte i = txt.input_chars(diskio.filename) gfx2.screen_mode(1) ; 320*240, 256c @@ -54,7 +55,7 @@ main { ;txt.print(filenameptr) ;txt.chrout('\n') uword extension = filenameptr + rfind(filenameptr, '.') - if strcmp(extension, ".iff")==0 { + if extension == ".iff" { ; TODO does this compare work? ;txt.print("loading ") ;txt.print("iff\n") if iff_module.show_image(filenameptr) { @@ -70,7 +71,7 @@ main { load_error(filenameptr) } } - else if strcmp(extension, ".pcx")==0 { + else if extension == ".pcx" { ; TODO works? ;txt.print("loading ") ;txt.print("pcx\n") if pcx_module.show_image(filenameptr) { @@ -79,7 +80,7 @@ main { load_error(filenameptr) } } - else if strcmp(extension, ".koa")==0 { + else if extension == ".koa" { ; TODO works? ;txt.print("loading ") ;txt.print("koala\n") if koala_module.show_image(filenameptr) { @@ -88,7 +89,7 @@ main { load_error(filenameptr) } } - else if strcmp(extension, ".bmp")==0 { + else if extension == ".bmp" { ; TODO works? ;txt.print("loading ") ;txt.print("bmp\n") if bmp_module.show_image(filenameptr) { @@ -97,7 +98,7 @@ main { load_error(filenameptr) } } -; else if strcmp(extension, ".ci")==0 { +; else if extension == ".ci" { ;; txt.print("loading ") ;; txt.print("ci\n") ; if ci_module.show_image(filenameptr) { @@ -117,12 +118,12 @@ main { sub extension_equals(uword stringptr, uword extensionptr) -> ubyte { ubyte ix = rfind(stringptr, '.') - return ix<255 and strcmp(stringptr+ix, extensionptr)==0 + return ix<255 and string.compare(stringptr+ix, extensionptr)==0 } sub rfind(uword stringptr, ubyte char) -> ubyte { ubyte i - for i in strlen(stringptr)-1 downto 0 { + for i in string.length(stringptr)-1 downto 0 { if @(stringptr+i)==char return i } diff --git a/examples/test.p8 b/examples/test.p8 index d38902a54..117cacd20 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,11 +1,99 @@ %import test_stack %import textio +%import string %zeropage basicsafe %option no_sysinit main { - sub start () { + ; TODO: error when a parameter has the same name as an existing module/block/subroutine: sub print_right(ubyte width, uword string) { + + + sub start() { + str s1 = "irmen" + str s2 = "what" + str s3 = "irmen2" + + s3[5] = 0 + + txt.print("length:\n") + txt.print_ub(len(s1)) + txt.chrout('\n') + txt.print_ub(string.length(s1)) + + txt.print("\n\ncopy:\n") + txt.print(s3) + txt.chrout('\n') + s3 = s2 + txt.print(s3) + txt.chrout('\n') + txt.print_ub(string.copy("new", s3)) + txt.print(s3) + + txt.print("\n\ncompare:\n") + txt.chrout('\n') + txt.print_ub(string.compare(s1, s2)) + txt.chrout('\n') + txt.print_ub(string.compare(s2, s1)) + txt.chrout('\n') + txt.print_ub(string.compare(s1, s3)) + txt.chrout('\n') + txt.chrout('\n') + txt.print_ub(s1==s2) + txt.chrout('\n') + txt.print_ub(s1s2) + txt.chrout('\n') + + txt.print("\n\nleft:") + string.left(s2,2,s3) + txt.print(s3) + txt.chrout('\n') + txt.print("\n\nright:\n") + txt.print(s2) + txt.chrout('\n') + string.right(s2,2,s3) + txt.print(s3) + txt.chrout('\n') + + txt.print("\n\nfind:\n") + txt.print(s1) + txt.chrout('\n') + uword found = string.find(s1, 'e') + txt.print_uwhex(found, 1) + if found + txt.print(found) + txt.chrout('\n') + found = string.find(s1, 'i') + txt.print_uwhex(found, 1) + if found + txt.print(found) + txt.chrout('\n') + found = string.find(s1, 'x') + txt.print_uwhex(found, 1) + if found + txt.print(found) + txt.chrout('\n') + + txt.print("\n\nslice:\n") + string.slice(s1, 0, 5, s2) + txt.print(s2) + txt.chrout('\n') + string.slice(s1, 1, 4, s2) + txt.print(s2) + txt.chrout('\n') + string.slice(s1, 2, 2, s2) + txt.print(s2) + txt.chrout('\n') + string.slice(s1, 3, 2, s2) + txt.print(s2) + txt.chrout('\n') + + test_stack.test() + } + + sub start2 () { str[] binstrings = [ "", "%", @@ -151,16 +239,16 @@ main { } -; found = strfind("irmen de jong", ' ') +; found = string.find("irmen de jong", ' ') ; txt.print_uwhex(found, 1) ; txt.chrout('\n') -; found = strfind(" irmen-de-jong", ' ') +; found = string.find(" irmen-de-jong", ' ') ; txt.print_uwhex(found, 1) ; txt.chrout('\n') -; found = strfind("irmen-de-jong ", ' ') +; found = string.find("irmen-de-jong ", ' ') ; txt.print_uwhex(found, 1) ; txt.chrout('\n') -; found = strfind("irmen-de-jong", ' ') +; found = string.find("irmen-de-jong", ' ') ; txt.print_uwhex(found, 1) ; txt.chrout('\n') diff --git a/examples/textelite.p8 b/examples/textelite.p8 index 3c660e829..87206bd0b 100644 --- a/examples/textelite.p8 +++ b/examples/textelite.p8 @@ -1,6 +1,7 @@ %import textio %import conv %import diskio +%import string %import test_stack %option no_sysinit %zeropage basicsafe @@ -943,11 +944,11 @@ util { return false } - sub print_right(ubyte width, uword string) { - repeat width - strlen(string) { + sub print_right(ubyte width, uword s) { + repeat width - string.length(s) { txt.chrout(' ') } - txt.print(string) + txt.print(s) } asmsub print_10s(uword value @AY) clobbers(A, X, Y) {