;; ;; Copyright (c) 2019 Marco Granati ;; ;; Permission to use, copy, modify, and distribute this software for any ;; purpose with or without fee is hereby granted, provided that the above ;; copyright notice and this permission notice appear in all copies. ;; ;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ;; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ;; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ;; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ;; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ;; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ;; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ;; ;; project: c16 library ;; name: string.asm ;; purpose: strings library ;; revision: 1.0 ;; date: 2019/03/18 ;; shared library: string functions .list off .include inc\mainlib.inc .list on ;--------------------------------------------------------------------------- ; code segment ;--------------------------------------------------------------------------- .CODE .longa off .longi off ; _strlen(src) ; ; return in C the length of the string src ; if the lenght is greater than 64k return $FFFF ; ;------------------------------- _strlen: .public _strlen ;------------------------------- .longa off .longi off parsiz .set 3 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; src .set entsr + 4 ; parameter ; php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's xy16 ; X/Y 16 bit am8 ; be sure A 8 bit ldy #0 ; pointer ; ?loop: lda [src],y ; get next char beq ?done ; done when get a nul iny ; bump pointer bne ?loop dey ; max length = $FFFF ; ?done: c16c ; A,X,Y 16 bit - clear carry lda $FFFF, dst will be truncated ; ;------------------------------- _strcpy: .public _strcpy ;------------------------------- .longa off .longi off parsiz .set 6 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; src .set entsr + 4 ; parameter dst .set src + 3 ; parameter ; php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's xy16 ; X/Y 16 bit am8 ; be sure A 8 bit ldy #0 ; pointer ; ?loop: lda [src],y ; get next char sta [dst],y ; store beq ?done ; done when get a null iny ; bump pointer bne ?loop tya ; A=0 dey ; max length = $FFFF sta [dst],y ; always NUL terminates dst ; ?done: c16c ; A,X,Y 16 bit - clear carry lda parameter str .set append + 3 ; parameter ; indx .set 1 ; : local var ; php ; save SR xy16 ; X/Y 16 bit ldy #0 ; pointer phy ; make room for local var tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's am8 ; be sure A is 8 bit ; ?loop: lda [str],y ; search end of string str beq ?cat ; done when get a nul iny ; bump pointer bne ?loop dey ; max length = $FFFF bra ?done ; can't append more char's ; ?cat: tyx ; X = current str index ldy ; at most siz characters will be copied ; return in C the lenght of string ; if src is an empty string ; ;------------------------------- _strncpy: .public _strncpy ;------------------------------- .longa off .longi off parsiz .set 8 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; siz .set entsr + 4 ; parameter src .set siz + 2 ; parameter dst .set src + 3 ; parameter ; php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's xy16 ; X/Y 16 bit am8 ; be sure A 8 bit ldy characters from string , starting ; at index ; ; stack param's: ; ; : long pointer to string ; : index of first character to remove (word) ; : count of characters to remove (word) ; ; return: C = length of string ; ; note: X & Y not preserved ; ;------------------------------- _strdel: .public _strdel ;------------------------------- .longa off .longi off parsiz .set 7 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; count .set entsr + 4 ; parameter start .set count + 2 ; parameter str .set start + 2 ; parameter ; php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8 ; be sure A,X,Y 8 bit ldy #0 ; pointer xy16 ; X/Y 16 bit ; ?len: lda [str],y ; compute string length beq ?chk ; done when get a nul iny ; bump pointer bne ?len tya dey ; max length = $FFFF sta [str],y ; force string terminator ; ?chk: tyx ; check string lenght beq ?done ; empty string ldx adc index cpy index ldy string into string, starting at index of ; character of , starting from index, are moved ahead to make ; room for characters of ; if =0 then string will be truncated (return an empty string) ; buffer should have a size=+1 to hold the nul terminator ; ; stack param's: ; ; : long pointer to target string ; : long pointer to string to insert ; : index of first character where start insertion (word) ; : max. length of (word) ; ; return: C = length of string ; ; note: X & Y not preserved ; ;------------------------------- _strins: .public _strins ;------------------------------- .longa off .longi off parsiz .set 10 ; parameter stack frame size locsiz .set 14 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; maxlen .set entsr + 4 ; parameter start .set maxlen + 2 ; parameter str2 .set start + 2 ; long pointer str1 .set str2 + 3 ; long pointer ; len1 .set 1 ; local: length len2 .set 3 ; local: length iy .set 5 ; local: temp. index end .set 7 ; local: index of last byte of to move newend .set 9 ; local: index of final ending cnt1 .set 11 ; local: count of bytes to move in cnt2 .set 13 ; local: count of bytes to copy from ; php ; save SR c16c tsc sbc #locsiz-1 ; make room for locals var's tcs ; stack frame phd ; save DP reg. tcd ; dp access to stack var's stz , , stz length beq ?stl ; done when get a nul iny ; bump pointer bne ?len2 tya dey ; max length = $FFFF sta [str2],y ; force string terminator ; ?stl: sty lenght txy ; Y = 0 ; ?len1: lda [str1],y ; compute string length beq ?chk ; done when get a nul iny ; bump pointer bne ?len1 tya dey ; max length = $FFFF sta [str1],y ; force string terminator ; ?chk: cpy if below the actual... bcc ?chk1 ; ... length sty cmp sta length ldx is empty... beq ?done ; ...return tyx ; if is empty... beq ?cat ; ...return = ldx lda is in range [1..length(str1)-1] ; ?cat: sty to the end of sec lda to... sbc starting at index sta + length(str2) bcc ?chk4 lda #$FFFF ; limit max. length ; ?chk4: cmp to make room? bcc ?less ; yes sbc ... sta sec lda clc adc ... sta can be inserted stx bytes from the to the new end of ?mov: am8 ; A/M 8 bit ldx ; ?lmov: ldy ... lda [str1],y dey sty bytes from beginning of to , starting at index ?cpy: ldx ; ?lcpy: ldy ldy length plp ; restore SR rtl .longa off .longi off ;--------------------------------------------------------------------------- ; strings comparison ;--------------------------------------------------------------------------- ; _strcmp(s1, s2) ; ; lexicographically compare the null-terminated strings s1 and s2 ; ; stack param's: ; ; : long pointer to first string ; : long pointer to second string ; ; return: status flags affected like in 'cmp' instruction ; CF=0 & ZF=0 if s1 less than s2 ; CF=1 & ZF=1 if s1 equal to s2 ; CF=1 & ZF=0 if s1 greater than s2 ; ; note: X & Y not preserved ; ;------------------------------- _strcmp: .public _strcmp ;------------------------------- .longa off .longi off parsiz .set 6 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; s2 .set entsr + 4 ; parameter s1 .set s2 + 3 ; parameter ; rep #pzflag+pcflag ; clear CF & ZF php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8 ; A/M & X/Y 8 bit ldy #0 ; pointer xy16 ; X/Y 16 bit ; ?cmp: lda [s1],y ; get next char from cmp [s2],y ; compare with byte in bne ?end ; unmatch, set flags cmp #0 ; end of string? beq ?end ; yes, set flags iny ; bump pointer bne ?cmp ; compare next char sec ; all match: set carry ; ?end: php ; CF & ZF affected according comparison pla and #pzflag+pcflag ; mask on CF & ZF tsb : long pointer to first string ; : long pointer to second string ; : word: count of characters to compare ; ; return: status flags affected like in 'cmp' instruction ; CF=0 & ZF=0 if s1 less than s2 ; CF=1 & ZF=1 if s1 equal to s2 ; CF=1 & ZF=0 if s1 greater than s2 ; ; note: X & Y not preserved ; ;------------------------------- _strncmp: .public _strncmp ;------------------------------- .longa off .longi off parsiz .set 8 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; n .set entsr + 4 ; parameter s2 .set n + 2 ; parameter s1 .set s2 + 3 ; parameter ; rep #pzflag+pcflag ; clear CF & ZF php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8c ; A/M & X/Y 8 bit & set carry ldy #0 ; pointer xy16 ; X/Y 16 bit ldx = 0 set CF=1 & ZF=1 beq ?end ; ?cmp: lda [s1],y ; get next char from cmp [s2],y ; compare with byte in bne ?end ; unmatch, set flags cmp #0 ; end of string? beq ?end ; yes, set flags iny ; bump pointer cpy characters... bcc ?cmp ; compare next char ; all match: set carry & zero flag ; ?end: php ; CF & ZF affected according comparison pla and #pzflag+pcflag ; mask on CF & ZF tsb : long pointer to first string ; : long pointer to second string ; ; return: status flags affected like in 'cmp' instruction ; CF=0 & ZF=0 if s1 less than s2 ; CF=1 & ZF=1 if s1 equal to s2 ; CF=1 & ZF=0 if s1 greater than s2 ; ; note: X & Y not preserved ; ;------------------------------- _strcasecmp: .public _strcasecmp ;------------------------------- .longa off .longi off parsiz .set 6 ; parameter stack frame size locsiz .set 2 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; s2 .set entsr + 4 ; parameter s1 .set s2 + 3 ; parameter ; tmp .set 1 ; local: temp. byte ; rep #pzflag+pcflag ; clear CF & ZF php ; save SR pea #0 ; local variable tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8 ; A/M & X/Y 8 bit ldy #0 ; pointer xy16 ; X/Y 16 bit ; ?cmp: lda [s2],y ; get next char from cmp #'a' ; translate lower case to upper case bcc ?st cmp #'z'+1 bcs ?st and #$DF ; upper case ?st: sta cmp #'a' ; translate lower case to upper case bcc ?cmp2 cmp #'z'+1 bcs ?cmp2 and #$DF ; upper case ?cmp2: cmp : long pointer to first string ; : long pointer to second string ; : word: count of characters to compare ; ; return: status flags affected like in 'cmp' instruction ; CF=0 & ZF=0 if s1 less than s2 ; CF=1 & ZF=1 if s1 equal to s2 ; CF=1 & ZF=0 if s1 greater than s2 ; ; note: X & Y not preserved ; ;------------------------------- _strncasecmp: .public _strncasecmp ;------------------------------- .longa off .longi off parsiz .set 8 ; parameter stack frame size locsiz .set 2 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; n .set entsr + 4 ; parameter s2 .set n + 2 ; parameter s1 .set s2 + 3 ; parameter ; tmp .set 1 ; local: temp. byte ; rep #pzflag+pcflag ; clear CF & ZF php ; save SR pea #0 ; local variable tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8c ; A/M & X/Y 8 bit & set carry ldy #0 ; pointer xy16 ; X/Y 16 bit ldx = 0 set CF=1 & ZF=1 beq ?end ; ?cmp: lda [s2],y ; get next char from cmp #'a' ; translate lower case to upper case bcc ?st cmp #'z'+1 bcs ?st and #$DF ; upper case ?st: sta cmp #'a' ; translate lower case to upper case bcc ?cmp2 cmp #'z'+1 bcs ?cmp2 and #$DF ; upper case ?cmp2: cmp characters... bcc ?cmp ; compare next char ; all match: set carry & zero flag ; ?end: php ; CF & ZF affected according comparison pla and #pzflag+pcflag ; mask on CF & ZF tsb in string pointed to by ; ; stack param's: ; ; : long pointer to string ; : character to find (byte) ; ; return: CF=0 & C = index of character in string, if found ; CF=1 & C = index of the nul terminator if not found ; (string length in fact) ; ; note: X & Y not preserved ; ;------------------------------- _strchr: .public _strchr ;------------------------------- .longa off .longi off parsiz .set 4 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; c .set entsr + 4 ; parameter s .set c + 1 ; parameter ; sec ; assume not found php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8 ; A/M & X/Y 8 bit ldy #0 ; pointer xy16 ; X/Y 16 bit ; ?cmp: lda [s],y ; get next char from cmp beq ?end ; match, reset carry flag cmp #0 ; end of string? beq ?done ; yes, return index of terminator iny ; bump pointer bne ?cmp ; compare next char dey bra ?done ; ?end: lda #pcflag ; clear CF trb in string pointed to by ; ; stack param's: ; ; : long pointer to string ; : character to find (byte) ; ; return: CF=0 & C = index of character in string, if found ; CF=1 & C = index of the nul terminator if not found ; (string length in fact) ; ; note: X & Y not preserved ; ;------------------------------- _strrchr: .public _strrchr ;------------------------------- .longa off .longi off parsiz .set 4 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; c .set entsr + 4 ; parameter s .set c + 1 ; parameter ; sec ; assume not found php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8 ; A/M & X/Y 8 bit ldy #0 ; pointer xy16 ; X/Y 16 bit ; ?len: lda [s],y ; find end of string beq ?ok ; done when get a nul iny ; bump pointer bne ?len tya dey ; max length = $FFFF sta [s],y ; force string terminator ; ?ok: tyx ; save pointer to the end of string lda in string pointed to by , ; starting from the character at index ; ; stack param's: ; ; : long pointer to string ; : character to find (byte) ; : index where start searching (word) ; ; return: CF=0 & C = index of character in string, if found ; CF=1 & C = index of the nul terminator if not found ; (string length in fact) ; ; note: X & Y not preserved ; ;------------------------------- _strlchr: .public _strlchr ;------------------------------- .longa off .longi off parsiz .set 6 ; parameter stack frame size locsiz .set 0 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; i .set entsr + 4 ; parameter c .set i + 2 ; parameter s .set c + 1 ; parameter ; sec ; assume not found php ; save SR tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's c8 ; A/M & X/Y 8 bit ldy #0 ; pointer xy16 ; X/Y 16 bit ; ?cmp: lda [s],y ; get next char from cmp beq ?fnd ; match, test index ?tst: cmp #0 ; end of string? beq ?done ; yes, return index of terminator iny ; bump pointer bne ?cmp ; compare next char dey bra ?done ; ?fnd: cpy in string ; ; stack param's: ; ; : long pointer to string ; : long pointer to string ; ; return: CF=0 & C=index of first character of first occurrence ; CF=1 & C=length of if occur nowhere in ; if is empty return CF=0 & C=0 ; ; note: X & Y not preserved ; ;------------------------------- _strstr: .public _strstr ;------------------------------- .longa off .longi off parsiz .set 6 ; parameter stack frame size locsiz .set 4 ; locals stack frame size skpsiz .set parsiz + locsiz ; bytes to skip to realign stack entsr .set locsiz + 1 ; pointer to saved status reg. exitsr .set skpsiz + 1 ; pointer to moved status reg. ; find .set entsr + 4 ; parameter s .set find + 3 ; parameter ; len .set 1 ; local: string length c .set 3 ; local: firs character of ; sec ; assume not found php ; save SR c8 ; A/M & X/Y 8 bit ldx #0 ; index xy16 ; X/Y 16 phx ; local: phx ; local: tsc ; stack frame phd ; save DP reg. tcd ; dp access to stack var's lda [find] ; if string is empty... beq ?end2 ; ...exit with index = 0 & CF = 0 sta = first character of ldy iny sty = pointer to 2nd character of bne ?go inc length beq ?do ; done when get a nul iny ; bump pointer bne ?len tya dey ; max length = $FFFF sta [find],y ; force string terminator ; ?do: sty = length ; ?do1: ldy ; ?do2: lda [s] ; next char of string beq ?done ; end of string : exit with CF=1 inx ; update scan index iny sty pointer bne ?do3 inc ? bne ?do2 ; no, so check next character of ldy = 0 match first character ldy #0 ; ?cmp: lda [s],y ; get next char from cmp [find],y ; compare with byte in bne ?do1 ; unmatch, try next char of cmp #0 ; end of string? beq ?end ; yes, reset carry flag iny ; bump pointer cpy characters... bcc ?cmp ; compare next char ; ?end: dex ; all match: update index & reset carry flag ; ?end2: lda #pcflag ; reset CF... trb