/*********************************  I2Cnode.c  *******************************
 * $Source: /home/cvs/ESP/gen2/software/msp430/lib/common/I2Cnode.c,v $
 *  Copyright (C) 2003 MBARI
 *
 *  MBARI Proprietary Information. All rights reserved.
 * $Id: I2Cnode.c,v 1.18 2006/05/17 00:18:02 brent Exp $
 *
 * I2C node message parsing and processing
 *    in a "sort of" object oriented fashion
 * I2Cnode is derived from the code the I2Cnode.c
 *
 * Theory of operation:
 *
 *  Unlike the gateway, which bridges an RS-232 port to the I2C bus,
 *  I2Cnodes are simply endpoints on the bus.  They may receive or originate
 *  messages and interface with other nodes and gateways as peers.
 *
 *  Variable length I2C events are logged in a byte oriented "event" fifo.
 *  The first byte represents the event's type.  
 *  This event type byte indicates whether the event was an caused by a master
 *  writing to or reading this node.  In the case of writes, it also indicates
 *  whether the write was followed by a CRC or not.
 *
 *  Each event type is followed by a length byte.  The total
 *  number of bytes in each event is 2+this length byte.
 *  that determines the number of bytes that follow.
 *  Note that any messages longer than 255 bytes are silently dropped.
 *
 *  The main (background) processing loop removes and parses these events
 *  and may output events to other nodes.  Note that there is no output
 *  event fifo.  Instead, the main processing loop blocks while mastering
 *  the I2C bus.
 *
 *  The I2C interrupt service routines call an event "filter" hook
 *  to notify the application of each incoming message.  The filter function
 *  may either ignore the message, handle it (quickly!) in the context
 *  of the ISR, or append the event to the application's event queue
 *  for later processing in its main loop.  If an event is deferred,
 *  the filter will also need to invoke signalEV() to wake up the background
 *  in case it was sleeping.
 *
 *  When a master sends us a read request, the I2C interrupt service
 *  routines call the readReq application hook. As the I2C bus is hung
 *  awaiting the application's response, the recommended practice is
 *  that readReq should call I2CnodeAnswer() directly.
 *
 * Known Bugs:
 *
 *  When in the stuckI2Cstate, reads always return CRCflow.
 *  On could argue that the app should still get notified of them in this case.
 *
 *****************************************************************************/

#include "dwarf.h"      //register and I/O definitions
#include "interrupts.h" //support for nested interrupts
#include "recode7.h"    //recoding 8-bit data in 7-bit streams
#include "crc8.h"       //crc byte support

#include "I2Cnode.h"

#if FALSE  //debugging
extern struct fifo toHost;
#include "kickstart.h"
#define put8debug(byte)   (FIFOwriteByteW(&toHost,(byte)), kickstart())
#else
#define put8debug(byte) 
#endif

// Macros & Constants

#define TICDELAY     ((TIMERHZ+2)/4)  //quarter second is minimum bus timeout
#define FLOWDELAY    ((TIMERHZ+16)/32)//1/32 for CRCNACKing slave flow control
#define HUNGDELAY    ((TIMERHZ+16)/32)//1/32 second between reset retries
#define INITIALDELAY ((TIMERHZ+4)/8)  //wait >1/8 sec if lost track of master

#define NULLprobe                 //use zero byte msg to probe for adr in use
#define PADbyte  (0xff)           //byte value for pad space
#define CRCACK   (CRC8seed^0xff)  //response byte to acknowledge a CRC message
#define CRCflow  (0x0)            //response to CRC message if slave wants bus

#define I2Ccmd  I2CSSEL_2 //I2C clock selection constant

#define DRBdepth        5         //max depth of the receive fifo (I2CIRB)

#define FIFOnearlyFull(events)          (FIFOfree (events)<=DRBdepth+4)
#define FIFOnearlyFullFrom(events,tail) (FIFOfreeFrom (events,tail)<=DRBdepth)

#define quickCRC(instance)  (instance->retries & 0x80)


// DATA -- dynamic processing state
static enum { //sundry internal state flags
  CRCready            = 0x1,  //CRCvalue contains a valid CRC response
  arbTimeout          = 0x2,  //arbTimer reached zero
  selected            = 0x4,  //selected
  enterLimboOnAL      = 0x8,  //enter limbo state of arbitration lost
  inTic               = 0x10  //prevents reentry into I2CnodeTic
} flags;


static byte matchedAdr;         //address by which we were last selected

static byte I2CDRbytes;   //# of bytes loaded to/waiting in I2CDRB
static byte bitsUnsent;	  //bit counter value at last NACK error

static byte retriesRemaining;   //# of retries remaining

static byte masterResult; //reason why master xfer terminated (interrupt mask)

static byte CRCvalue;     //CRC-8 accumulator

static unsigned byteCount; //for reads and master operations
static byte *cursor;       //for all operations

static uint16 busTimer;   //countdown to bus command timeout (in tics)
static uint16 arbTimer;   //countdown to bus arbitration timeout


// I2C bus interupt handler utilities and macros

//current state of I2C interrupt processing
#define I2Cstate (instance->current->state)

// assign new slave interrupt processing state and I2C interrupt mask
#define enterSlaveState(newState,intMask) \
  (I2Cstate = (newState), I2CIE = (intMask))


    
static void ignoreisr (I2CnodeInstance *instance)
/*
  Null interrupt service function to ignore the event
  or as a placeholder
*/
{
}


static inline void holdSCL(void)
/*
  hold SCL low on its next falling edge
*/
{   
  clear8 (DWRFI2COUT, DWRFI2CHOLD|DWRFI2CINC);
}

static inline void releaseSCL(void)
/*
  reset the CPLD's I2C clock counter low to stop counting bits
*/
{   
  set8 (DWRFI2COUT, DWRFI2CHOLD|DWRFI2CINC);
}

//true if SCL is being held or will be held in the next falling edge
#define SCLheld()  !(DWRFI2COUT & DWRFI2CHOLD)


static void throttle (I2CnodeInstance *instance)
/*
  try to stop bus in slave mode (by holding SCL low)
  when the output buffer is nearly full
*/
{
  if (FIFOnearlyFull(instance->events)) {
    holdSCL();
    signalEV();  //in case main loop was waiting to start a master operation
  }
}


static inline void unthrottle (void)
/*
  give up trying throttle the bus when master ends
  the transaction to spite our efforts to stop it
*/
{
  releaseSCL();
}



// I2C Interrupt processing states

extern I2Cisr USART0_defaultISR;
static I2Cisr TXRDYwrite, TXRDYwriteCRC, TXRDYidle, TXRDYlimbo, TXRDYstuck;
static I2Cisr RXRDYwrite, RXRDYread, RXRDYreadAck;
static I2Cisr ARDYmaster, ARDYread0, ARDYwriteCRC, ARDYreadAck;
static I2Cisr ALmaster, ALerror, NACKadr, NACKwrite, NACKCRC;
static I2Cisr OAselect, OAreselect, OAreceived, OAmaster, OAresponded;
static I2Cisr GCselect, GCreselect, GCreceived, GCmaster;
static I2Cisr RXRDYreceive, RXRDYreceiving, RXRDYidle;
static I2Cisr TXRDYrespond, TXRDYresponding, TXRDYpadding, TXRDYreceived;
static I2Cisr ARDYreceived, ARDYresponded, ARDYwrite0;
#ifdef NULLprobe
static I2Cisr ARDYprobe;
#endif
static I2Cisr STTselecting, STTreceived, STTidle, STTstuck, STTlimbo;

//aliases to avoid repeating function definitions
#define STTresponded STTidle
#define NACKresponded STTidle
#define RXRDYresponded RXRDYidle

//selected by master
static I2Cisr * const selectI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,     //slaves don't do arbitration
 ignoreisr,     //can't have NACK yet
 OAreselect,    //repeated specific call
 ignoreisr,     //placeholder -- we must process STT, RXRDY, or TXRDY first
 RXRDYreceive,  //receive first byte from master
 TXRDYrespond,  //send first byte to master
 GCreselect,    //repeated general call
 STTselecting   //handle repeated start before first data byte
};
#define selectIE (ALIE|OAIE|RXRDYIE|TXRDYIE|GCIE|STTIE)
   
//receiving from master
static I2Cisr * const receiveI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,     //slaves don't do arbitration
 ignoreisr,     //no ACK expected while receiving
 OAreceived,    //repeated specific call
 ARDYreceived,  //transaction complete -- transition back to idle
 RXRDYreceiving,//receive next byte
 TXRDYreceived, //send first byte -- transition to responding
 GCreceived,    //repeated general call
 STTreceived    //repeated start in receive state
};
#define receiveIE (ALIE|OAIE|ARDYIE|RXRDYIE|TXRDYIE|GCIE|STTIE)

//responding to master
static I2Cisr * const respondI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,      //slaves don't do arbitration
 NACKresponded,  //NACKs from MASTER on our response -- transition back to idle
 OAresponded,    //repeated specific call -- transition back to select
 ARDYresponded,  //transaction complete -- transition back to idle
 RXRDYresponded, //receive first byte -- transition to receiving
 TXRDYresponding,//send another byte to master
 GCselect,       //repeated general call -- transition back to select
 STTresponded    //repeated start in respond state -- back to idle
};
//sending pad bytes after master has read our complete response
static I2Cisr * const paddingI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,      //slaves don't do arbitration
 NACKresponded,  //NACKs from MASTER on our response -- transition back to idle
 OAresponded,    //repeated specific call -- transition back to select
 ARDYresponded,  //transaction complete -- transition back to idle
 RXRDYresponded, //receive first byte -- transition to receiving
 TXRDYpadding,  //send another pad byte to master
 GCselect,       //repeated general call -- transition back to select
 STTresponded    //repeated start in respond state -- back to idle
};
#define respondIE (ALIE|NACKIE|OAIE|ARDYIE|RXRDYIE|TXRDYIE|GCIE|STTIE)

//dropping message where abritration was lost AFTER sending the address byte
static I2Cisr * const limboI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,      //lost arbitration (shouldn't happen)
 ignoreisr,      //missing ACK from slave (?)
 OAresponded,    //got slave address -- transition to selected
 ARDYresponded,  //transaction complete -- transition back to idle
 RXRDYwrite,     //throw byte away
 TXRDYlimbo,     //we're lost, just throw master a fish
 GCselect,       //got general call -- transition to selected
 STTlimbo        //repeated start -- NACK quick CRC and back to idle
};
#define limboIE  respondIE

//dropping receive messages while app attempts to master the bus after
//SCL has been held low while receiving due to a full event buffer.
static I2Cisr * const stuckI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,      //lost arbitration (shouldn't happen)
 ignoreisr,      //missing ACK from slave (?)
 ignoreisr,      //got slave address -- ignore it
 ARDYresponded,  //transition back to idle so we can try to master the bus
 RXRDYwrite,     //throw byte away
 TXRDYstuck,     //we're swamped, tell master to let us get a msg in edgewise
 ignoreisr,      //got general call -- ignore it
 STTstuck        //NACK quickCRC and remain in stuck state
};
#define stuckIE  respondIE

//controller will not reset!
//must be just before the initialI2Cstate
static I2Cisr * const hungI2Cstate[] = {
  USART0_defaultISR
};
#define hungIE  (0)

//awaiting own address or general call before syncing to the bus
//must be just before the idleI2Cstate
static I2Cisr * const initialI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,    //lost arbitration (shouldn't happen)
 ignoreisr,    //missing ACK from slave (?)
 OAselect,     //got 1st slave address -- transition to selected
 ignoreisr,    //shouldn't complete operation without first seeing OA or GC
 RXRDYidle,    //error unless GCIFG
 TXRDYidle,    //error unless GCIFG
 GCselect,     //got 1st general call -- transition to selected
 STTidle       //initial bus synchronization complete
};
#define initialIE  (ALIE|OAIE|GCIE|TXRDYIE|RXRDYIFG|STTIE)

//awaiting own address or general call
static I2Cisr * const idleI2Cstate[] = {
 USART0_defaultISR,
 ignoreisr,    //lost arbitration (shouldn't happen)
 ignoreisr,    //missing ACK from slave (?)
 OAselect,     //got slave address -- transition to selected
 ignoreisr,    //shouldn't complete operation without first seeing OA or GC
 RXRDYidle,    //error unless GCIFG
 TXRDYidle,    //error unless GCIFG
 GCselect      //got general call -- transition to selected
};
#define idleIE  (ALIE|OAIE|TXRDYIE|RXRDYIFG|GCIE)

//idleI2Cstate must be the last slave state defined
//all the states past here are master mode states


//writing short message to a slave using 8-bit (counted) encoding
static I2Cisr * const writeI2Cstate[] = {
 USART0_defaultISR,
 ALmaster,     //lost arbitration while master
 NACKwrite,    //missing ACK from slave
 OAmaster,     //we selected ourselves
 ARDYmaster,   //master write transaction complete
 RXRDYwrite,   //master received a byte while writing (?)
 TXRDYwrite,   //master ready to transmit a byte
 GCmaster      //we selected ourselves
};
//writing short CRC message to a slave using 8-bit (counted) encoding
static I2Cisr * const crcWriteI2Cstate[] = {
 USART0_defaultISR,
 ALmaster,     //lost arbitration while master
 NACKwrite,    //missing ACK from slave
 OAmaster,     //we selected ourselves
 ARDYwriteCRC, //master write transaction complete
 RXRDYwrite,   //master received a byte while writing (?)
 TXRDYwriteCRC,//master ready to transmit a byte
 GCmaster      //we selected ourselves
};
//broadcasting CRC message to a slaves using 8-bit (counted) encoding
static I2Cisr * const crcGCwriteI2Cstate[] = {
 USART0_defaultISR,
 ALmaster,     //lost arbitration while master
 NACKwrite,    //missing ACK from slave
 OAmaster,     //we selected ourselves
 ARDYmaster,   //master write transaction complete
 RXRDYwrite,   //master received a byte while writing (?)
 TXRDYwriteCRC,//master ready to transmit a byte
 GCmaster      //we selected ourselves
};
//write null message to slave 
static I2Cisr * const writeNullI2Cstate[] = {
 USART0_defaultISR,
 ALmaster,     //lost arbitration while master
 NACKadr,      //missing ACK from slave  (must be address NACK)
 OAmaster,     //we selected ourselves
 ARDYwrite0,   //null master write transaction complete
 RXRDYwrite,   //master received a byte while writing (?)
 TXRDYwrite,   //master ready to transmit the byte
 GCmaster      //we selected ourselves
};
#ifdef NULLprobe
//awaiting ARDY after null write to probe a proposed address
static I2Cisr * const probeI2Cstate[] = {
 USART0_defaultISR,
 ALmaster,     //lost arbitration while master
 NACKadr,      //missing ACK from slave
 OAmaster,     //we selected ourselves
 ARDYprobe,    //master probe transaction complete
 RXRDYwrite,   //master received a byte while writing (?)
 TXRDYlimbo,   //master ready to transmit with this interrupt disabled?!
 GCmaster      //we selected ourselves
};
#endif
//awaiting only byte in slave's msg ACK response 
static I2Cisr * const readAckI2Cstate[] = {
 USART0_defaultISR,
 ALerror,      //lost arbitration when it shouldn't happen
 NACKCRC,      //missing CRC ACK from slave
 OAmaster,     //we selected ourselves
 ARDYreadAck,  //master CRC read transaction complete
 RXRDYreadAck, //master received CRC ack byte from slave
 TXRDYlimbo,   //master ready to transmit while reading??
 GCmaster      //we selected ourselves
};
//awaiting subsequent bytes in slave's response
static I2Cisr * const readI2Cstate[] = {
 USART0_defaultISR,
 ALerror,      //lost arbitration at a time when it shouldn't happen
 NACKadr,      //NACK must be address in read mode
 OAmaster,     //we selected ourselves
 ARDYmaster,   //master read transaction complete
 RXRDYread,    //master received next byte
 TXRDYlimbo,   //master ready to transmit while reading??
 GCmaster      //we selected ourselves
};
#define masterIE (ALIE|NACKIE|OAIE|ARDYIE|RXRDYIE|TXRDYIE|GCIE) 


// Various ways to beat a confused I2C controller into submission

static inline void cancelI2C (void)
// cancel master operation in progress.  leaves controller disabled
{
  I2CTCTL = I2Ccmd;
  clear8 (U0CTL, I2CEN);
  U0CTL = I2C | SYNC | LISTEN; 
    //Looping back the data may help keep controller from hanging if it is
    //resumed while the bus is busy
}


static inline void resumeI2Cslave (void)
// resume normal master operations after a cancelI2C
{
  U0CTL = I2C | SYNC | I2CEN;  //finally, re-enable I2C on USART0
  I2CIFG = 0;
}


static inline void resumeI2C (void)
// resume all normal operations after a cancelI2C
{
  releaseSCL();
  resumeI2Cslave();
}


static void abortCmd (void)
{
  releaseSCL();
  clear8 (DWRFIRQIE, DWRFI2CSCL);
  disableDWRFirqs (DWRFI2CHELD);    //just in case we were awaiting countdown
}


//cancel all pending and future I2C interrupts
//global interrupts must be disabled on entry
static void endI2Cinterrupts (void)
{
  I2CIFG = I2CIE = 0;
  abortCmd();
}


//try anything to get the controller to reset
//enter hungI2Cstate if all else fails
//Also updates I2COA with the currently configured I2C address
//must be called with global interrupts enabled
static boolean coreI2Creset (I2CnodeInstance *instance)
{
  I2CIE = 0;
  busTimer = 0;  //stop processing timeouts
  _EINT();
  flags &= inTic|arbTimeout;
  cancelI2C();
  signalEV();
  I2COA = (unsigned)instance->current->ownAdr & 0x7f;
  if (I2CDCTL & I2CBUSY || I2CTCTL != I2Ccmd) { //controller is hung
    I2CNDAT = 1;
    clear8 (P3SEL, I2Cclock|I2Cdata);  //disconnect controller from the bus
    resumeI2C();  //this time, let the command complete...
    set8 (U0CTL, MST | LISTEN);
    I2CTCTL = I2Ccmd | I2CTRX | I2CSTT;
    I2Cstate = hungI2Cstate;  //enter hung state
    busTimer = HUNGDELAY;     // and reset again later
    if (TXRDYIFG) I2CDRB = PADbyte;
    return false;
  }
  resumeI2C();
  return true;
}



static void resetI2C (I2CnodeInstance *instance)
{
  if (coreI2Creset (instance)) {
    enterSlaveState (idleI2Cstate, idleIE);
    busTimer = instance->timeout;
    _EINT();
  }
}


static void resetI2CafterNACK (I2CnodeInstance *instance)
{
  if (coreI2Creset (instance)) {
    enterSlaveState (initialI2Cstate, initialIE);
    busTimer = 2;
    _EINT();
  }
}


static void initialI2Creset (I2CnodeInstance *instance)
{
  if (I2Cstate == hungI2Cstate) cancelI2C();
  connectI2C();
  if (coreI2Creset (instance)) {
    enterSlaveState (initialI2Cstate, initialIE);
    busTimer = INITIALDELAY;
    _EINT();
  }       
}


static void restartI2C (I2CnodeInstance *instance)
{
  cancelI2C();
  if (I2CDCTL & I2CBUSY || I2CTCTL != I2Ccmd) //controller is hung
    initialI2Creset(instance);
  else {
    enterSlaveState (idleI2Cstate, idleIE);
    resumeI2C();
  }
}


//interrupts must be disabled
static inline void startArbTimer (I2CnodeInstance *instance)
{
  uint16 tics = instance->timeout;
  arbTimer = tics + (tics >> 2);  //25% longer than timeout
  flags &= ~arbTimeout;
}

//start counting down for bus arbitration timeout if bus already synchronized
static void restartArbTimer (I2CnodeInstance *instance)
{
  _DINT();
  startArbTimer(instance);
  _EINT();
}


//quickly and quietly reset in slave mode
static inline void resyncSlave (I2CnodeInstance *instance) 
{
  restartI2C(instance);
}


/***************** I2C Slave processing ************/


//reenter idle state and signal background
#define enterIdle() {enterSlaveState(idleI2Cstate,idleIE); signalEV();}


//inform app of a null message written to us
static void putNullWrite(I2CnodeInstance *instance)
{
  fifo *events = instance->events;
  byte msgType = I2CnodeNonCRCmsg;
  if (!matchedAdr) msgType = I2CnodeGCmsg;
  cursor = FIFOaddByte (events, 
                        FIFOaddByte (events, FIFOtail(events), msgType), 0);  
  instance->filter(instance);   //let the app decide whether or not to accept it
}


static inline void beSelected (I2CnodeInstance *instance)
/*
  every select is preceeded by a start, but we don't see it until
  later because STTIFG is lowest priority.  So, clear STTIFG
  if it's coincident with select so that it is not mistaken
  for a restart condition in the current message.
*/
{
  clear8 (I2CIFG, STTIFG);
  flags |= selected;
  busTimer = instance->timeout;
}


//enter select state after matching slave adr
static void slaveSelected (I2CnodeInstance *instance)
{
  if (!matchedAdr && instance->current->ownAdr & 0x80) 
    resyncSlave (instance);
  else{
    beSelected(instance);
    if (!(I2CIFG & TXRDYIFG))       //if master wants to send us something...
      throttle(instance);           //stop SCL if our event fifo almost full
    enterSlaveState (selectI2Cstate, selectIE);
  }
}


//matched slave adr (by RESTART) in selected state
static void slaveReselected (I2CnodeInstance *instance)
{
  beSelected(instance);
  if (!(I2CIFG & TXRDYIFG)) {
    putNullWrite (instance);
    throttle(instance);
  }
}  


//process end of received message that ended with a STOP condition
static void nonCRCmsgRcvd (I2CnodeInstance *instance)
{
  fifo *events = instance->events;
  byte *tail = FIFOtail(events);
  unsigned msgLen = FIFOdelta (events, cursor, tail) - 2;
  if (msgLen <= 0xff) { //simply ignore message if is was too long
    byte msgType = I2CnodeNonCRCmsg;
    if (!matchedAdr) msgType = I2CnodeGCmsg;
    FIFOaddByteW (events, FIFOaddByte (events, tail, msgType), msgLen);
    instance->filter (instance);
  }
  flags &= ~CRCready;   //CRC response is no longer valid
}


//process end of received message that ended without a STOP condition
//last byte in msgIn is the CRC-8 code
static void crcMsgRcvd (I2CnodeInstance *instance)
{
//clear8 (P5OUT, 0x40);
//set8 (P5OUT, 0x40);
  if (!CRCvalue) {  //CRC matched received message
    fifo *events = instance->events;
    byte *tail = FIFOtail(events);
    byte *msgEnd = cursor = FIFOdec (events, cursor); //remove CRC;
    unsigned msgLen = FIFOdelta (events, msgEnd, tail) - 2;
    CRCvalue = CRCACK;
    if (msgLen <= 0xff) { //simply ignore message if is was too long
      byte msgType = I2CnodeCRCmsg;
      if (!matchedAdr) msgType = I2CnodeGCCRCmsg;
      FIFOaddByteW (events, FIFOaddByte (events, tail, msgType), msgLen);
      instance->filter (instance);
    }
  }else
    CRCvalue = ~CRCACK;
  flags |= CRCready;   //CRC response is ready
}


//Own Address Match in selected state
static void OAreselect (I2CnodeInstance *instance)
{
  matchedAdr = I2COA;
  slaveReselected(instance);
}

//General Call address match in selected state
static void GCreselect(I2CnodeInstance *instance)
{
  matchedAdr = 0;
  slaveReselected(instance);
}


//received our first START condition.  Now we can arbitrate for the bus
static void STTidle (I2CnodeInstance *instance)
{
  enterIdle();        //exit initialI2Cstate
}


//Own Address Match in idle state -- transition to selected state
static void OAselect (I2CnodeInstance *instance)
{
  matchedAdr = I2COA;
  slaveSelected(instance);
}

//General Call address match in idle state -- transition to selected state
static void GCselect(I2CnodeInstance *instance)
{
  matchedAdr = 0;
  slaveSelected(instance);
}


static void STTselecting (I2CnodeInstance *instance)
/*
  A null write message won't be terminated by ARDYIFG, even
  if it ends with a STOP condition.  If the next message
  is to a different node, all we'll see it the STTIFG...
*/
{
  putNullWrite (instance);  //report null write
  enterIdle();
}


//null writes don't generate an ARDY interrupt even after a STOP
//invoke this frequently (w/interrupts disabled) to check for end of null msgs
#macro pollSelect(state)
  if (!(I2CDCTL&I2CBB))
    if (state==stuckI2Cstate) {
      enterIdle();
    }else if (state==selectI2Cstate && I2CIE&STTIE)
      STTselecting (instance);
#endm


//if we're trying to access the bus while we're holding SCL low to
//prevent our event buffer from overflowing -- WE'RE STUCK!
//enter stuck state and drop messages to avoid deadlock
static void avoidDeadlock (I2CnodeInstance *instance)
{
  if (SCLheld()) { //enter stuck state and drop messages to avoid deadlock
    releaseSCL();
    enterSlaveState (stuckI2Cstate, stuckIE);  //exit when bus is freed
  }
}


//We suddenly switched to responding intervening select
//This happens if we missed an OAIFG interrupt earlier
static void TXRDYreceived (I2CnodeInstance *instance)
{  //the receive fifo was cleared by the START condition -- we're lost
  resyncSlave (instance);  
}

  
//add all remaining bytes from I2CDRB to the output fifo
//assumes RXRDYIFG set and >= DRBsize bytes free in the output fifo
static void inline flushRcvrCore (I2CnodeInstance *instance)
{
  fifo *events = instance->events;
  unsigned count = DRBdepth;
  for (;;) {
    unsigned next = I2CDRB;
    CRC8next (CRCvalue, next);
    cursor = FIFOaddByteW (events, cursor, next);
    if (!(I2CIFG & RXRDYIFG)) break;
    if (!--count) {   //ran out of buffer space. Panic!
      TXRDYreceived (instance); 
      return;
    }
  }
}


//add all remaining bytes from I2CDRB to the output fifo
//assumes >= DRBsize bytes free in output fifo
#macro flushRcvr(instance)
{
  if (I2CIFG & RXRDYIFG) flushRcvrCore(instance);
}
#endm


//Received byte in select state -- process first byte & enter receive
static void RXRDYreceive (I2CnodeInstance *instance)
{
  unsigned first = I2CDRB;
  fifo *events = instance->events;
  cursor = FIFOaddByteW (events, FIFOadvance(events,FIFOtail(events),2), first); 
  CRC8init (CRCvalue);
  CRC8next (CRCvalue, matchedAdr);
  CRC8next (CRCvalue, first);
  enterSlaveState (receiveI2Cstate, receiveIE);
  flags &= ~CRCready;   //indicate that CRCvalue is being computed
}


//Received next byte in receive state
static void RXRDYreceiving (I2CnodeInstance *instance)
{
  flushRcvrCore (instance);
  {  //Throttle or give up if there's an impending FIFO overflow
    fifo *events = instance->events;
    if (FIFOnearlyFullFrom(events, cursor))
      // give up if FIFO is already empty or message > 256 bytes long
      if (FIFOempty(events) ||
          FIFOdelta (events, cursor, FIFOtail(events)) > 0xff+2)
        enterSlaveState (limboI2Cstate, limboIE);
      else{ //throttle the bus while we process queued events
        holdSCL();
        signalEV();  //in case main loop was waiting to start a master operation
      }
  }
}


//Send a one bit CRC ACK if quickCRC's are configured
#macro ackQuickCRC(instance, crcRemainder)
  if (quickCRC(instance)) {           //CRC checking w/single bit ACK enabled?
    set8 (P3DIR, I2Cclock|I2Cdata);   //drive SDA to signal message ACK
    clear8 (P3SEL, I2Cclock);   //drive just SCL low
    if ((crcRemainder)
//           || !(MCLKnow & 0xf)  //if uncommented, produces sporatic errors testing
    ) clear8 (P3DIR, I2Cdata);  //SDA will float when its SEL bit cleared below      
    //send single bit ACK or NACK to sender if msg ended in repeated start
    clear8 (P3SEL, I2Cdata);    //drive SDA low to signal ACK, float for NACK
    clear8 (DWRFIRQIFG, DWRFI2CSCL);
    set8 (DWRFIRQIE, DWRFI2CSCL); //interrupt on next SCL rising edge

    //rev B silicon is confused our CRC ACK bit -- so, reset again
    clear8 (U0CTL, I2CEN);
    U0CTL = I2C | SYNC; 
    U0CTL = I2C | SYNC | I2CEN;  //finally, re-enable I2C on USART0

    clear8 (P3DIR, I2Cclock);     //release SCL
  }  //SCL will rise after the master reads SDA
#endm


//Process message that ended with a REPEATED START condition
//but not followed immediately by another address
static void STTreceived (I2CnodeInstance *instance)
{
  //STT event has cleared the receiver fifo in hardware.  Data is lost to us.
  unthrottle();
  ackQuickCRC(instance, CRCvalue);
  enterIdle();
  crcMsgRcvd(instance);
}  


//Process message that ended with a REPEATED START condition
//but not followed immediately by another address if we're in stuck state
static void STTstuck (I2CnodeInstance *instance)
{
  //STT event has cleared the receiver fifo in hardware.  Data is lost to us.
  unthrottle();
  ackQuickCRC(instance, true);  //force a CRC NACK
}  


//Process message that ended with a REPEATED START condition
//but not followed immediately by another address if we're in limbo state
static void STTlimbo (I2CnodeInstance *instance)
{
  STTstuck (instance);
  enterIdle();
}  


//Access Ready in receive state -- finish non-CRC message & enter idle
static void ARDYreceived (I2CnodeInstance *instance)
{
  byte ifg = I2CIFG;
  unthrottle();
  enterIdle();
  flushRcvr(instance);
  if (ifg & STTIFG) { //sometimes a START will generate a spurious ARDYIFG!
    ackQuickCRC(instance, CRCvalue); //if so, ignore the ARDYIFG and
    crcMsgRcvd(instance);  //process msg as though it ended with REPEATED START
  }else
    nonCRCmsgRcvd(instance);
}


//Own Address in receive state -- finish message & enter selected
static void OAreceived (I2CnodeInstance *instance)
{
  byte ifg = I2CIFG;
  matchedAdr = I2COA;
  slaveSelected (instance);
  flushRcvr(instance);
  if (ifg & ARDYIFG)
    nonCRCmsgRcvd (instance);
  else
    crcMsgRcvd (instance);   //never saw STOP, so it's a CRC message
}


//General Call in receive state -- finish message & enter selected
static void GCreceived (I2CnodeInstance *instance)
{
  matchedAdr = 0;
  slaveSelected (instance);
  crcMsgRcvd (instance);  //we would not be in this state if we'd seen a STOP
}  


//Transmitter Ready in respond state -- send next byte of response
static void TXRDYresponding (I2CnodeInstance *instance)
{
  if (byteCount)
    I2CDRB = *cursor, cursor++, --byteCount;
  else{
    I2Cstate = paddingI2Cstate;  //start padding past the end of response
    TXRDYpadding (instance);
  }
}


//Transmitter Ready in padding state -- send next pad byte of response
static void TXRDYpadding (I2CnodeInstance *instance)
{
  unsigned count = DRBdepth;
  for (;;) {
    I2CDRB = PADbyte;
    if (!(I2CIFG & TXRDYIFG)) break;
    if (!--count) {
      resyncSlave (instance);   //give up if controller appears to be hung
      return;
    }
  }
}


//Transmitter Ready in select state -- start response & enter respond
static void TXRDYrespond (I2CnodeInstance *instance)
{
  enterSlaveState (respondI2Cstate, respondIE);
  if (flags & CRCready && !quickCRC(instance)) {
    I2CDRB = CRCvalue;
    byteCount = 0;
  }else
    instance->readReq (instance);  //bus is held at this point!
      //readReq fn MUST call I2CnodeAnswer or cause it to be called ASAP!
}


//service current I2CnodeReadReq event
I2CnodeErr 
  I2CnodeAnswer (I2CnodeInstance *instance, byte *buffer, unsigned len)
{
  unsigned oldIE = _BIC_SR(GIE);
  I2CnodeErr result = I2CnodeOK;   //fill the delay slot
  if (I2Cstate == respondI2Cstate) {
    cursor = buffer;
    byteCount = len;
    TXRDYresponding (instance);
  }else
    result = I2CnodeBusErr;
  _BIS_SR(oldIE);
  return result;  
}


//Transmitter Ready in read state -- flag that we're lost
static void TXRDYlimbo (I2CnodeInstance *instance)
{
  I2CDRB = PADbyte;
  flags |= enterLimboOnAL;
}


//Transmitter Ready in stuck state -- beg master for flow control
static void TXRDYstuck (I2CnodeInstance *instance)
{
  I2CDRB = CRCflow;
}


//Access Ready in respond state -- enter idle
static void ARDYresponded (I2CnodeInstance *instance)
{
  flags &= ~CRCready;     //remember that we saw a STOP
  enterIdle();
}


//Own Address Match in responding state -- transition to selected state
static void OAresponded (I2CnodeInstance *instance)
{
  if (I2CIFG & ARDYIFG) {  //pending ARDY being superceded by OA?
    clear8 (I2CIFG, ARDYIFG);
    flags &= ~CRCready;  //remember that we've seen a STOP
  }
  matchedAdr = I2COA;
  slaveSelected(instance);
}


//Receiver or Transmitter ready in the idle state
//This usually means that select occured twice while interrupts were disabled
//or that GCIFG has been superceded by TXRDY or RXRDYIFG.
//GCIFG should have been a higher priority -- we must check it here explicitly
static void RXRDYidle (I2CnodeInstance *instance)
{
  if (I2CIFG & GCIFG) {
    clear8 (I2CIFG, GCIFG);
    GCselect(instance);
  }
  RXRDYreceive (instance);
}

static void TXRDYidle (I2CnodeInstance *instance)
{
  if (I2CIFG & GCIFG) {
    clear8 (I2CIFG, GCIFG);
    GCselect(instance);
  }
  TXRDYrespond (instance);
}


/***************** I2C Master processing ************/

static inline void nineMoreSCLs(void)
/*
  pulse the CPLD's I2C clock counter low to allow nine more bits
  to transfer  (8 + 1 for ACKnlowlege)
*/
{   
  clear8 (DWRFI2COUT, DWRFI2CINC), set8 (DWRFI2COUT, DWRFI2CINC);
}


#macro oneMoreByte(mask)
//incement I2CDRbytes after writing or reading to/from I2CDRB
//mask is either TXRDYIFG for writing or RXRDYIFG for reading
{
  I2CDRbytes++;
  if (!(I2CIFG & mask)) do
    nineMoreSCLs();
  while (--I2CDRbytes);
}
#endm

#macro creditLastByte()
//credit the last byte transferred
{
  I2CDRbytes++;
  do nineMoreSCLs(); while (--I2CDRbytes);
}
#endm

#macro creditAllBytes()
//credit all bytes that have been transferred
{
  if (I2CDRbytes) do nineMoreSCLs(); while (--I2CDRbytes);
}
#endm

#define creditDRBwrite()  oneMoreByte(TXRDYIFG)
#define creditDRBread()   oneMoreByte(RXRDYIFG)


//quit master mode after an error occurred (& I2C interrupts already "ended")
static inline void quitMaster(I2CnodeInstance *instance)
{
  I2Cstate = idleI2Cstate;
  signalEV();
}


//resume slave processing and signal wakeup
static inline void exitMaster (I2CnodeInstance *instance)
{
  enterIdle();  //for now, same as slave entering idle
}


//resume slave processing, signal wakeup and set success indicator
static inline void masterSuccess (I2CnodeInstance *instance)
{
  set8 (masterResult, ARDYIE);  //flag that all went well
  exitMaster (instance);
}


static void stopI2C (I2CnodeInstance *instance, I2Cisr *reset)
/*
  Bit bang an I2C STOP condition
*/
{
  set8 (P3DIR, I2Cclock|I2Cdata);
  clear8 (P3SEL, I2Cclock);          //drive just SCL low
  clear8 (DWRFIRQIFG, DWRFI2CSCL);
  set8 (DWRFIRQIE, DWRFI2CSCL); //interrupt on next SCL rising edge
  clear8 (P3SEL, I2Cdata);           //drive SDA low too
  reset(instance);  //while both SDA and SCL are being driven low
  clear8 (P3DIR, I2Cclock);          //let just the clock line float
  //resulting I2CSCL interrupt invokes connectI2C() which floats SDA
}


//Own Address Match in master state -- just update matched address
static void OAmaster (I2CnodeInstance *instance)
{
  matchedAdr = I2COA;
  beSelected(instance);
}

//General Call address match in master state -- just update matched address
static void GCmaster(I2CnodeInstance *instance)
{
  matchedAdr = 0;
  beSelected(instance);
}


//lost arbitration as master at a point when it should not be possible to do so
static void ALerror(I2CnodeInstance *instance)
{ 
  I2CTCTL = I2Ccmd;
  endI2Cinterrupts();
  quitMaster(instance);
put8debug('A');  //arbitration error
}


//lost arbitration while master
static void ALmaster(I2CnodeInstance *instance)
{
  I2CTCTL = I2Ccmd;
  clear8 (I2CIFG, TXRDYIFG);//clear any pending TXRDY from aborted operation
  if (flags & arbTimeout)   //been trying to acquire the bus too long...
    ALerror (instance);
  else{
    abortCmd();
    enterIdle();
    if (flags & (selected|enterLimboOnAL))
      if (!matchedAdr && instance->current->ownAdr & 0x80) {
        cancelI2C(); resumeI2C(); //ignore general calls
      }else if (flags & enterLimboOnAL) 
        enterSlaveState (limboI2Cstate, limboIE);
      else if (flags & selected)  //enter slave selected state immediately
        enterSlaveState (selectI2Cstate, selectIE);
    set8 (masterResult, ALIE);
put8debug('a');  //arbitration
  }
}


//missing ACK from slave during write
static void NACKwrite(I2CnodeInstance *instance)
{
  I2CTCTL = I2Ccmd;
  bitsUnsent = readDWRF (DWRFI2CSCLCNT); //bits unxferred for error report
  endI2Cinterrupts();
  I2CDRbytes = byteCount;		//bytes remaining to send for error report
  set8 (masterResult, NACKIE);
  quitMaster(instance);
put8debug('n');  //nack'd data
}


//missing ACK from slave
static void NACKadr(I2CnodeInstance *instance)
{
  I2CTCTL = I2Ccmd;
  endI2Cinterrupts();
  I2CDRbytes = 0;     //force I2CnodeBytesXferred to report 0
  set8 (masterResult, NACKIE);
  quitMaster(instance);
put8debug('N');  //nack'd address
}


//generic master transaction complete
static void ARDYmaster(I2CnodeInstance *instance)
{
  I2CTCTL = I2Ccmd;
  masterSuccess(instance);
}

#ifdef NULLprobe
//master null probe transaction complete
static void ARDYprobe(I2CnodeInstance *instance)
{
  masterSuccess(instance);   //some other node responded to our new address
}
#endif

//master null transaction complete
static void ARDYwrite0(I2CnodeInstance *instance)
{
  stopI2C(instance, restartI2C);  //relinquish bus & reset the hung controller
  set8 (masterResult, ARDYIE);  //flag that all went well
  signalEV();
}


//trying to write to ourselves and/or another node has our address
static void RXRDYwrite(I2CnodeInstance *instance)
{ 
  volatile ignore = I2CDRB;
  flags |= enterLimboOnAL;
}


//true iff controller is ready to accept a new address or command respectively
#define ready4adr() (!(DWRFIRQIE & DWRFI2CSCL))

//busy parameter is either I2CBUSY or I2CBUSY|I2CBB|I2CSCLLOW
#define ready4cmd(busy)  (ready4adr() && rdy4core(busy))

#define rdy4core(busy) (I2CTCTL==I2Ccmd && !(I2CDCTL & (busy)))
    

static byte startMaster(I2CnodeInstance *instance, unsigned ndat, byte cmd)
/*
  enter specified master interrupt processing state
  issue cmd to start the transaction
  returns non-zero "startResult" if command failed with ineterrupts enabled.
  returns with interrupts disabled if command successfully started.
*/
{
  I2CDRbytes = 0;
  for (;;) {  //wait to reach idleI2Cstate or arbitration timeout
    I2Cvectors state;
    _DINT();       //ensure state stable while probed
    state = I2Cstate;
    if (flags & arbTimeout) break;
    if (state == idleI2Cstate) {
      if (ready4cmd(I2CBUSY|I2CBB|I2CSCLLOW)) {
        if (!ndat) { //for null write case...
          clear8 (DWRFI2COUT, DWRFI2CINC);  //count down from 10 rather than 9
          I2CNDAT = 1;
        }else{
          I2CNDAT = ndat;
          byteCount = ndat;
        }
        set8 (U0CTL, MST);
        clear8 (DWRFI2COUT, DWRFI2CHOLD);  //enable the I2C clock counter
        I2CTCTL = cmd;
        if (U0CTL & MST) goto startedOK;
        releaseSCL();  //disable the I2C clock counter if we're not bus master
        if (masterResult = I2CIFG & (ARDYIFG|NACKIFG|ALIFG)) goto cmdDone;
        I2CTCTL = I2Ccmd;
put8debug('I');  //keep trying if command was ignored
      }
      _EINT();
      continue;
    }else{
      avoidDeadlock(instance);  //release the bus if we're holding SCL low
      pollSelect(state);  //hurry back to the idle state in case of null write
    }
    AWAITEV();
  }
  masterResult = ALIE|STTIE;  //unrecoverable arbitration error
cmdDone:
  I2CTCTL = I2Ccmd;
  _EINT();
put8debug('E');  //comand already terminated
  return masterResult;
  
startedOK:    
  busTimer = instance->timeout;   //let command finish before checking bus again
  flags &= ~(selected|enterLimboOnAL);
  return masterResult = 0;
}


static inline byte masterEnd (I2CnodeInstance *instance)
/*
  wait for transaction to complete
  returns masterResult
  interrupts are enabled
  NOTE: This assumes that the successive const structs are assembled into
        ascending memory locations.  Every compiler I've used does this.
        But, I don't think there's a hard rule that this must always be so.
*/
{
  do
    AWAITEV(), _DINT();   //sleep while message is transferred via interrupt
  while (I2Cstate > idleI2Cstate);  //while still in a master mode state
  _EINT();
  return masterResult;
}

static inline
  byte masterWrite (I2CnodeInstance *instance, I2Cisr * const *masterState,
                    byte *buffer, unsigned ndat, byte cmd)
/*
  enter specified master write interrupt processing state
  issue cmd to start the transaction
  wait for transaction to complete
  returns masterResult  
*/
{
  byte result = startMaster (instance, ndat, cmd);
  if (!result) {
    cursor = buffer;
    I2Cstate = masterState;
    I2CIE = masterIE;
    result = masterEnd(instance);
  }
  return result;
}


static inline
  byte masterCRCwrite (I2CnodeInstance *instance, I2Cisr * const *masterState,
                       byte *buffer, unsigned ndat, byte cmd, unsigned crcSeed)
/*
  enter specified master write interrupt processing state
  issue cmd to start the transaction
  wait for transaction to complete
  returns masterResult  
*/
{
  byte result = startMaster (instance, ndat, cmd);
  if (!result) {
    cursor = buffer;
    I2Cstate = masterState;
    I2CIE = masterIE;
    CRCvalue = crcSeed;
    result = masterEnd(instance);
  }
  return result;
}


static inline
  byte masterRead (I2CnodeInstance *instance, I2Cisr * const *masterState,
                   byte *buffer, unsigned ndat, byte cmd)
/*
  enter specified master read interrupt processing state
  issue cmd to start the transaction
  wait for transaction to complete
  returns masterResult
  
*/
{
  byte result = startMaster (instance, ndat, cmd);
  if (!result) {
    nineMoreSCLs(); nineMoreSCLs();  //account 16-bit wide I2CDR input buffer
    cursor = buffer;
    I2Cstate = masterState;
    I2CIE = masterIE;
    result = masterEnd(instance);
  }
  return result;
}


static 
  byte masterNullWrite (I2CnodeInstance *instance, I2Cisr * const *masterState)
/*
  enter specified master null message interrupt processing state
  issue cmd to start the transaction
  wait for transaction to complete
  returns masterResult
*/
{
  byte result;
  result = startMaster (instance, 0, I2Ccmd|I2CTRX|I2CSTT);
  set8 (DWRFI2COUT, DWRFI2CINC); 
  if (!result) {
    I2Cstate = masterState;
    I2CIE = masterIE & ~TXRDYIE;
    enableDWRFirqs (DWRFI2CHELD);
    I2CDRB = 0;  //throw the xmitter a fish just to clear the interrupt
    result = masterEnd(instance);
  }
  return result;
}


//one-time init of master retry mechanism
static void addressSlave (I2CnodeInstance *instance, unsigned slaveAdr)
{
  I2CSA = slaveAdr;
  retriesRemaining = instance->retries & 0x7f;
  restartArbTimer(instance);
}


//restart normal slave mode operation after mastering the bus
static void restartSlave (I2CnodeInstance *instance, I2Cisr *reset)
{
  if (U0CTL & MST)
    stopI2C (instance, reset);  //give bus up and reset controller
  else if (!I2CIE)
    initialI2Creset(instance);  //we've missed events, so just reset
  else {  //most likely timed out trying to start the last command
    _DINT();  //reset only if stuck in idle state
    if (I2Cstate == idleI2Cstate) reset(instance);
    _EINT();
  }
}


//prepare to retry failed operation
static void prepNextTry (I2CnodeInstance *instance)
{
  --retriesRemaining;
  if (U0CTL & MST)        //if still mastering the bus...
    restartI2C(instance); //restart, but keep bus between retries
  else if (!I2CIE)
    initialI2Creset(instance);   //reset controller if we timed out
  restartArbTimer(instance);
}


//process a message Errors in master mode (in masterResult)
//returns I2CnodeRetryErr if message should be retried
static I2CnodeErr msgError (I2CnodeInstance *instance)
{
  I2CnodeErr errCode = I2CnodeOK;
  byte errs = masterResult;
  if (errs != ARDYIE) { //interpret the mask in masterResult to the host
    if (errs & ALIE) {
      if (!(errs & STTIE))
        return I2CnodeRetryErr;  //keep trying to arbitrate for the bus
      errs = 0;  //handle arbitration timeout like a bus timeout
    }
    if (!errs) {
      if (retriesRemaining > 2) 
        retriesRemaining -= 2;  //keep total timeout time in check
    }
    if (retriesRemaining) {  //do we have retries remaining?
      prepNextTry(instance);
      return I2CnodeRetryErr;
    }
    {  //if no more retries, return the appropriate errCode
      I2Cisr *reset;
      if (errs & NACKIE) {
        if (errs == (ARDYIE|NACKIE)) { //CRC NACK on otherwise OK message
          errCode = I2CnodeCRCNACK;
          reset = resetI2C;
        }else{
          errCode = I2CnodeNACK;
          reset = resetI2CafterNACK;
        }
      }else{
        errCode = I2CnodeTimeOut;
        if (errs) errCode = I2CnodeBusErr; 
        reset = initialI2Creset;
      }
      restartSlave(instance, reset);
    }
  } 
  return errCode;
}


//Read length bytes from I2Csrc address into buffer
I2CnodeErr I2CnodeRead(I2CnodeInstance *instance, uint16 I2Csrc, 
                            byte *buffer, unsigned length)
{
  I2CnodeErr errCode;
  if (I2Csrc-1 > 0x7e || I2Csrc == I2COA || length > 255)
    return I2CnodeArgErr;

  if (!length) {  //convert null reads to 1 byte read to a hidden buffer
    length = 1;
    buffer = &CRCvalue;
  }
  addressSlave(instance, I2Csrc);
  do {
    masterRead (instance, readI2Cstate, buffer, length, I2Ccmd|I2CSTT|I2CSTP);
    errCode = msgError (instance);    
  } while (errCode == I2CnodeRetryErr);
  return errCode;
}


//master receive the next byte of slave's response
static void RXRDYread(I2CnodeInstance *instance)
{
  I2CDRB = *cursor, cursor++;
  if (--byteCount) { 
    creditDRBread();
  }else{
    releaseSCL();
    clear8 (I2CIE, RXRDYIE);
  }
}


//Write length byte message in buffer to I2Cdst
static inline
  I2CnodeErr I2CnodeNonCRCwrite(I2CnodeInstance *instance, uint16 I2Cdst, 
                                      byte *buffer, unsigned length)
{
  I2CnodeErr errCode;
  addressSlave (instance, I2Cdst);
  if (length) {
    do {
      masterWrite (instance, writeI2Cstate, buffer, length, 
                     I2Ccmd|I2CTRX|I2CSTT|I2CSTP);
      errCode = msgError(instance);
    } while (errCode == I2CnodeRetryErr);
    I2CDRbytes = length - I2CDRbytes;
  }else  //write null message
    do {
      masterNullWrite (instance, writeNullI2Cstate);
      errCode = msgError(instance);
    } while (errCode == I2CnodeRetryErr);
  return errCode;
}


//Write length byte message in buffer to I2Cdst
//if noI2CnodeCRC of I2Cdst is set, the message is written without CRC checking
I2CnodeErr I2CnodeWrite(I2CnodeInstance *instance, uint16 I2Cdst, 
                              byte *buffer, unsigned length)
{
  I2CnodeErr errCode;
  if (I2Cdst & 0x7f == I2COA || I2Cdst & ~(0x7f|noI2CnodeCRC) || length >= 255)
    return I2CnodeArgErr;
  if (I2Cdst & noI2CnodeCRC)
    return I2CnodeNonCRCwrite (instance, I2Cdst & 0x7f, buffer, length);
  length++;  //account for CRC byte at the end of the message
  {
    I2Cisr * const * writeState = crcWriteI2Cstate;
    byte wrtCmd = I2Ccmd | I2CTRX | I2CSTT;
    unsigned CRCseed;
    if (!I2Cdst) {
      set8 (wrtCmd, I2CSTP);  //cannot verify broadcast msgs
      writeState = crcGCwriteI2Cstate;
    }
    CRC8init (CRCseed); CRC8next (CRCseed, I2Cdst);
    addressSlave(instance, I2Cdst);
    do {
      byte errs = masterCRCwrite (instance, writeState,
                                    buffer, length, wrtCmd, CRCseed);
      if (errs == ARDYIE) return I2CnodeOK;
      errCode = msgError (instance) ;
      if (errCode != I2CnodeRetryErr) break;
      if (errs==(ARDYIE|NACKIE) && (quickCRC(instance) || CRCvalue==CRCflow)) {
        // A CRCflow msg ACK byte from the slave signals buffer overflow
        // (when using one bit msg ACKs, treat any CRC err as flow control)
        // Delay after overflow error to give the slave a chance to send,
        //   -- so that it can receive OUR msg when retried!
        if (U0CTL & MST) stopI2C(instance, restartI2C);
        _DINT();
        arbTimer = FLOWDELAY;
        while (arbTimer) AWAITEV(),_DINT();
        startArbTimer(instance);
        _EINT();
      }
    } while (true);
  }
  I2CDRbytes = length - I2CDRbytes;
  return errCode;
}


//check message ACK/NACK (via configured method) after CRC checked write
static void ARDYwriteCRC(I2CnodeInstance *instance)
{
  I2CTCTL = I2Ccmd;
  if (quickCRC(instance)) {  //non-standard single bit message ACK/NACK  
    //initially, we are driving SCL low and SDA is floating high
    cancelI2C();            //disable the I2C controller (floats SCL)
    holdSCL();
    enableDWRFirqs (DWRFI2CHELD);
    set8 (P3DIR, I2Cdata);
    clear8 (P3SEL, I2Cdata); //drive SDA low to signal REPEATED START to slave
  }else{ //immediate 1 byte read from dst to find out if message was OK
    I2CNDAT = 1;
    I2Cstate = readAckI2Cstate;
    I2CTCTL = I2Ccmd | I2CSTT | I2CSTP;
  }
}  


//missing CRC ACK from slave -- but message may have been received intact
//just retry the CRC query to avoid slave getting a duplicate message
static void NACKCRC(I2CnodeInstance *instance)
{
  if (retriesRemaining) {
    --retriesRemaining;
    cancelI2C();
    resumeI2C();
    I2CTCTL = I2Ccmd | I2CSTT | I2CSTP;
  }else
    NACKadr (instance);
}


//end master's CRC checked write message processing
static void endCRCmsg (I2CnodeInstance *instance, byte CRCerr)
{
  set8 (masterResult, ARDYIE);
  if (CRCerr) {
//clear8(P5OUT, 0x40);
//set8(P5OUT, 0x40);
    set8 (masterResult, NACKIE);  //indicate NACK'd CRC to mainline
  }
  enterIdle();
}


//CRC ack byte recevied
static void RXRDYreadAck(I2CnodeInstance *instance)
{
  CRCvalue = I2CDRB;
}  

//explicit 1 byte read completed after CRC checked write
static void ARDYreadAck(I2CnodeInstance *instance)
{
  I2CTCTL = I2Ccmd;
  endCRCmsg(instance, CRCvalue ^ CRCACK);
}  


//master transmit the next byte of a CRC checked write msg
static void TXRDYwriteCRC (I2CnodeInstance *instance)
{
  if (--byteCount) {
    unsigned next = *cursor;
    I2CDRB = next;
    creditDRBwrite();
    CRC8next (CRCvalue, next);
    cursor++;