/*****************************************************************************/ /* */ /* 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 */ } /*****************************************************************************/