/*****************************************************************************/
/*                                                                           */
/*  conio.c         ;high-level serial console I/O routines for IC           */
/*                  ;works with both the Handy Board and the 6.270 board     */
/*                                                                           */
/*  by                                                                       */
/*                                                                           */
/*  Dr. Richard F. Drushel                                                   */
/*  Department of Biology                                                    */
/*  Case Western Reserve University                                          */
/*  Biology 300                                                              */
/*  2080 Adelbert Road                                                       */
/*  Cleveland, Ohio  44106-7080  U.S.A.                                      */
/*  rfd@po.cwru.edu                                                          */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  VERSION HISTORY:                                                         */
/*                                                                           */
/*  Whenever the program is updated, record it here.  Add new version info   */
/*  to the top of this list, so the newest version is always first.          */
/*                                                                           */
/*  2.0     18 May 1997         ;Richard F. Drushel                          */
/*                              ;added high-level serial input routines      */
/*                              ;cgets(), cgeti(), cgetf(), and cgetline()   */
/*                              ;as well as atof() to convert an ASCII       */
/*                              ;string to a float.  Internal functions      */
/*                              ;_isspace(), _isdigit(), _isspaceordelim(),  */
/*                              ;_isdelimiter(), and _isascii()              */
/*                                                                           */
/*                              ;Note:  input routines tested with           */
/*                              ;debug_serialio.c library, but have *not*    */
/*                              ;yet been tested using a real serial link    */
/*                              ;for input.  Output routines, however,       */
/*                              ;*have* been tested using a real serial link */
/*                              ;for output, as well as with the debugging   */
/*                              ;library.                                    */
/*                                                                           */
/*  1.2     16 May 1997         ;Richard F. Drushel                          */
/*                              ;comment style standardization               */
/*                              ;replaced cgetchar() with serial_getchar()   */
/*                              ;now requires serialio.c                     */
/*                                                                           */
/*  1.1     22 August 1996      ;Richard F. Drushel                          */
/*                              ;added cputs(), cputi(), cwritei(), cputf(), */
/*                              ;and cwritef().  Renamed cwritestringarray() */
/*                              ;to cwrites().  Junked cwriteintarray().     */
/*                                                                           */
/*  1.0     24 February 1996    ;original code by Chris Cifra and Geoffrey   */
/*                              ;Schmit, student teaching assistants for     */
/*                              ;BIOL/EBME/CMPS/NEUR 479, Spring 1996, CWRU. */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PUBLIC FUNCTIONS:                                                        */
/*                                                                           */
/*  These functions may be used freely by user programs.                     */
/*                                                                           */
/*  void cwrites(char string[])     ;sends a string to the console, no \n    */
/*                                                                           */
/*  void cputs(char string[])       ;sends a string to the console with \n   */
/*                                  ;(linefeed, 0x0A)                        */
/*                                                                           */
/*  void cwritei(int a)             ;sends a signed integer in ASCII form    */
/*                                  ;to the console, no \n                   */
/*                                                                           */
/*  void cputi(int a)               ;sends a signed integer in ASCII form    */
/*                                  ;to the console with \n (linefeed, 0x0A) */
/*                                                                           */
/*  void cwritef(float a)           ;sends a float in exponential format     */
/*                                  ;to the console, no \n                   */
/*                                                                           */
/*  void cputf(float a)             ;sends a float in exponential format to  */
/*                                  ;to the console with \n (linefeed, 0x0A) */
/*                                                                           */
/*  float atof(char s[])            ;converts an ASCII string representation */
/*                                  ;of an int, float, or E-format float to  */
/*                                  ;a float.                                */
/*                                                                           */
/*  void cgets(char s[])            ;reads a comma- or linefeed-delimited    */
/*                                  ;or quoted ASCII string from the console */
/*                                                                           */
/*  float cgetf()                   ;reads a comma- or linefeed-delimited    */
/*                                  ;numeric ASCII string as a float from    */
/*                                  ;the console                             */
/*                                                                           */
/*  int cgeti()                     ;reads a comma- or linefeed-delimited    */
/*                                  ;numeric ASCII string as an int from     */
/*                                  ;console                                 */
/*                                                                           */
/*  void cgetline(char s[])         ;reads a newline-delimited ASCII string  */
/*                                  ;from the console                        */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PUBLIC GLOBAL VARIABLES:                                                 */
/*                                                                           */
/*  These global variables may be accessed freely by user programs.  Any     */
/*  restrictions on their use (e.g., flags which are read-only) should be    */
/*  noted here.  It is suggested that global variables be named in all       */
/*  capital letters, to avoid confusion with local varibles which are        */
/*  defined within functions.                                                */
/*                                                                           */
/*  char SERIAL_RECV_BUFFER[256]    ;used for the cgetxxx() function family. */
/*                                  ;Allocated as a global because the       */
/*                                  ;default stacksize for processes is too  */
/*                                  ;small.  Not reentrant, so be careful!   */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PRIVATE FUNCTIONS:                                                       */
/*                                                                           */
/*  These are internal functions which have no public entry points, and      */
/*  which user programs should not access directly.  It is suggested that    */
/*  private functions be named with a leading underscore (_), to avoid       */
/*  confusion with user-defined functions which might have the same name.    */
/*                                                                           */
/*  int _isspace(int a)             ;returns 1 for space or tab, 0 otherwise */
/*                                  ;used by atof() and cgets()              */
/*                                                                           */
/*  int _isdigit(int a)             ;returns 1 if a digit 0-9, 0 otherwise   */
/*                                  ;used by atof()                          */
/*                                                                           */
/*  int _isspaceordelim(int a)      ;returns 1 if a space, tab, comma, or    */
/*                                  ;linefeed; otherwise returns 0           */
/*                                  ;used by cgets()                         */
/*                                                                           */
/*  int _isdelimiter(int a)         ;returns 1 if a comma or linefeed;       */
/*                                  ;otherwise returns 0                     */
/*                                  ;used by _isspaceordelim() and cgets()   */
/*                                                                           */
/*  int _isascii(int a)             ;returns 1 if an ASCII character >31     */
/*                                  ;and <128, otherwise returns 0           */
/*                                  ;used by cgets() and cgetline()          */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  PRIVATE GLOBAL VARIABLES:                                                */
/*                                                                           */
/*  These are internal global variables which user programs should not       */
/*  access directly.  It is suggested that private global variables be       */
/*  named in all capital letters with a leading underscore (_), to avoid     */
/*  confusion with user-defined global variables which might have the same   */
/*  name.                                                                    */
/*                                                                           */
/*      None.                                                                */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*  EXTERNAL LIBRARY FILE DEPENDENCIES:                                      */
/*                                                                           */
/*  Any external functions or variables which are used but not defined in    */
/*  this file should be noted here.  List the external library filename,     */
/*  the library function, and the local calling function.                    */
/*                                                                           */
/*  math.c              ;external abs() in cwritei()                         */
/*                      ;external abs(), fabs(), fint(), sgn() in cwritef()  */
/*                                                                           */
/*  serialio.c          ;external serial_getchar() and serial_putchar()      */
/*                      ;used just about everywhere here :-)                 */
/*                                                                           */
/*      or                                                                   */
/*                                                                           */
/*  debug_serialio.c    ;emulates serial_putchar() with printf() and         */
/*                      ;serial_getchar() with an input BUFFER[] string;     */
/*                      ;see debug_serialio.c file for details.              */
/*                                                                           */
/*****************************************************************************/


/*****************************************************************************/
/*                                                                           */
/*                        global variable declarations                       */
/*                                                                           */
/*****************************************************************************/

char SERIAL_RECV_BUFFER[256];       /* for serial I/O of strings */

/*****************************************************************************/
/*                                                                           */
/*                            function declarations                          */
/*                                                                           */
/*****************************************************************************/

void cwrites( char string[] )       /* sends a null-terminated string   */
                                    /* to the console.  Note:  the null */
                                    /* is *NOT* sent, nor is a newline  */
                                    /* character.  Use cputs() to send  */
                                    /* newline-delimited strings.       */
{
    int x=0;
    while( string[x] != 0 )             /* until we reach the end */
    {
        serial_putchar( string[x] );    /* send a character */
        x++;                            /* point to next character */
    }
}

/*****************************************************************************/

void cputs( char string[] )         /* sends a null-terminated string to     */
                                    /* the console, *with* newline character */
                                    /* newline is line feed 0x0A             */
{
    cwrites( string );              /* send the string */
    serial_putchar( 10 );           /* send the newline */
}

/*****************************************************************************/

void cwritei (int a)        /* sends a signed integer in ASCII form   */
                            /* note:  does not send newline character */
                            /* (use cputi() for that instead).        */
{
    char b[8];          /* string workspace, including space for terminal null */
    int c=0;            /* remainder accumulator */
    int d[5];           /* divisors for isolation of decimal digits */
    int i=0;            /* index into string */
    int n;              /* general register */
    int leading=1;      /* flag that we're still in leading mode */
    
    /* initialize divisor array */
    d[0]=1;
    for (n=1;n<=4;n++)
        d[n]=d[n-1]*10;         /* make powers of 10 from 1 to 1e4 */
    
    /* now check for sign */    
    if (a<0)
    {
        b[i]=45;                    /* save minus sign as ASCII */
        i++;                        /* point to next string slot */
    }
    a=abs(a);                       /* throw away the sign */
    
    /* now get the digits */
    for (n=4;n>=0;n--)
    {
        c=(a/d[n]);                 /* try to get a digit */
        if (c!=0)                   /* a non-zero digit */
        {
            if (leading==1)
                leading=0;          /* no more leading zeros */
            b[i]=c+48;              /* save it as an ASCII digit */
            i++;                    /* and point to next */
        }
        else                        /* the digit *is* zero */
        {
            if (((leading==1) && (n==0)) || (leading==0))   /* embedded zero or true zero */
            {
                b[i]=48;                                    /* save ASCII zero */
                i++;                                        /* point to next */
            }
            /* otherwise, ignore leading zeroes */
        }
        a=a%d[n];                   /* get next remainder */    
    }
    b[i]=0;         /* null-terminate the string */
    cwrites(b);     /* copy it to the console with no newline */
}

/*****************************************************************************/

void cputi(int a)                   /* sends a signed integer in ASCII form */
                                    /* followed by a newline (line feed).   */
                                    /* Use cwritei() to send integers with  */
                                    /* no newline character.                */
{
    cwritei(a);             /* send the ASCII string with no newline */
    serial_putchar(10);     /* send the newline (line feed, 0x0A)    */
}

/*****************************************************************************/

void cwritef(float a)               /* sends a float in exponential format   */
                                    /* no newline sent; use cputf() for that */
                                    /* only valid for absolute values        */
                                    /* in range of 1e-37 to 1e+37            */
{
    char m[8];          /* string workspace for mantissa, with terminal null */
    char e[6];          /* string workspace for exponent, with terminal null */
                        /* really only need 5, but compiler bombs on only 5  */
    float d[6];         /* divisors for isolation of mantissa decimal digits */
    float c;            /* general float register */
    int e2=-38;         /* exponent accumulator */
    float f=1e-37;      /* exponent base register */
    int i=0;            /* index into mantissa string */
    int n;              /* general int register */
    
    /* some string initializations */
    m[1]=46;            /* ASCII "." */
    m[7]=0;             /* mantissa terminal null */
    e[0]=69;            /* ASCII "E" */
    e[4]=0;             /* exponent terminal null */
    
    /* initialize divisor array -- this is more accurate than a multiply loop */
    d[0]=1e0;
    d[1]=1e1;
    d[2]=1e2;
    d[3]=1e3;
    d[4]=1e4;
    d[5]=1e5;
    
    /* immediate check for true zero */
    if (a==0.)
    {
        m[0]=48;                    /* make mantissa 0.00000 */
        m[2]=48;
        m[3]=48;
        m[4]=48;
        m[5]=48;
        m[6]=48;
        e[1]=43;                    /* ASCII "+" */
        e[2]=48;                    /* make exponent 00 */
        e[3]=48;
        cwrites(m);                 /* send mantissa */
        cwrites(e);                 /* send sign */
        return;
    }
    
    /* check for mantissa sign */       
    if (a<0.)
    {                           /* if mantissa is negative,     */
        serial_putchar(45);     /* send the minus sign as ASCII */
        a=fabs(a);              /* throw away the mantissa sign */
    }
    
    /* find our exponent and its sign */
    while (f<=a)
    {
        f*=10.;             /* next power of 10 */
        e2++;               /* next exponent */
    }
    a=(1./f)*a;             /* gives us a mantissa between 1.0 and 10.0 */
    a=(1e6)*a;              /* now gives us a number ranging 1000000.0 to 9999999.9 */
                            /* we only have 6 digits of accuracy guaranteed,*/
    if (fint((a/d[5])+.00001)==10.)     /* if we wrapped around */
    {
        a=a/10.;                        /* shift us down again */
        e2++;                           /* and increment the exponent */
    }
        
    if (sgn(e2)<0)
        e[1]=45;            /* ASCII "-" */
    else
        e[1]=43;            /* ASCII "+" */
    
    e2=abs(e2);             /* discard the sign if negative */
    e[2]=(e2/10)+48;        /* get 10s digit as ASCII */
    e[3]=(e2%10)+48;        /* get 1s digit as ASCII */

    /* now get the mantissa digits */
    for (n=5;n>=0;n--)
    {
        c=fint((a/d[n])+.00001);    /* get a digit */
        m[i]=((int)(c))+48;         /* save it as an ASCII digit */
        i++;                        /* point to next slot */
        if (n==5)                   /* if it's the first digit. */
            i++;                    /* skip over the decimal point */
        a=a-(c*d[n]);               /* get the next remainder */
    }
    
    /* at last, we can write the rest of the number to the console */
    /* remember, we already sent the mantissa sign */   
    cwrites(m);                 /* send the mantissa */
    cwrites(e);                 /* send the exponent */
}

/*****************************************************************************/

void cputf(float f)                 /* sends a float in exponential format  */
                                    /* followed by a newline (line feed).   */
                                    /* Use cwritef() to send floats with    */
                                    /* no newline character.                */
{
    cwritef(f);             /* send the ASCII string with no newline */
    serial_putchar(10);     /* send the newline (line feed, 0x0A) */
}

/*****************************************************************************/

int _isspace(int a)         /* returns 1 for space or tab, 0 otherwise     */
                            /* internal routine used by atof() and cgets() */

{
    return ((a == 32) || (a == 9));     /* 32 is space, 9 is tab */
}

/*****************************************************************************/

int _isdigit(int a)         /* returns 1 if a digit 0-9, 0 otherwise */
                            /* internal routine used by atof()       */

{
    return ((a >= 48) && (a <= 57));    /* 48 is '0', 57 is '9' */
}

/*****************************************************************************/

int _isdelimiter(int a)     /* returns 1 if comma or linefeed; otherwise 0 */
                            /* internal routine used by _isspaceordelim()  */
                            /* and cgets()                                 */

{
    return ((a == 44) || (a == 10));    /* 44 is comma, 10 is linefeed */
}

/*****************************************************************************/

int _isspaceordelim(int a)  /* returns 1 if a space, tab, comma, */
                            /* or linefeed; otherwise, returns 0 */
                            /* internal routine used by cgets()  */

{
    return ((_isspace(a)) || (_isdelimiter(a)));
}

/*****************************************************************************/

int _isascii(int a)         /* returns 1 if an ASCII character >31 and <128, */
                            /* otherwise returns 0                           */
                            /* internal routine used by cgets()              */

{
    return ((a > 31) && (a < 128));
}

/*****************************************************************************/

float atof(char s[])    /* Convert a string containing a number in ASCII     */
                        /* form (integer, float, or exponential float) to a  */
                        /* float.  Strips whitespace characters (space and   */
                        /* tab) from the front of the string, but stops      */
                        /* parsing at the first (unexpected) non-numeric     */
                        /* character if the string has garbage at the end.   */
                        /* This means that "  34.3foo78" translates to 34.3. */
                        /* Modified from atof() function in the standard     */
                        /* library of the Hi-Tec C compiler for CP/M.        */
                        /* Note:  all string literals converted to decimal   */
                        /* form because IC can't deal with string literals   */
                        /* in math calculations.                             */
                        /* Also note:  very ugly code because IC will not    */
                        /* allow any math operations on pointers!  Thus, the */
                        /* the number string has to be treated as an array!  */
                        /* Also also note:  no error handling; assumes that  */
                        /* the string is a valid representation of a number! */
                        /* Valid range for exponential-format numbers is     */
                        /* approximately 2.0e-38 to 3.4e+38.                 */

{
    int     i=0;            /* index into string array */
    int     sign=0;         /* mantissa sign flag:  0=positive, 1=negative */
    int     exp0=0;         /* mantissa exponent counter */
    int     eexp=0;         /* E-form exponent counter */
    int     expsign=0;      /* exponent sign flag:  0=positive, 1=negative */
    float   m=0.0;          /* mantissa accumulator */

    /* skip any leading whitespace (space, tab) */
    while (_isspace(s[i]))
        i++;                                /* skip it */

    /* check for mantissa sign */
    if (s[i] == 45)                         /* 45 is '-' */
    {
        sign = 1;                           /* flag minus sign */
        i++;                                /* point to next */
    }
    else if (s[i] == 43)                    /* 43 is '+' */
        i++;                                /* point to next */

    /* now get all digits up to either a decimal point or an e/E */
    while (_isdigit(s[i]))
    {
        m = 10.0*m + (float)(s[i] - 48);    /* 48 is '0' */
        i++;                                /* point to next */
    }

    /* no more digits, so check for decimal point */
    if (s[i] == 46)                         /* 46 is '.' */
    {
        i++;                                /* point to next */
        /* get all digits after decimal point */
        while (_isdigit(s[i]))
        {
            exp0--;
            m = 10.0*m + (float)(s[i] - 48);    /* 48 is '0' */
            i++;                                /* point to next */
        }
    }

    /* check for e/E exponential form */
    if ((s[i] == 101) || (s[i] == 69))      /* 101 is 'e', 69 is 'E' */
    {
        i++;                                /* point to next */
        /* check for exponent sign */
        if (s[i] == 45)                     /* 45 is '-' */
        {
            expsign = 1;                    /* flag negative exponent */
            i++;                            /* point to next */
        }
        else if (s[i] == 43)                /* 43 is '+' */
            i++;                            /* point to next */

        /* now get exponent */
        while (_isdigit(s[i]))
        {
            eexp = eexp*10 + s[i] - 48;     /* 48 is '0' */
            i++;                            /* point to next */
        }

        /* adjust exponent sign */
        if (expsign)
            eexp = -eexp;                   /* make it negative */
    }

    /* compute absolute value of final float */
    exp0 += eexp;
    while (exp0 < 0)                    /* for negative exponents */
    {
        m = m / 10.0;
        exp0++;
    }
    while (exp0 > 0)                    /* for positive exponents */
    {
        m = m * 10.0;
        exp0--;
    }

    /* adjust final float sign from mantissa */
    if (sign)
        return (-m);                    /* negative */
    else
        return (m);                     /* positive */
}

/*****************************************************************************/

void cgets(char s[])    /* get a comma- or linefeed-delimited or quoted */
                        /* string.  Ignores whitespace and non-ASCII    */
                        /* characters (<32 and >127)                    */
{
    int i=0;            /* index into string */
    int a;              /* current character */
    int q=0;            /* flag for quote mode, 0=no quotes */

    while (_isspace(a=serial_getchar()));       /* strip leading whitespace */
    
    /* check for empty string (such as ,,) */
    if (_isdelimiter(a))
    {
        s[i]=0;             /* empty string, so null-terminate it */
        return;             /* and exit */
    }

    /* non-leading space character; is it a quote? */
    if (a == 39)
        q=39;               /* single quote ' */
    else if (a == 34)
        q=34;               /* double quote " */

    if (q == 0)             /* no quotes */
    {
        if (_isascii(a))        /* if it's ASCII */
        {
            s[i]=a;             /* save character */
            i++;                /* point to next slot */
        }
        
        while (!(_isspaceordelim(a=serial_getchar())))
        {
            if (_isascii(a))    /* if it's ASCII */
            {
                s[i]=a;         /* save character */
                i++;            /* point to next */
            }
        }
    }
    else    /* keep everything between the quotes */
    {
        while (!((a=serial_getchar()) == q))    /* until the closing quote...*/
        {
            if (_isascii(a))    /* if it's ASCII */
            {
                s[i]=a;         /* save character */
                i++;            /* point to next slot */
            }
        }
        
        /* a now holds a quote; we need to keep scaning for a delimiter. */
        /* To reuse code and provide a common exit for quoted and non-   */
        /* quoted strings, let's make a into a space.  That way, we can  */
        /* fall into the delimiter-search routine.                       */
        
        a=32;   /* 32 is space */        
    }
    
    /***** WARNING HERE *****/
    /* We hope that there is a valid delimiter somewhere at the end! */
    /* Since there is no serial_ungetch(), we can only keep grabbing */
    /* characters and throwing them away until we come to a comma or */
    /* linefeed.                                                     */

    if (_isspace(a))
        while (!(_isdelimiter(a=serial_getchar()))) /* ignore everything   */
                                                    /* until we come to  a */
                                                    /* comma or linefeed   */
        
    s[i]=0;             /* we're done, so null-terminate it */
}

/*****************************************************************************/

float cgetf()       /* reads an ASCII string from the console as a float    */
                    /* leading and trailing whitespace (space, tab) is      */
                    /* stripped out.                                        */
                    /* trailing delimiters are comma, and linefeed          */

{
    cgets(SERIAL_RECV_BUFFER);              /* get the numeric string */
    return (atof(SERIAL_RECV_BUFFER));      /* convert it to a float */
}

/*****************************************************************************/

int cgeti()         /* reads an ASCII string from the console as a signed */
                    /* int.  Note:  integer underflow and overflow are    */
                    /* trapped, so out-of-range inputs are converted to   */
                    /* -32768 and 32767, respectively.  We use cgetf() to */
                    /* guarantee that we can at least read the number.    */
                    /* This avoids the runtime error you'd get by simply  */
                    /* trying to cast the float to an int.                */

{
    float f;                        /* temporary workspace */

    f=cgetf();                      /* read the number as a float */
    if (f > 32767.0)                /* if it's too big, */
        return (32767);             /* return the biggest integer */
    else if (f < -32768.0)          /* if it's too small, */
        return (-32768);            /* return the smallest integer */
    else return ((int)f);           /* otherwise, it's safe to cast to int */
}

/*****************************************************************************/

void cgetline(char s[])     /* get an entire line delimited by linefeed      */
                            /* discards non-ASCII characters (<32 and >127)  */

{
    int i=0;                /* index into string */
    int a;                  /* current character */

    while ((a=serial_getchar()) != 10)      /* as long as it isn't a linefeed */
    {
        if (_isascii(a))    /* if it's an ASCII character */
        {
            s[i]=a;         /* save it */
            i++;            /* point to next */
        }
    }

    s[i]=0;                 /* null-terminate it */
}

/*****************************************************************************/


