;; ;; Copyright (c) 2016 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. ;; ;; name: string.asm ;; rev.: 2016/22/10 ;; o.s. 65C816 version v1.0 ;; shared library: string functions .LIST on ; flags PNFLAG .EQU 10000000B ; Negative flag PVFLAG .EQU 01000000B ; Overflow flag PMFLAG .EQU 00100000B ; Acc/Mem 8 bit flag PXFLAG .EQU 00010000B ; Index 8 bit flag PDFLAG .EQU 00001000B ; Decimal flag PIFLAG .EQU 00000100B ; IRQ disable flag PZFLAG .EQU 00000010B ; Zero flag PCFLAG .EQU 00000001B ; Carry flag ; macro's ; MEM/ACC 16 bit ACC16: .MACRO .MLIST rep #PMFLAG .LONGA on .MNLIST .ENDM ; MEM/ACC 16 bit AND CLEAR CARRY ACC16CLC: .MACRO .MLIST rep #(PMFLAG.OR.PCFLAG) .LONGA on .MNLIST .ENDM ; MEM/ACC 8 bit ACC08: .MACRO .MLIST sep #PMFLAG .LONGA off .MNLIST .ENDM ; MEM/ACC 8 bit AND SET CARRY ACC08SEC: .MACRO .MLIST sep #(PMFLAG.OR.PCFLAG) .LONGA off .MNLIST .ENDM ; X/Y 16 bit INDEX16: .MACRO .MLIST rep #PXFLAG .LONGI on .MNLIST .ENDM ; X/Y 16 bit AND CLEAR CARRY INDEX16CLC: .MACRO .MLIST rep #(PXFLAG.OR.PCFLAG) .LONGI on .MNLIST .ENDM ; X/Y 8 bit INDEX08: .MACRO .MLIST sep #PXFLAG .LONGI off .MNLIST .ENDM ; A/M/X/Y 16 bit CPU16: .MACRO .MLIST rep #(PMFLAG.OR.PXFLAG) .LONGA on .LONGI on .MNLIST .ENDM ; A/M/X/Y 16 bit AND CLEAR CARRY CPU16CLC: .MACRO .MLIST rep #(PMFLAG.OR.PXFLAG.OR.PCFLAG) .LONGA on .LONGI on .MNLIST .ENDM ; A/M/X/Y 8 bit CPU08: .MACRO .MLIST sep #(PMFLAG.OR.PXFLAG) .LONGA off .LONGI off .MNLIST .ENDM ; A/M/X/Y 8 bit AND SET CARRY CPU08SEC: .MACRO .MLIST sep #(PMFLAG.OR.PXFLAG.OR.PCFLAG) .LONGA off .LONGI off .MNLIST .ENDM ;--------------------------------------------------------------------------- ; 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 INDEX16 ; X/Y 16 bit ACC08 ; 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: CPU16CLC ; 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 INDEX16 ; X/Y 16 bit ACC08 ; 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: CPU16CLC ; A,X,Y 16 bit - clear carry lda parameter str .SET append + 3 ; parameter ; indx .SET 1 ; : local var ; php ; save SR INDEX16 ; 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 ACC08 ; 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 INDEX16 ; X/Y 16 bit ACC08 ; 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 CPU08 ; be sure A,X,Y 8 bit ldy #0 ; pointer INDEX16 ; 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 CPU16CLC 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: ACC08 ; 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 CPU08 ; A/M & X/Y 8 bit ldy #0 ; pointer INDEX16 ; 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 CPU08SEC ; A/M & X/Y 8 bit & set carry ldy #0 ; pointer INDEX16 ; 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 CPU08 ; A/M & X/Y 8 bit ldy #0 ; pointer INDEX16 ; 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 CPU08SEC ; A/M & X/Y 8 bit & set carry ldy #0 ; pointer INDEX16 ; 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 CPU08 ; A/M & X/Y 8 bit ldy #0 ; pointer INDEX16 ; 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 CPU08 ; A/M & X/Y 8 bit ldy #0 ; pointer INDEX16 ; 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 CPU08 ; A/M & X/Y 8 bit ldy #0 ; pointer INDEX16 ; 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 CPU08 ; A/M & X/Y 8 bit ldx #0 ; index INDEX16 ; 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