/* strings.c - hack - no strcasecmp in base library, we use the special allocation.
 *  Copyright (C) 2022 by Jed Marti
 *   This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see https://www.gnu.org/licenses/.
 */

// Note: a big user of OSEG (18 bytes).

#include <C8051F380.h>
#include <stdint.h>
#include <math.h>

#include "storage.h"
#include "strings.h"
#include "serial.h"

extern __xdata uint16_t   PC;
extern __xdata uint8_t    I[];
extern __xdata uint8_t    D[];
extern uint16_t           DREG;
extern __xdata char       STR[];
extern __xdata uint16_t   actives;
extern __xdata floatOrInt IACC;
extern __xdata floatOrInt IACCB;
extern __xdata floatOrInt FACC;
extern __xdata floatOrInt FACCB;

// Swiped from lex. Note that we rely on the linker to ignore that there its a uint8_t
extern __xdata int8_t ttype;



__bit strCaseEq(char __code *b)
// Compare STR[] to b (must be code space) and return 0/1. DREG used in place of a pointer to
// save data space. String comparison is case insensitive.
{
  char aa;
  char bb;

  DREG = (uint16_t) (char __xdata *) STR;
  while (*((char __xdata *)DREG) != 0 && *b != 0)
  {
    aa = *((char __xdata *) DREG);
    ++DREG;
    if (aa >= 'a' && aa <= 'z') aa -= 32;
    bb = *b++;
    if (bb >= 'a' && bb <= 'z') bb -= 32;
    if (aa != bb) return 0;
  }
  return ((*((char __xdata *)DREG)) == 0 && *b == 0)?1:0;
}



__bit strEq(char *a, char *b)
//     Same as above but case sensitive.
{
  while (*a != 0 && *b != 0) if (*a++ != *b++) return 0;
  return *a == 0 && *b == 0?1:0;
}



char __xdata *strCpy(char __xdata *dest, char *src)
//     Copy a string from a random location (RAM, CODE, XDATA, whereever) to dynamic location.
// See dmalloc for where these go.
{
  while (*src != 0) *dest++ = *src++;
  *dest = 0;
  return dest;
}



uint16_t strlenSTR(void)
//     The number of bytes in the zero terminated string STR (see lex.c for the buffer).
// This is currently 16 bits but doesn't really need to be.
{
  uint16_t cnt;
  for (cnt = 0; STR[cnt] != 0; ++cnt);
  return cnt;
}



uint16_t newStringSTR(void)
//     Create a new string from the string in STR. It's allocated in dynmaic space, the tag is
// set and the address returned.
{
  uint16_t sl;
  sl = strlenSTR() + 1;
  DREG = dmalloc(sl);
  setTag(DESC_STRING);
  strCpy(&D[DREG + 3], STR);
  return DREG;
}



uint16_t strlenI(void)
//     The length of a string in I space. Once again 16 bits but doesn't need to be.
{
  uint16_t sl = 0;
  uint8_t __xdata *tpc;

  for (tpc = &I[PC]; *tpc != 0; ++tpc) ++sl;
  return sl;
}
  
  

uint16_t newStringI(void)
//     Move a string from I space (at PC) to D space. This gets done over and over, not very
//   efficient. The alternative is to grim to consider. Returns the D space offset.
{
  uint16_t sl;
  
  sl = strlenI() + 1;
  DREG = dmalloc(sl);
  setTag(DESC_STRING);
  strCpy(&D[DREG + 3], &I[PC]);
  return DREG;
}



char __xdata *int2string(char __xdata *s)
//     Convert a 32 bit integer into a string. Zero terminate the string and return the
// XDATA address of this 0.
{
  __bit z = 0;
  __xdata uint8_t i;
  IACCB.i = 1000000000;
  if (IACC.i < 0)
  {
    *s++ = '-';
    IACC.i = - IACC.i;
  }
  for (i = 0; i < 9; ++i)
  {
    if (z || IACC.i >= IACCB.i)
    {
      *s++ = IACC.i / IACCB.i + '0';
      IACC.i %= IACCB.i;
      z = 1;
    }
    IACCB.i /= 10;
  }
  *s++ = IACC.i + '0';
  *s = 0;
  return s;
}



static void pow10(int8_t i)
//     FACCB.f becomes the ith power of 10.0.
{
  FACCB.f = 1.0;
  while (i > 0) { FACCB.f *= 10.0f; --i; }
}



#define lg10 ttype
char __xdata *float2string(char __xdata *s)
//     Convert a float to a string and return the address of the 0 at the end when done.
{
  //  int8_t lg10;

  if (FACC.f < 0.0f)
  {
    *s++ = '-';
    FACC.f = -FACC.f;
  }
  lg10 = (int8_t) log10f(FACC.f);

  if (lg10 < 0)
  {
    pow10(-lg10);
    FACC.f *= FACCB.f;
  }
  else
  {
    pow10(lg10);
    FACC.f /= FACCB.f;
  }
  IACC.i = FACC.f *  10000.0f;
  *s++ = IACC.i / 10000 + '0';
  IACC.i %= 10000;
  *s++ = '.';
  *s++ = IACC.i / 1000 + '0';
  if (IACC.i > 0)
  {
    IACC.i %= 1000;
    *s++ = IACC.i / 100 + '0';
    if (IACC.i > 0)
    {
      IACC.i %= 100;
      *s++ = IACC.i / 10 + '0';
      if (IACC.i > 0)
      {
        IACC.i %= 10;
        *s++ = IACC.i + '0';
      }
    }
  }

  if (lg10 != 0)
  {
    *s++ = 'e';
    if (lg10 < 0)
    {
      *s++ = '-';
      lg10 = -lg10;
    }
    if (lg10 >= 10)
    {
      *s++ = lg10 / 10 + '0';
      lg10 %= 10;
    }
    *s++ = lg10 + '0';
  }
  *s = 0;
  return s;
}
#undef lg10


// EOF
