/********************************  parser.c  *******************************
 * $Source: /home/cvs/ESP/gen2/software/msp430/lib/common/parser.c,v $
 *  Copyright (C) 2003 MBARI
 *
 *  MBARI Proprietary Information. All rights reserved.
 * $Id: parser.c,v 1.6 2005/11/24 00:26:17 brent Exp $
 *
 * Serial message parsing and processing
 *    in a "sort of" object oriented fashion
 *
 * Note: This only partially implments the parser interface
 *       defined in parser.h
 *
 *****************************************************************************/

#include "string.h"
#include "interrupts.h"  //just for AWAITEV()
#include "parser.h"

/*
 On input tests, it is necessary to disable interrupts between the
 FIFO length check and the AWAITEV() to ensure that an input that
 might arrive between those two operations is missed.  This would
 otherwise cause the system to sleep until the next event 
 (of any type) occured.
*/

void parserAwaitInput (fifo *from, unsigned length)
/*
  block until fifo contains at least length bytes
*/
{
  if (FIFOlen (from) < length) {
    unsigned oldSR = _BIC_SR (GIE);  //ensure interrupts are disabled
retest:
    if (FIFOlen(from) < length) {
      AWAITEV();
      _DINT();
      goto retest;
    }
    _BIS_SR (oldSR);    //restore GIE state      
  }
}


static inline void 
  awaitAppend (fifo *from, byte *empty)
/*
  block until fifo tail != specified "empty" point
  equivalant to awaitInput (fifo, 1) with a specified tail
*/
{
  if (FIFOtail(from) == empty) {
    unsigned oldSR = _BIC_SR (GIE);  //ensure interrupts are disabled
retest:
    if (FIFOtail(from) == empty) {
      AWAITEV();
      _DINT();
      goto retest; //goto generates better code than while with , operator
    }
    _BIS_SR (oldSR);    //restore GIE state      
  }
}


static inline void 
  waitWhileEmpty (fifo *from)
/*
  block until fifo is not empty.
*/
{
  awaitAppend (from, FIFOhead(from));
}


unsigned parserGetByte (parserInstance *instance)
/*
  return the next byte from the host
  block while no bytes available
*/
{
  fifo *from = instance->input;
  waitWhileEmpty (from);
  return FIFOreadByte (from);
}


unsigned parserGetByteAhead (parserInstance *instance, byte **head)
/*
  like getByte above, but may advance a head pointer outside the fifo body
  so that the input may be rescanned or discarded later.
*/
{
  fifo *from = instance->input;
  awaitAppend (from, *head);
  return FIFOreadByteAhead (from, head);
}


uint16 parserGetWord (parserInstance *instance)
/*
  return the next word from the host
  block while no words available
*/
{
  fifo *from = instance->input;
  parserAwaitInput (from, sizeof (uint16));
  return FIFOread16 (from);
}


void parserPutByte (parserInstance *instance, unsigned b)
/*
  write b to the host
  block while buffer full
*/
{
  fifo *to = instance->output;
  // no need to retest with interrupts disabled in this case, because
  // the next byte sent from a full fifo will generate another event
  // (assuming the size of the output fifo > 1)!
  while (!FIFOwriteByteIfNotFull (to, b)) AWAITEV();
  parserKickstart(instance);
}


void parserPutWord (parserInstance *instance, uint16 word)
/*
  write word to the host
  block while buffer full
*/
{
  fifo *to = instance->output;
  // no need to retest with interrupts disabled in this case, because
  // the next byte sent from a full fifo will generate another event
  // (assuming the size of the output fifo > sizeof uint16)!
  while (FIFOfree(to) < sizeof(uint16)) AWAITEV();
  FIFOwrite16 (to, word);
  parserKickstart(instance);
}


void parserPutBlock (parserInstance *instance, byte *src, unsigned len)
/*
  output len byte memory block to serial port
*/
{
  if (len) {
    fifo *to = instance->output;
    do {
      unsigned chunkLen;
      while (!(chunkLen = FIFOpoke (to))) AWAITEV();
      if (chunkLen > len) chunkLen = len;
      FIFOwriteChunk (to, src, chunkLen);
      parserKickstart(instance);
      src += chunkLen;
      len -= chunkLen;
    } while (len);
  }
}
  

void parserPutString (parserInstance *instance, char *s)
/*
  write NUL terminated string s to host
*/
{
  parserPutBlock (instance, (byte *)s, strlen (s));
}



void parserGetBlock (parserInstance *instance, byte **dst, unsigned len)
/*
  input len bytes to *dst
*/
{
  if (len) {
    fifo *from = instance->input;
    do {
      unsigned chunkLen;
      if (!(chunkLen = FIFOpeek (from))) {
        unsigned oldSR = _BIC_SR (GIE);  //ensure interrupts are disabled
retest:
        if (!(chunkLen = FIFOpeek (from))) {
          AWAITEV();
          _DINT();
          goto retest;
        }
        _BIS_SR (oldSR);    //restore GIE state  
      }
      if (chunkLen > len) chunkLen = len;
      FIFOreadChunk (from, *dst, chunkLen);
      *dst += chunkLen;     
      len -= chunkLen;
    } while (len);
  }
}


 
void parserSkipInput (parserInstance *instance, unsigned len)
/*
  skip past next len bytes of input
*/
{
  if (len) {
    fifo *from = instance->input;
    fifoBody *body = from->body;
    do {
      unsigned chunkLen;
      if (!(chunkLen = FIFOpeek (from))) {
        unsigned oldSR = _BIC_SR (GIE);  //ensure interrupts are disabled
retest:
        if (!(chunkLen = FIFOpeek (from))) {
          AWAITEV();
          _DINT();
          goto retest;
        }
        _BIS_SR (oldSR);    //restore GIE state  
      }
      if (chunkLen > len) chunkLen = len;
      body->head = FIFOadvance (from, body->head, chunkLen);
      len -= chunkLen;
    } while (len);
  }
}


//input an unsigned value in hexidecimal from the specified port
//terminated by carridge return
// returns < 0 if input was invalid
int parserGetHex (parserInstance *port, unsigned limit)
{
 unsigned value = 0x8000;
 do {
    unsigned hexDigit = parserGetByte(port);
    if (value <= limit && hexDigit == '\r') return value;
    if (hexDigit < '0') break;
    parserPutByte (port, hexDigit);  //echo user input
    if (hexDigit >= 'a') hexDigit -= 'a'-'A';
    hexDigit -= '0';
    if (hexDigit > 9) {
      hexDigit -= 'A'-'9'-1;
      if (hexDigit <= 9) break;
    }
    if (hexDigit > 0xf) break;
    value <<= 4;
    value |= hexDigit;
    if (value > 0xff) break;
  } while (true);
  return -1;
}

