//------------------------------------------------------------------------
//                             String library
//
// Author: Mattias Hansson
// Copyright (c) : 2005 Mattias Hansson
// License: GNU GPL 2
// Language: 65CM v0.4+
// Dependencies:
// ZP usage: $fb-$fe
//
// External functions:
//   lib_string_strlen
//   lib_string_strchr
//   lib_string_strcpy
//   lib_string_stpcpy
//   lib_string_strncpy
//   lib_string_strcmp
//   lib_string_memset
//   lib_string_strcat
//   lib_string_strncat

//
// Internal function:
//
//
// Note: Several of the lib-functions are combined to save space, where
//       this can be done at a fairly small speed penalty.
//
//------------------------------------------------------------------------

#IFNDEF __LIB_STRING
#DEFINE __LIB_STRING = 1

GOTO lib_string_skip

//Makes it possible for all modules that needs NULL
//to define the constant
#IFNDEF NULL
  #DEFINE NULL = 0
#IFEND

WORD lib_string_strptr1 @ $fb
WORD lib_string_strptr2 @ $fd
WORD lib_string_length

//just aliases for the mem* functions
WORD lib_string_ptr1 @ $fb
WORD lib_string_ptr2 @ $fd

WORD lib_string_temp


//-----------------------------------------------------------
// lib_string_strlen
//
// Purpose: Returns the string length of a null-terminated
//          string in memory.
//
// Params:
//   lib_string_strptr1 - pointer to string to measure
//   lib_string_length  - returns the length of the string
//
// lib_string_strchr
//
// Purpose: Returns pointer to where char lib_string_sl_matchchar
//          first occurs in string at lib_string_strptr1.
//          Returns NULL if not found.
//-----------------------------------------------------------

BYTE lib_string_sl_matchchar

FUNC lib_string_strlen ( lib_string_strptr1 out:lib_string_length )

  LET lib_string_sl_matchchar = 0

FUNC lib_string_strchr ( io:lib_string_strptr1 lib_string_sl_matchchar )

  BYTE char

  LET lib_string_length = 0

  PEEK lib_string_strptr1 -> char

  WHILE char
    IF char == lib_string_sl_matchchar
      SUBEND
    ENDIF
    INC lib_string_strptr1
    INC lib_string_length
    PEEK lib_string_strptr1 -> char
  WEND

  LET lib_string_strptr1 = NULL
FEND


//-----------------------------------------------------------
// lib_string_strcpy
//
// Purpose: Copies the null-terminated string at
//          lib_string_strptr1 to lib_string_strptr2
//
// lib_string_stpcpy
//
// Purpose: Same as strcpy but returns a pointer to the
//          end of the copy in lib_string_strptr2
//
// lib_string_strncpy
//
// Purpose: Copies the null-terminated string at
//          lib_string_strptr1 to lib_string_strptr2, but
//          stops after lib_string_length chars regardless
//          of if the full string is copied
//
// Note: Dont send in 0 (zero) into lib_string_length.
//-----------------------------------------------------------

FUNC lib_string_stpcpy ( lib_string_strptr1 io:lib_string_strptr2 )

FUNC lib_string_strcpy ( lib_string_strptr1 lib_string_strptr2 )

  LET lib_string_length = 0

FUNC lib_string_strncpy ( lib_string_strptr1 lib_string_strptr2 lib_string_length )

  BYTE char
  LET char = $ff

  WHILE char
    PEEK lib_string_strptr1 -> char
    POKE lib_string_strptr2 , char
    INC lib_string_strptr1
    INC lib_string_strptr2
    DEC lib_string_length
    IF lib_string_length == 0
      BREAK
    ENDIF
  WEND

FEND

//-----------------------------------------------------------
// lib_string_memset
//
// Purpose: Sets lib_string_length bytes starting at
//          lib_string_ptr1 to the value lib_string_memset_value
//
//-----------------------------------------------------------

BYTE lib_string_memset_value
FUNC lib_string_memset ( lib_string_ptr1 lib_string_memset_value lib_string_length )

  ADD lib_string_ptr1 + lib_string_length -> lib_string_ptr2
  DEC lib_string_ptr2
  WHILE lib_string_ptr2 >= lib_string_ptr1
    POKE lib_string_ptr2 , lib_string_memset_value
    DEC lib_string_ptr2
  WEND

FEND

//-----------------------------------------------------------
// lib_string_strcmp
//
// purpose: Compare two strings.
// Returns 2 if str1 is less than str2
//         1 if str1 is greater than str2
//         0 if they are equal.
//-----------------------------------------------------------

BYTE lib_string_strcmp_result

FUNC lib_string_strcmp ( lib_string_strptr1 lib_string_strptr2 out:lib_string_strcmp_result )
  BYTE val1
  BYTE val2

  LET lib_string_strcmp_result = 0

  PEEK lib_string_strptr1 -> val1
  PEEK lib_string_strptr2 -> val2

  WHILE val1 == val2
    INC lib_string_strptr1
    INC lib_string_strptr2
    PEEK lib_string_strptr1 -> val1
    PEEK lib_string_strptr2 -> val2
    IF val1 == 0
      BREAK
    ENDIF
    IF val2 == 0
      BREAK
    ENDIF
  WEND

  IF val1 < val2
    LET lib_string_strcmp_result = 2
  ENDIF

  IF val1 > val2
    LET lib_string_strcmp_result = 1
  ENDIF

FEND

//-----------------------------------------------------------
// lib_string_strstr
//
// purpose:
//         Locate the position of one string in another.
//         Returns a pointer to the location of the
//         substring. (or NULL if not found)
//-----------------------------------------------------------

WORD lib_string_strstr_return

FUNC lib_string_strstr ( lib_string_strptr1 lib_string_strptr2 out:lib_string_strstr_return )

  LET lib_string_strstr_return = NULL
  WORD strlen1
  WORD strlen2
  WORD substartpos
  WORD mainlastpos
  BYTE mainval
  BYTE subval
  WORD strptr1firstmatch
  BYTE innerloop

  //Ugly hack: Since "lib_string_strptr1" is used internally in strlen we remember the value in a temp variable
  LET lib_string_temp = lib_string_strptr1
  CALL lib_string_strlen ( lib_string_strptr1 strlen1 )
  CALL lib_string_strlen ( lib_string_strptr2 strlen2 )
  //Ugly hack: Since "lib_string_strptr1" is used internally in strlen we restore the value from a temp variable
  LET lib_string_strptr1 = lib_string_temp

  IF strlen1 == 0
    SUBEND
  ENDIF
  IF strlen2 == 0
    SUBEND
  ENDIF

  IF strlen2 > strlen1
    SUBEND
  ENDIF

  SUBT strlen1 - strlen2 -> mainlastpos
  ADD lib_string_strptr1 + mainlastpos -> mainlastpos

  LET substartpos = lib_string_strptr2
  LET innerloop = 0
  PEEK lib_string_strptr2 -> subval

  WHILE lib_string_strptr1 <= mainlastpos
    PEEK lib_string_strptr1 -> mainval
    IF mainval = subval
      LET strptr1firstmatch = lib_string_strptr1
      LET innerloop = 1
    ENDIF
    WHILE mainval = subval
      INC lib_string_strptr1
      INC lib_string_strptr2
      PEEK lib_string_strptr1 -> mainval
      PEEK lib_string_strptr2 -> subval
      IF subval = 0
        //This is the match! We found substr!
        LET lib_string_strstr_return = strptr1firstmatch
        SUBEND
      ENDIF
    WEND
    IF innerloop != 0
      LET innerloop = 0
      LET lib_string_strptr1 = strptr1firstmatch
      LET lib_string_strptr2 = substartpos
      PEEK lib_string_strptr2 -> subval
    ENDIF
    INC lib_string_strptr1
  WEND

FEND

//-----------------------------------------------------------
// lib_string_strcat
//
// purpose:
//         To concatenate two strings.
//         Appends lib_string_strptr1 to the
//         end of lib_string_strptr2 (overwritng
//         the terminating NULL character).
//
// lib_string_strncat
//
// purpose:
//         very similar to strcat except at most
//         lib_string_sc_copymaxchars are
//         copied from the source. result is
//         always NULL-terminated.
//         (i.e. result string is at most
//         length( lib_string_strptr1 ) +
//         lib_string_sc_copymaxchars + 1
//
// Note:
//         Be sure that memory after NULL
//         in lib_string_strptr2 is avaliable
//         for writing (so you don't overwrite
//         something else lying there, or you
//         may get some very unexpected
//         results.
//-----------------------------------------------------------

WORD lib_string_sc_copymaxchars

FUNC lib_string_strcat ( lib_string_strptr1 lib_string_strptr2 )

LET lib_string_sc_copymaxchars = $ffff

FUNC lib_string_strncat ( lib_string_strptr1 lib_string_strptr2 lib_string_sc_copymaxchars )

  BYTE char

  //first find end of string2
  PEEK lib_string_strptr2 -> char
  WHILE char
    INC lib_string_strptr2
    PEEK lib_string_strptr2 -> char
  WEND

  PEEK lib_string_strptr1 -> char
  POKE lib_string_strptr2 , char
  WHILE char
    PEEK lib_string_strptr1 -> char
    POKE lib_string_strptr2 , char
    INC lib_string_strptr1
    INC lib_string_strptr2
    DEC lib_string_sc_copymaxchars
    IF lib_string_sc_copymaxchars == 0
      POKE lib_string_strptr2 , 0
      SUBEND
    ENDIF
  WEND

FEND

//-----------------------------------------------------------
// lib_string_memcmp
//
//  Purpose:
//    Compare two memory areas.
//
//-----------------------------------------------------------

BYTE lib_string_mc_res

FUNC lib_string_memcmp ( lib_string_ptr1 lib_string_ptr2 lib_string_length out:lib_string_mc_res )

  BYTE b1
  BYTE b2

  LET lib_string_mc_res = 1
  WHILE lib_string_length
    PEEK lib_string_ptr1 -> b1
    PEEK lib_string_ptr2 -> b2
    IF b1 != b2
      SUBEND
    ENDIF
    INC lib_string_ptr1
    INC lib_string_ptr2
    DEC lib_string_length
  WEND

  LET lib_string_mc_res = 0
FEND


//-----------------------------------------------------------
// lib_string_memcpy
//
//  Purpose:
//    Copy contants of a memory area to another memory area
//-----------------------------------------------------------

FUNC lib_string_memcpy ( lib_string_ptr1 lib_string_ptr2 lib_string_length )

  BYTE btemp

  WHILE lib_string_length
    PEEK lib_string_ptr1 -> btemp
    POKE lib_string_ptr2 , btemp
    INC lib_string_ptr1
    INC lib_string_ptr2
    DEC lib_string_length
  WEND
FEND


LABEL lib_string_skip

#IFEND
