/* MegaView VFD Display Program for MS-II  */
/*
     v1.0 - initial operational version 
     v1.1 - fixed lockup problem when incr/ decr inputs
     v2.0 - updated code to handle MSII CAN, v2.3 and up
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "defs.h"
#include "Gp32.h"

#pragma DATA_SEG _DATA_ZEROPAGE
#define DISPLAY 0
#define CONTROL 1
#define NO_DISP_BYTES_FROM_ECU 112
/* following not true no. - just need this many inputs */
#define NO_CTRL_BYTES_FROM_ECU 914
#define NUM_CTL_VARIABLES 19
#define CHR 0
#define UCH 1
#define SHT 2
#define USH 3
#define ULG 4
#define FLT 5
#define MAX_COLS 20
#define MAX_VFD_STRLEN strlen(DispVarUpperStrng)

/*============================ PORT VARIABLES ================================*/

/* The Address of all Ports Data Registers */
volatile U8* PortDataReg[] = { pPTA, pPTB, pPTC, pPTD, pPTE };

/* The Address of all Ports Data Registers */
volatile U8* PortDirecReg[] = { pDDRA, pDDRB, pDDRC, pDDRD, pDDRE };
/******************************************************************************
 *  Macro       : PortRead
 *  Description : Reads a data bit from a port.
 *                
 *  Input Parameters: U8 PortId
 *                               port identifier : PORT_A,PORT_B,PORT_C,PORT_D
 *                                                 PORT_E,PORT_F,PORT_G,PORT_H
 *                    U8 Bit
 *                      The Bit of the port to read from
 *
 *  Return Value    : U8 Value
 *                               The value of the port bit
 *
 ******************************************************************************/
#define  PortRead(PortId, Bit)  (((*PortDataReg[PortId]) & (0x1 << Bit)) >> Bit)

/******************************************************************************
 *  Macro       : PortSetDir
 *  Description : Configures all the Port's Direction pins as input or output pins.
 *                
 *  Input Parameters :
 *                     1. U8 PortId Port Identifier:
 *                          PORT_A,PORT_B,PORT_C,PORT_D
 *                          PORT_E,PORT_F,POR_G,PORT_H
 *                     2. U8 IoMode:
 *                          The mode (INPUT/OUTPUT) of the port
 *
 *  Return Value     : None 
 *
 *  Example     : Port a configure as an input:
 *                    PortSetDir(PORT_A,INPUT);
 *                Port a configure as an output:
 *                    PortSetDir(PORT_A,OUTPUT_PORT);
 *                
 ******************************************************************************/
#define PortSetDir(PortId,IoMode)   (*PortDirecReg[PortId]) = IoMode

/******************************************************************************
 *  Macro    : PortWrite
 *  Description : Write a data bit to a port.
 *                
 * Input Parameters: 1. U8 PortId Port Identifier:
 *                      PORT_A,PORT_B,PORT_C,PORT_D
 *                      PORT_E,PORT_F,PORT_G,PORT_H
 *                   2. U8 Bit
 *                      The bit # of the port to write to
 *
 ******************************************************************************/
#define PortSet(PortId, Bit)        (*PortDataReg[PortId]) |= (0x1 << Bit)
#define PortClr(PortId, Bit)        (*PortDataReg[PortId]) &= ~(0x1 << Bit)

U8 select,freeze,mode,modeChange;
U8 ibyte,no_xbytes,xmtbuf[10],rcvbuf[NO_DISP_BYTES_FROM_ECU + 2];
U16 irbyte,no_rbytes,rbyteoffset;
U8 controlRcv,curr_var_no;
U8 irpm,ikpa,VFDDisp_flg;
const U8 *DispVarUpperStrng; 
// display variables
U8 alarm_alt;
short colix;
union all_types  {
  char chr;
  U8   uch;
  U16  ush;
  short sht;
  float flt;
} control_var_val;
U8 ctl_var_ecu_val, ctl_var_ecu_val2;
U8 debounce,TmpUnits,get_config;
short changed_ctl_var;
U8 incr_count,decr_count;
U8 dim,rcv_timeout,sem,CnvtSw;

#pragma DATA_SEG DEFAULT

#include "table.h"
#include "asc_table.h"

const U8 FDispVarUpperStrng[] = { 
" SECS   ENGINE  WRMENRCH ACCENRCH   RPM  PW1(MS) ADV(DG) COIL(MS) IACPOS\
 BARO(KPA) MAP(KPA) CLT(F) MAT(F) TPS(V) BAT(V)  AFR  AFRTGT\
 EGOCOR(%) AIRCOR(%)\
 BARCOR(%)  VECOR(%) WRMCOR(%) ACCOR(MS) DECEL(%) GAMMA(%) CLDADV(DG)"
 };
const U8 CDispVarUpperStrng[] = { 
" SECS   ENGINE  WRMENRCH ACCENRCH   RPM  PW1(MS) ADV(DG) COIL(MS) IACPOS\
 BARO(KPA) MAP(KPA) CLT(C) MAT(C) TPS(V) BAT(V)  AFR  AFRTGT\
 EGOCOR(%) AIRCOR(%)\
 BARCOR(%)  VECOR(%) WRMCOR(%) ACCOR(MS) DECEL(%) GAMMA(%) CLDADV(DG)"
};
const U8 DispVarLowerStrng[] = {
"                                                                        \
                                                            \
                    \
                                                                     "
};

const U8 SelectStrng[MAX_COLS + 1] = {"     Selected:      "};
const U8 BlnkStrng[MAX_COLS + 1] =   {"                    "};
const U8 CtlVarStrng[NUM_CTL_VARIABLES][MAX_COLS + 2] = {
"CRNK ENRCH_L=%4.1f ms",
"CRNK ENRCH_H=%4.1f ms",
" AFT WRM PCT_L=%3d   ",
" AFT WRM PCT_H=%3d   ",
" AFT WRM EVTS_L=%3d  ",
" AFT WRM EVTS_H=%3d  ",
"   IACSTART=%3d      ",
"PRIME PLSE_L=%4.1f ms",
"PRIME PLSE_H=%4.1f ms",
"   REQFUEL=%4.1f ms  ",
"   DIVIDER=%3u       ",
"   ALTERNATE=%3u     ",
"   INJOPEN=%4.1f ms  ",
"  BATTFAC=%4.1f ms   ",
"  INJPWMT=%4.1f ms   ",
"  INJPWMPD=%5.3f ms  ",
"   INJPWDTY=%3u      ",
" ADV OFFSET=%4.1f deg",
"   AMC Option=%2u    ",
};
const U16 ctl_var_offset[NUM_CTL_VARIABLES] = {
   570, 572, 574, 576, 578, 580, 552, 566, 568,
   608, 610, 611, 612, 616, 613, 614, 615, 42, 628
};
const U8 ctl_var_type[NUM_CTL_VARIABLES] = {
  FLT,FLT,SHT,SHT,SHT,SHT, SHT,FLT,FLT,
  FLT,UCH,UCH,FLT, FLT,FLT,FLT, UCH,FLT, UCH
};
const U8 ctl_var_sze[NUM_CTL_VARIABLES] = {
  2,2,2,2,2,2,2,2,2,2, 1,1,1,1,1,1,1, 2, 1
};
const short diva[5] = {1,10,100,1000,10000};

U8 VFDLower[sizeof(DispVarLowerStrng)];

// Prototypes
void SystemInit(void);
void SciInit(void);
interrupt void SciTxIntHand(void);
interrupt void SciRxIntHand(void);
void PortAInit(void);
void PortBInit(void);
void PortCInit(void);
void PortDInit(void);
void KbiInit(void);
interrupt void KbiHand(void);
void ModeIsr(void);
void ModeChange(void);
void SelectIsr(void);
void IncrIsr(void);
void DecrIsr(void);
interrupt void GetDispIsr(void);
void VFDInit(void);
void VFDDisplay(void);
void VFDConvert(void);
void itoa(short value, U8 *bufi, U8 width, U8 prec);
void VFDsprintf(U8 *bufo, U8 type, U8 neg, U8 width, U8 *bufi, U8 alarm);
void VFDSend(char line, char col, char value);
void VFDData(char value);

void main(void) {
U8 ix,jx,kx;
    // Configure system and set up clock
	SystemInit();

    // Set up rs232
	SciInit();

    // Initialize variables
	modeChange = 0;
    select = 0;
    freeze = 0;
    colix = 0;
    DispVarUpperStrng = FDispVarUpperStrng;
	strcpy(VFDLower, DispVarLowerStrng);
	VFDDisp_flg = 0;
	debounce = 0;
	alarm_alt = 0;
	rcv_timeout = 0;
	sem = 0;
	
	// Set up VFD control line I/O
	PortBInit();
	PortCInit();
	PortDInit();

	// Delay while power stabilizes, allow MS and VFD to come up
	for(ix=0;ix<5;ix++)  {
		for(jx=0;jx<200;jx++)  {
			for(kx=0;kx<200;kx++)  {
				asm nop;
			}
		}
	}
	
    // Enable CPU Global Interrupts
    asm CLI;

    // Set up VFD
    dim = PortRead(PORT_B,0);
	VFDInit();

    // Set up buttons (Inputs - enable pullups)
	PortAInit();
	KbiInit();    // enable interrupts
	
    mode = CONTROL;
    // Set up and turn on 4 Hz interrupt timer (Timebase Module)
	TBCR = 0x10;     // set 4 Hz
	TBCR |= 0x06;    // turn on and enable interrupt

	// Get config data from ECU
	get_config = 1;
	curr_var_no = 0;
	incr_count = 0;
	decr_count = 0;
    ibyte = 0;
    irbyte = 0;
    xmtbuf[0] = 'r';
    xmtbuf[1] = 0;   // canID
    xmtbuf[2] = 4;   // tbleID for inpram
    // 0 offset from inpram
    rbyteoffset = 0;
    xmtbuf[3] = 0;
    xmtbuf[4] = 0;
    // no ctl bytes (<=sizeof inpram)
    no_rbytes = NO_CTRL_BYTES_FROM_ECU;
    xmtbuf[5] = (U8)(no_rbytes >> 8);  // msb
    xmtbuf[6] = (U8)no_rbytes;         // lsb
    no_xbytes = 7;
    SCC2 |= SCI_Tx_INT_ENABLE;  // enable xmit empty interrupt
	ix = SCS1;
	SCDR = xmtbuf[0];
	while(get_config);
    mode = DISPLAY;
    
    // idle
    while(1)  {
    	if(VFDDisp_flg)  {
    		VFDDisplay();
    		VFDDisp_flg = 0;
    	}
    	if(mode == CONTROL)  {       // incr/scroll button auto repeat
    		if(incr_count >= 2)  {
    			incr_count = 1;
    			if(!select)  {
    				debounce = 0;
    				sem = 1;
    				IncrIsr();
    				sem = 0;
    			}
    		}
    		if(decr_count >= 2)  {
    			decr_count = 1;
    			if(!select)  {
    				debounce = 0;
    				sem = 1;
    				DecrIsr();
    				sem = 0;
    			}
    		}
    	}
    }      // end while(1)
}

interrupt void SwiHand(void)
{
}
interrupt void NullHand(void)
{
}

/*================================== VARIABLES ================================*/
/* gPllLockState -  Global Var that hold the PLL lock state PLL_LOCKED or PLL_UNLOCKED; */
static char gPllLockState = PLL_UNLOCKED;

/******************************************************************************
 *  FUNCTION    : SystemInit
 *     System Initialization
 ******************************************************************************/
void SystemInit(void) 
{ 
#define  DELAY_TIME 10  
  U8 i;
  /* Set the CONFIG1 Option Register */
  CONFIG1 = ( COP_TIMOUT_LONG |  /* Set the COP rate */ 
              LVI_DISABLE_IN_STOP |  /* Set the LVI mode during stop mode */           
              LVI_RESET_DISABLE |  /* (MOR=CONFIG1) Set the LVI Reset Enable Bit */
              LVI_POWERD_DISABLE |  /* (MOR=CONFIG1) Set the LVI Power Enable Bit */
              LVI_IN_5_V |  /* Set the LVI to 5v/3v operating voltage */
              SLOW_RECOVERY_FROM_STOP |  /* Set fast/slow recover time from stop mode */
              MOR_STOP_ENABLE |  /* (MOR=CONFIG1) Set the stop mode */ 
              DISABLE_COP_MODULE );  /* (MOR=CONFIG1) Set the COP module */

  /* Set oscillator mode during stop mode */
  CONFIG2 = ( OSCI_IN_STOP_DISABLE |
              SCI_INTERNAL_BUS_CLOCK );  /* set the baud rate source for the sci */ 


  /* Initialize the IRQ Status and Control Register */
  ISCR = ( IRQ1_ENABLE |          /* Enable/disable IRQ */
           IRQ_FALLING_EDGE_AND_LEVEL             /* IRQ signal Sensitivity */
         );
  /* Init the PLL Bandwidth Control Register */
  PBWC = ( PLL_AUTOMATIC_CONTROL  /* Set The AUTO - Automatic Bandwidth Control Bit */
          );

  /* 
    The PLL control register (PCTL) contains the interrupt enable and flag
    bits, the on/off switch, and the base clock selector bit.
  */  
  PCTL &= ~PLL_ON;  /* Turn off to set up bus freq = 7.3728 MHz */
  PCTL |= 0x2;
  PMSH =0x3;
  PMSL = 0x84;
  PMRS = 0xC0;
  PMDS = 0x1;
  PCTL = ( PLL_INTERRUPT_DISABLED |  /* Set PLLIE - PLL Interrupt Enable Bit */
           PLL_ON );  /* Set PLLON - PLL On/Off Bit */ 
  
  /* 
     After toggling BCS, it may take up to three CGMXCLK and three
     CGMVCLK cycles to complete the transition from one source clock to
     the other.
  */  
  for ( i = 0 ; i < DELAY_TIME ; i++ );  /* Delay */
  PCTL |= PLL_VCO_ENABLE ;  /* Set BCS - Base Clock Select Bit */
  for ( i = 0 ; i < DELAY_TIME ; i++ );  /* Delay */

  /* Break Module */
  
  /* Init the SIM Break Flag Control Register */
  SBFCR = STATUS_BITS_NOT_CLEARABLE;
  return;
}
/******************************************************************************
 *  FUNCTION    : SciInit
 *     Initial for the Sci module
 ******************************************************************************/
void SciInit(void)
{
 
 CONFIG2 |= SCI_INTERNAL_BUS_CLOCK; /* set the clock source for the baud rate */
 SCC1 = ENABLE_SCI;                /* Enable the sci module */
 
 SCC1 |= ( SCI_LOOP_DISABLE           |  /* Enable/disable loop mode */ 
          SCI_TRANSMIT_UNINVERTED  |  /* Transmit character inverted/not inverted */
          SCI_8_BIT_DATA         |  /* Set character length to 8/9 bits */  
          SCI_IDLE_WAKEUP       |  /* Wakeup by address mark/idle line */  
          SCI_IDLE_AFTER_START      |  /* Begin idle character bit count after stop/start bit */   
          SCI_DISABLE_PARITY       |  /* Enable/disable parity function */  
          SCI_ODD_PARITY     );  /* Select odd/even as parity */ 
          
          
  /* This register initialize interrupts request
      activate the transmitter and receiver and wakeup mode */         
 SCC2 = ( SCI_Tx_INT_DISABLE          |  /* Enable/disable transmitter empty interrupt */
          SCI_INT_TRAN_COMP_DISABLE  |  /* Enable/disable transmitter complete interrupt */          
          SCI_Rx_INT_DISABLE          |  /* Enable/disable receiver full interrupt */  
          SCI_INT_IDLE_DISABLE     |  /* Enable/disable receiver idle interrupt */           
          SCI_TRANSMITTER_ENABLE            |  /* Enable/disable transmitter */              
          SCI_RECEIVER_ENABLE             |  /* Enable/disable receiver */
          SCI_RWU_NORMAL                 );  /* Enable/disable wakeup mode */
          
  /* This register initialize the DMA services and error interrupts */       
 SCC3 = ( SCI_DMA_FOR_REC_DISABLE  |  /* Enable/disable receive DMA service */ 
          SCI_DMA_FOR_TRAN_DISABLE |  /* Enable/disable transfer DMA service */
          SCI_INT_REC_OVERUN_DISABLE |  /* Enable/disable receiver overrun interrupt */           
          SCI_INT_REC_NOISE_DISABLE   |  /* Enable/disable receiver noise error interrupt */
          SCI_INT_REC_FRAME_DISABLE   |  /* Enable/disable receiver frame error interrupt */
          SCI_INT_REC_PARITY_DISABLE   );  /* Enable/disable receiver parity error interrupt */          
          
  /* This register sets baud rate to 115200 at bus freq = 7.3728 MHz */       
// SCBR = ( SCI_PD_DIV_BY_3 | SCI_BD_DIV_BY_4 );        
   SCBR = 0x00;
   
}

/******************************************************************************
 *  FUNCTION    : SciTxIntHand
 *     Transmitter interrupt handler,  cpu will get here when TEIE bit set in 
 *                SCC2
 ******************************************************************************/
interrupt void SciTxIntHand(void)
{

  // Enter here when the rs232 transmit buffer is empty
  U8 Status ;
  U16 ix;

  ibyte++;
  if(ibyte < no_xbytes)  {
    if(no_xbytes > 2)for(ix=0;ix < 4000;ix++);
    // clear status register (by read) to allow next interrupt
    Status=SCS1;
  	// transfer transmit byte from buffer to register
  	SCDR = xmtbuf[ibyte];
  }
  else  {
    // Done transmitting - kill transmit interrupts
    SCC2 &= (~SCI_Tx_INT_ENABLE);
    SCC2 &= (~SCI_INT_TRAN_COMP_ENABLE);
    if(mode == DISPLAY)  {    // Display mode
      // Set up to receive the requested display data
      irbyte = 0;
      SCC2 |= SCI_Rx_INT_ENABLE;   // enable receiver full interrupt
    }
    else if(xmtbuf[0] != 'b')  {       // Control mode
      // Set up to receive the requested control data
      irbyte = 0;
      rcv_timeout = 1;
      SCC2 |= SCI_Rx_INT_ENABLE;   // enable receiver full interrupt
    }
  }

  return;
}

/******************************************************************************
 *  FUNCTION    : SciRxIntHand
 *     Receiver interrupt handler
 ******************************************************************************/
interrupt void SciRxIntHand(void)
{
  U8 Status,readbuf;
      
  // Enter here when have received rs232 byte
  // clear status register to allow next interrupt
  Status = SCS1;
  // transfer received byte from register to buffer
  readbuf = SCDR;
  if(get_config)  {
	if(irbyte == 599)
  	TmpUnits = readbuf;
  	if(TmpUnits == 1)
  		DispVarUpperStrng = CDispVarUpperStrng;
  }
  else if(mode == DISPLAY)
  	rcvbuf[irbyte] = readbuf;
  else  {
  	if (irbyte + rbyteoffset == ctl_var_offset[curr_var_no])
  		rcvbuf[0] = readbuf;
  	if((irbyte + rbyteoffset == ctl_var_offset[curr_var_no] + 1) && (ctl_var_sze[curr_var_no] == 2))
  			rcvbuf[1] = readbuf;    // 2nd half (lsb) of shorts
  }
  irbyte++;
  if(irbyte < no_rbytes)  {
  	return;
  }
  else  {
    // Done receiving - kill receive interrupt enable
    SCC2 &= (~SCI_Rx_INT_ENABLE);
    rcv_timeout = 0;
    if(get_config)  {
    	get_config = 0;
    	return;
    }
    if(mode == CONTROL)
	  controlRcv = 1;

    // Check if mode change desired. If so, can change mode now
    //   without getting ecu comms out of synch
    if(modeChange)  {
      ModeChange();
    }
    else  {
      // convert data to engineering units, put in VFD
      //   display string, and write/ scroll across VFD
      VFDDisp_flg = 1;
    }
  }
  return;
}

/******************************************************************************
 *  FUNCTION    : PortAInit
 *     Port A Initialization
 ******************************************************************************/
void PortAInit(void)
{

    /* Set global Var with Registers Address */
    PortDirecReg[PORT_A] = pDDRA ;
    PortDataReg[PORT_A] = pPTA ;

    /* Set all Data Pin to 0 */
    PTA = 0;

    /* Set Data direction Port to input for bits 0-3 */
    DDRA = 0xF0;

    /* Set Pullup Enables  for bits 0 - 3 (buttons) 
        Bit 0    Scroll Left/ Decrease
        Bit 1    Scroll Right/ Increase
        Bit 2    Select/ Freeze
        Bit 3    Mode Button            */
    PTAPUE = 0x0F;

}

/******************************************************************************
 *  FUNCTION    : PortBInit
 *     Port B Initialization
 ******************************************************************************/
void PortBInit(void)
{

    /* Set global Var with Registers Address */
    PortDirecReg[PORT_B] = pDDRB ;
    PortDataReg[PORT_B] = pPTB ;

    /* Set all Data Pin to 0 */
    PTB = 0;

    /* Set Data direction Port to: 
           input for bit 0; outputs for bits 4-6
        VFD Control lines:    
        Bit 0    Dimming
        Bit 4    Enable
        Bit 5    R/W
        Bit 6    RS           */
    DDRB = 0xFE;

}

/******************************************************************************
 *  FUNCTION    : PortCInit
 *     Port C Initialization
 ******************************************************************************/
void PortCInit(void)
{

    /* Set global Var with Registers Address */
    PortDirecReg[PORT_C] = pDDRC ;
    PortDataReg[PORT_C] = pPTC ;

    /* Set all Data Pin to 0 */
    PTC = 0;

    /* Set Data direction Port to ouputs for bits 0-3
        VFD data bits:
        Bit 0    DB4
        Bit 1    DB5
        Bit 2    DB6
        Bit 3    DB7            */
    DDRC = 0xFF;

}

/******************************************************************************
 *  FUNCTION    : PortDInit
 *     Port D Initialization
 ******************************************************************************/
void PortDInit(void)
{

    /* Set global Var with Registers Address */
    PortDirecReg[PORT_D] = pDDRD ;
    PortDataReg[PORT_D] = pPTD ;

    /* Set all Data Pin to 0 */
    PTD = 0;

    /* Set Data direction Port to output for bit 4 */
    DDRD = 0xFF;     // LCD contrast

}

/******************************************************************************
 *  FUNCTION    : KbiInit
 *     Kbi initialization
 ******************************************************************************/
#define KbiWriteAcknowledge()  INTKBSCR |= ( KBI_ACKNOWLEDGE )

void KbiInit(void)
{

 /* Initialize status and control register */
 INTKBSCR = ( DISABLE_KBI_INT |   /* Set the musk interrupt bit */
                  KBI_INT_ON_FALLING);  /* Set the sensitivity of the keyboard */

 /* Initialize the keyboard interrupt register */
 INTKBIER = ( ENABLE_KBI_INT_0 |  /* Enable/Disable keyboard interrupt 0 */                            
                 ENABLE_KBI_INT_1 |  /* Enable/Disable keyboard interrupt 1 */
                 ENABLE_KBI_INT_2 |  /* Enable/Disable keyboard interrupt 2 */
                 ENABLE_KBI_INT_3 |  /* Enable/Disable keyboard interrupt 3 */
                 DISABLE_KBI_INT_4 |  /* Enable/Disable keyboard interrupt 4 */
                 DISABLE_KBI_INT_5 |  /* Enable/Disable keyboard interrupt 5 */
                 DISABLE_KBI_INT_6 |  /* Enable/Disable keyboard interrupt 6 */
                 DISABLE_KBI_INT_7 );  /* Enable/Disable keyboard interrupt 7 */
              
 KbiWriteAcknowledge();              

  /* Enable the keyboard interrupts */
  INTKBSCR &= ~( DISABLE_KBI_INT );   /* clear the musk interrupt bit */  
  return;
 
}

/******************************************************************************
 *  FUNCTION    : KbiHand
 *     KBI interrupt Handler
 ******************************************************************************/
interrupt void KbiHand(void)
{
U8 Status;
  if(!debounce)  {
  	debounce = 2;
  	/* Determine which button pushed */
  	if(PortRead(PORT_A,3) == 0)
		ModeIsr();
 	else if(PortRead(PORT_A,2) == 0)
		SelectIsr();
  	else if(PortRead(PORT_A,1) == 0)  {
		if(!sem)IncrIsr();
  	}
  	else if(PortRead(PORT_A,0) == 0)  {
		if(!sem)DecrIsr();
  	}
  }
  else
  	Status = PortRead(PORT_A,0);   // read to clear

  /* Write an Acknowledge to clear the interrupt flag */
  KbiWriteAcknowledge();
  return;
  
}

void ModeIsr(void)  {
  // Enter here when push Mode button
        if(mode == DISPLAY)  {
            // Set flag so when current rs232 rcv transaction done, will 
            //   call ModeChange
            modeChange = 1;
        }
        else  {
			if(!controlRcv)  {   // havn't got ctl data yet from ecu
                	// Set flag so when current rs232 rcv transaction done, will 
                	//   call ModeChange
                	modeChange = 1;
			}
			else  {
                	ModeChange();
			}
        }
        return;
}

void ModeChange(void)  {
U8 Status;
        modeChange = 0;
        if(mode == DISPLAY)  {  // Switch to Control Mode
                mode = CONTROL;
                select = 0;
                controlRcv = 0;  // havn't got ctl data yet from ecu

                // Transmit request for all control variable values
                ibyte = 0;
                xmtbuf[0] = 'r';
                xmtbuf[1] = 0;   // canID
                xmtbuf[2] = 4;   // tbleID for inpram
                // 0 offset from inpram
                rbyteoffset = 0;
                xmtbuf[3] = 0;
                xmtbuf[4] = 0;
                // no ctl bytes (<=sizeof inpram)
                no_rbytes = NO_CTRL_BYTES_FROM_ECU;
                xmtbuf[5] = (U8)(no_rbytes >> 8);  // msb
                xmtbuf[6] = (U8)no_rbytes;         // lsb
                no_xbytes = 7;
                SCC2 |= SCI_Tx_INT_ENABLE;  // enable xmt empty interrupt
	    	    Status = SCS1;
	    	    SCDR = xmtbuf[0];
        }
        else  {                // Switch to Display Mode

                mode = DISPLAY;
                if(!freeze)
                	colix = 0;
                strcpy(VFDLower, DispVarLowerStrng);
                VFDDisp_flg = 0;
        }
        
        return;
}

void SelectIsr(void)  {
char col;
U8 Status;
//  Enter here when push freeze/select button'
        if(mode == DISPLAY)  {
            freeze = 1 - freeze;
        }
        else  {      // Control Mode
            select = 1 - select;
            if(select)  {
              changed_ctl_var = 0;
              // Indicate Control is Selected, so
              //  incr/decr will change value.
              // send out top line
              for(col=0; col < MAX_COLS; col++)  {
                VFDSend(0,col,SelectStrng[col]);
              }
            }  
            else  {
              // Have just unselected - check if have made a net change
              if(changed_ctl_var)  {
              	// Burn change into inpflash
                ibyte = 0;
                xmtbuf[0] = 'b';
                xmtbuf[1] = 0;   // canID
                xmtbuf[2] = 4;   // tbleID for inpram
                no_xbytes = 3;
                no_rbytes = 0;
                rbyteoffset = 0;
                SCC2 |= SCI_Tx_INT_ENABLE;  // enable xmt empty interrupt
                Status = SCS1;
                SCDR = xmtbuf[0];
              }
              // Indicate Control is Not Selected, so 
              //  incr/decr will cause scrolling
              // send out top line
              for(col=0; col < MAX_COLS; col++)  {
                VFDSend(0,col,BlnkStrng[col]);
              }
            }
        }
        return;
}

void IncrIsr(void)  {
//  Enter here when push incr or scroll right button
short test;
float testf;
U8 Status;

        if(mode == DISPLAY)return;
        if(select && !rcv_timeout)  {   // control variable has been selected,
                        // therefore want to increase value
      		changed_ctl_var++;
      		if(ctl_var_type[curr_var_no] == UCH)  {
                test = control_var_val.uch + 1;
                if((curr_var_no == 11) && (test > 1))test = 0;	 // alternate
                if((curr_var_no == 16) && (test > 100))test = 100;  // InjPWMDty
                if(test < 256)
                	control_var_val.uch = (U8)test;
      		}
      		else if(ctl_var_type[curr_var_no] == SHT)  {
                test = control_var_val.sht + 1;
                control_var_val.sht = test;
      		}
      		else if(ctl_var_type[curr_var_no] == FLT)  {
                if(curr_var_no == 15)  {                     // InjPWMPd
                	testf = control_var_val.flt + 0.01;
                	if(testf > 254.0)testf = 254.0;
                }
                else
                	testf = control_var_val.flt + 0.1;
                control_var_val.flt = testf;
      		}
          controlRcv = 0;  // havn't got ctl data yet from ecu
          rcv_timeout = 1;
                // Transmit request to write, then rdbk all ctl vars
          ibyte = 0;
          xmtbuf[0] = 'e';
          xmtbuf[1] = 0;   // canID
          xmtbuf[2] = 4;   // tbleID for inpram
          rbyteoffset = ctl_var_offset[curr_var_no];
          xmtbuf[3] = (U8)(rbyteoffset >> 8);     // msb
          xmtbuf[4] = (U8)rbyteoffset;            // lsb
          no_rbytes = ctl_var_sze[curr_var_no];   // no data bytes (1 or 2)
          xmtbuf[5] = 0;                     // msb
          xmtbuf[6] = (U8)no_rbytes;         // lsb
          CnvtSw = 2;
          VFDConvert();   // This takes control_var_val and converts
                             //  to ECU units (ctl_var_ecu_val)
          xmtbuf[7] = ctl_var_ecu_val;
          if(no_rbytes == 1)  {	 // uchars
                no_xbytes = 8;
          }
          else  {		                         // shorts
                xmtbuf[8] = ctl_var_ecu_val2;                // lsb
                no_xbytes = 9;
          }
          SCC2 |= SCI_INT_TRAN_COMP_ENABLE;  // enable xmit complete interrupt         
          Status = SCS1;
          SCDR = xmtbuf[0];
        }
        else  {         // control variable not selected, therefore want
                        //   to scroll right
        	if(!rcv_timeout)  {
        		curr_var_no++;
            	if(curr_var_no >= NUM_CTL_VARIABLES)
                        curr_var_no = 0;
                        
               	controlRcv = 0;  // havn't got ctl data yet from ecu
                // Transmit request for all control variable values
                ibyte = 0;
                xmtbuf[0] = 'r';
                xmtbuf[1] = 0;   // canID
                xmtbuf[2] = 4;   // tbleID for inpram
                rbyteoffset = ctl_var_offset[curr_var_no];
                xmtbuf[3] = (U8)(rbyteoffset >> 8);     // msb
                xmtbuf[4] = (U8)rbyteoffset;            // lsb
                no_rbytes = ctl_var_sze[curr_var_no];   // no data bytes (1 or 2)
                xmtbuf[5] = 0;                     // msb
                xmtbuf[6] = (U8)no_rbytes;         // lsb
                no_xbytes = 7;
                SCC2 |= SCI_Tx_INT_ENABLE;  // enable xmit empty interrupt
                Status = SCS1;
                SCDR = xmtbuf[0];
        	}
        }
        return;
}

void DecrIsr(void)  {
//  Enter here when push decr or scroll left button
short test;
float testf;
U8 Status;

        if(mode == DISPLAY)return;
        if(select && !rcv_timeout)  {   // control variable has been selected,
                        // therefore want to decrease value
      		changed_ctl_var--;
      		if(ctl_var_type[curr_var_no] == UCH)  {
      			test = control_var_val.uch - 1;
      			if(((curr_var_no == 11) || (curr_var_no == 18))	 // alternate,AMC optn
					&& (test < 0))test = 1;  
      			if(test >= 0)
                	control_var_val.uch = (U8)test;
      		}
      		else if(ctl_var_type[curr_var_no] == SHT)  {
      			test = control_var_val.sht - 1;
      			control_var_val.sht = test;
      		}
      		else if(ctl_var_type[curr_var_no] == FLT)  {
                if(curr_var_no == 15)  {                     // InjPWMPd
                	testf = control_var_val.flt - 0.01;
                	if(testf < 0.01)testf = 0.01;
                }
                else
                	testf = control_var_val.flt - 0.1;
      			if((curr_var_no != 17) && (testf < 0.0))testf = 0.0; // adv_offset can be -
      			control_var_val.flt = testf;
      		}
          controlRcv = 0;  // havn't got ctl data yet from ecu
          rcv_timeout = 1;
          // Transmit request to write, then rdbk all ctl vars
          ibyte = 0;
          xmtbuf[0] = 'e';
          xmtbuf[1] = 0;   // canID
          xmtbuf[2] = 4;   // tbleID for inpram
          rbyteoffset = ctl_var_offset[curr_var_no];
          xmtbuf[3] = (U8)(rbyteoffset >> 8);     // msb
          xmtbuf[4] = (U8)rbyteoffset;            // lsb
          no_rbytes = ctl_var_sze[curr_var_no];   // no data bytes (1 or 2)
          xmtbuf[5] = 0;                     // msb
          xmtbuf[6] = (U8)no_rbytes;         // lsb
          CnvtSw = 2;
          VFDConvert();   // This takes control_var_val and converts
                             //  to ECU units (ctl_var_ecu_val)
          xmtbuf[7] = ctl_var_ecu_val;
          if(no_rbytes == 1)  {	 // uchars
                no_xbytes = 8;
          }
          else  {		                         // shorts
                xmtbuf[8] = ctl_var_ecu_val2;                // lsb
                no_xbytes = 9;
          }
          SCC2 |= SCI_INT_TRAN_COMP_ENABLE;  // enable xmit complete interrupt         
          Status = SCS1;
          SCDR = xmtbuf[0];
        }
        else  {         // control variable not selected, therefore want
                        //   to scroll left
        	if(!rcv_timeout)  {
        		if(curr_var_no == 0)
                    	curr_var_no = NUM_CTL_VARIABLES;
        		curr_var_no--;

        		controlRcv = 0;  // havn't got ctl data yet from ecu
        		// Transmit request for all control variable values
        		ibyte = 0;
        		xmtbuf[0] = 'r';
        		xmtbuf[1] = 0;   // canID
        		xmtbuf[2] = 4;   // tbleID for inpram
                rbyteoffset = ctl_var_offset[curr_var_no];
                xmtbuf[3] = (U8)(rbyteoffset >> 8);     // msb
                xmtbuf[4] = (U8)rbyteoffset;            // lsb
                no_rbytes = ctl_var_sze[curr_var_no];   // no data bytes (1 or 2)
                xmtbuf[5] = 0;                     // msb
                xmtbuf[6] = (U8)no_rbytes;         // lsb
        		no_xbytes = 7;
        		SCC2 |= SCI_Tx_INT_ENABLE;  // enable xmit empty interrupt
        		Status = SCS1;
        		SCDR = xmtbuf[0];
        	}
        }
        return;
}

interrupt void GetDispIsr(void)  {
U8 Status;
//  Enter here every 0.25 seconds

	    // acknowledge TBM interrupt (to clear interrupt flag)
	    TBCR |= 0x08;
	    
		if(debounce > 0)debounce--;
		// incr/scroll button auto repeat
		if(mode == CONTROL)  {
			if(PortRead(PORT_A,1) == 0)
				incr_count++;
			else
				incr_count = 0;
			if(PortRead(PORT_A,0) == 0)
				decr_count++;
			else
				decr_count = 0;
		}
		
		// Check for receiver timeout
		if(rcv_timeout)  {
			rcv_timeout++;
			if(rcv_timeout > 8)  {   // 2 sec timeout
				// Re-transmit
				ibyte = 0;
        		SCC2 |= SCI_INT_TRAN_COMP_ENABLE;  // enable xmit complete interrupt         
	    		Status = SCS1;
	    		SCDR = xmtbuf[0];
			}
		}
		
        if(mode != DISPLAY)
    		return;
        // Set up to transmit request for all display data.
        //  At end of transmit, will automatically set up for
        //  receiving the data, then displaying it. 
        ibyte = 0;
        xmtbuf[0] = 'a';
        xmtbuf[1] = 0;   // canID
        xmtbuf[2] = 6;   // tbleID for txbuf(outpc)
        no_xbytes = 3;
        rbyteoffset = 0;
        no_rbytes = NO_DISP_BYTES_FROM_ECU;

	    SCC2 |= SCI_Tx_INT_ENABLE;   // enable xmit empty interrupt
	    Status = SCS1;
	    SCDR = xmtbuf[0];

        return;
}

void VFDInit(void)  {

	// RS = R/W = EN = 0
	PortClr(PORT_B, 4);   // EN
	PortClr(PORT_B, 5);   // R/W
	PortClr(PORT_B, 6);   // RS
	VFDData(0x33);
	// check if want to dim
    if(dim)  {    			
    	PortSet(PORT_B,6);      // set RS=1
    	VFDData(0x02);          // 50% brightness
    	PortClr(PORT_B,6);      // restore RS=0
    }
	
	VFDData(0x32);
	// 4 bit data, 2 lines, 5x7 dots
	VFDData(0x28);
	// ???????
	VFDData(0x06);
	// Turn on cursor
	VFDData(0x0C);
	// Clear display
	VFDData(0x01);
	
	return;
}

void VFDDisplay(void)  {
// This subroutine converts receive buffer data from ECU
//  to engineering data, formats it into VFD display strings,
//  and writes/ scrolls it across the VFD display.
char col;
short coljx;

	if(mode == DISPLAY)  {    // Display mode
		CnvtSw = 0;
		VFDConvert();     // This takes rcvbuf and converts  to
				   //  display variable values, then puts in VFDLower
		if(!freeze)  {
			colix++;
			if(colix >= MAX_VFD_STRLEN)colix=0;
		}
		coljx = colix;
		for(col=0; col < MAX_COLS; col++)  {
			VFDSend(0,col,DispVarUpperStrng[coljx]);
			VFDSend(1,col,VFDLower[coljx]);
			coljx++;
			if(coljx >= MAX_VFD_STRLEN)coljx=0;
		}
	}
	else  {  // Control mode
		CnvtSw = 1;
		VFDConvert();  // This takes rcvbuf and converts/sets
			   	//  value of current control variable
		// Display the control variable
        if(ctl_var_type[curr_var_no] == UCH)
        	sprintf(VFDLower, &CtlVarStrng[curr_var_no][0],
                             control_var_val.uch);
      	else if(ctl_var_type[curr_var_no] == SHT)
        	sprintf(VFDLower, &CtlVarStrng[curr_var_no][0],
                             control_var_val.sht);
      	else if(ctl_var_type[curr_var_no] == FLT)
        	sprintf(VFDLower, &CtlVarStrng[curr_var_no][0],
                              control_var_val.flt);
        // Send out VFD lines
        for(col=0; col < MAX_COLS; col++)  {
        	if(select)
            	VFDSend(0,col,SelectStrng[col]);
        	else
            	VFDSend(0,col,BlnkStrng[col]);
            VFDSend(1,col,VFDLower[col]);
        }
	}
	return;
}

void VFDConvert(void)  {
U8 status,itmp,neg,alarm;
short tmps;
unsigned short tmpu;

	if(CnvtSw == 0)  {
		// convert ECU data to display (engineering) units
		alarm_alt = 1 - alarm_alt;
		tmpu = (rcvbuf[0] << 8) + rcvbuf[1]; // seconds
		itoa(tmpu,&VFDLower[0],5,0);
		status = rcvbuf[11];             // ignore 10 = squirt
		if((status & 0x03) == 0)          // engine (bits 0,1)
			VFDsprintf(&VFDLower[9], 0, 0, 5, " OFF ",0);
		else if((status & 0x03) == 1)
			VFDsprintf(&VFDLower[9], 0, 0, 5, " RUN ",0);
		else
			VFDsprintf(&VFDLower[9], 0, 0, 5, "CRANK",0);
		if((status & 0x0C) == 0)         // warm enrich (bits 2,3)
			VFDsprintf(&VFDLower[18], 0, 0, 5, " OFF ",0);
		else if(((status & 0x0C) >> 2) == 1)
			VFDsprintf(&VFDLower[18], 0, 0, 5, "START",0);
		else
			VFDsprintf(&VFDLower[18], 0, 0, 5, "  ON ",0);
		if((status & 0x30) == 0)           // accel enrich (bits 4,5)
			VFDsprintf(&VFDLower[27], 0, 0, 5, " OFF ",0);
		else if(((status & 0x30) >> 4) == 1)
			VFDsprintf(&VFDLower[27], 0, 0, 5, "ACCEL",0);
		else
			VFDsprintf(&VFDLower[27], 0, 0, 5, "DECEL",0);
		tmpu = (rcvbuf[6] << 8) + rcvbuf[7]; // rpm
		itoa(tmpu,&VFDLower[35],5,0);
		tmpu = (rcvbuf[2] << 8) + rcvbuf[3];
		itmp = (U8)(tmpu / (U8)100);         // pw1 (msx10)
		VFDsprintf(&VFDLower[42], 2, 0, 4, &itmp,0);
		tmps = (rcvbuf[8] << 8) + rcvbuf[9];
		if(tmps >= 0)
		  neg = 0;
		else  {
		  tmps = -tmps;
		  neg = 1;
		}
		itmp = (U8)(tmps / (U8)10);      // adv deg
		VFDsprintf(&VFDLower[50], 1, neg, 3, &itmp,0);
		itmp = rcvbuf[63];                  // coil_dur (msx10)
		VFDsprintf(&VFDLower[59], 2, 0, 4, &itmp,0);
		itmp = rcvbuf[55];                  // iacstep (max 255 steps displayed)
		VFDsprintf(&VFDLower[68], 1, 0, 3, &itmp,0);
		
		tmpu = (rcvbuf[16] << 8) + rcvbuf[17];
		itmp = (U8)(tmpu / (U8)10);          // baro (kpa)
		if(alarm_alt && ((itmp < 70) || (itmp > 130)))  // baro out of range
			alarm = 1;
		else
			alarm = 0;
		VFDsprintf(&VFDLower[76], 1, 0, 3, &itmp,0);
		tmpu = (rcvbuf[18] << 8) + rcvbuf[19];
		itmp = (U8)(tmpu / (U8)10);          // map (kpa)
		if(alarm_alt && ((itmp == 0) || (itmp == 255)))  // railed
			alarm = 1;
		else
			alarm = 0;
		VFDsprintf(&VFDLower[86], 1, 0, 3, &itmp,alarm);
		tmps = (rcvbuf[22] << 8) + rcvbuf[23];
		if(TmpUnits == 0)  {   // Farenheit
		  if(alarm_alt && ((tmps <= -400) || (tmps >= 2150)))  // temp out of range/ overheat
			alarm = 1;
		  else
			alarm = 0;
		}
		else  {     // Celsius
		  if(alarm_alt && ((tmps <= -400) || (tmps >= 1010)))  // temp out of range/ overheat
			alarm = 1;
		  else
			alarm = 0;
		}
		if(tmps >= 0)
		  neg = 0;
		else  {
		  tmps = -tmps;
		  neg = 1;
		}
		itmp = (U8)(tmps / (U8)10);          // clt (F or C)
		VFDsprintf(&VFDLower[93], 1, neg, 3, &itmp,alarm);
		tmps = (rcvbuf[20] << 8) + rcvbuf[21];
		if(TmpUnits == 0)  {   // Farenheit
		  if(alarm_alt && ((tmps <= -400) || (tmps >= 2150)))  // temp out of range/ overheat
			alarm = 1;
		  else
			alarm = 0;
		}
		else  {                // Celsius
		  if(alarm_alt && ((tmps <= -400) || (tmps >= 1010)))  // temp out of range/ overheat
			alarm = 1;
		  else
			alarm = 0;
		}
		if(tmps >= 0)
		  neg = 0;
		else  {
		  tmps = -tmps;
		  neg = 1;
		}
		itmp = (U8)(tmps / (U8)10);          // mat (F or C)
		VFDsprintf(&VFDLower[100], 1, neg, 3, &itmp,alarm);
		tmpu = (rcvbuf[24] << 8) + rcvbuf[25];
		itmp = (U8)(tmpu / (U8)10);          // tps (Vx10)
		VFDsprintf(&VFDLower[107], 2, 0, 4, &itmp,0);
		itmp = rcvbuf[27];                   // batt (Vx10)
		if(alarm_alt && ((itmp < 90) || (itmp > 160)))     // out of range
			alarm = 1;
		else
			alarm = 0;
		VFDsprintf(&VFDLower[114], 2, 0, 4, &itmp,alarm);
		itmp = rcvbuf[29];                   // ego1 (afrx10)
		if(alarm_alt && ((itmp < 80) || (itmp > 220)))     // out of range
			alarm = 1;
		else
			alarm = 0;
		VFDsprintf(&VFDLower[120], 2, 0, 4, &itmp,alarm);
		VFDsprintf(&VFDLower[127], 2, 0, 4, &rcvbuf[12],0);   // afrtgt1
		itmp = rcvbuf[35];                  // egocor1 (%) (max of 255 displayed)
		VFDsprintf(&VFDLower[136], 1, 0, 3, &itmp,0);
		itmp = rcvbuf[39];                  // aircor (%) (max of 255 displayed)
		VFDsprintf(&VFDLower[146], 1, 0, 3, &itmp,0);
		itmp = rcvbuf[47];                  // barocor (%) (max of 255 displayed)
		VFDsprintf(&VFDLower[156], 1, 0, 3, &itmp,0);
		itmp = rcvbuf[51];                  // ve1 (%) (max of 255 displayed)
		VFDsprintf(&VFDLower[166], 1, 0, 3, &itmp,0);
		itmp = rcvbuf[41];                  // warmcor (%) (max of 255 displayed)
		VFDsprintf(&VFDLower[176], 1, 0, 3, &itmp,0);
		itmp = rcvbuf[43];                  // tpsaccel (msx10) (max of 25.5 displayed)
		VFDsprintf(&VFDLower[185], 2, 0, 4, &itmp,0);
		itmp = rcvbuf[45];                  // tpsfuelcut (%) (max of 255 displayed)
		VFDsprintf(&VFDLower[195], 1, 0, 3, &itmp,0);
		itmp = rcvbuf[49];                  // gammae (%) (max of 255 displayed)
		VFDsprintf(&VFDLower[205], 1, 0, 3, &itmp,0);
		tmps = (rcvbuf[56] << 8) + rcvbuf[57];
		if(tmps >= 0)
		  neg = 0;
		else  {
		  tmps = -tmps;
		  neg = 1;
		}
		itmp = (U8)(tmps / (U8)10);         // cold_adv (deg)
		VFDsprintf(&VFDLower[214], 1, neg, 3, &itmp,0);
	}
	else if(CnvtSw == 1)  {
		// convert control data from ecu to display data
		switch(curr_var_no)  {
		case 0:
		case 1:
			tmpu = (rcvbuf[0] << 8) + rcvbuf[1];  
			control_var_val.flt = (float)tmpu * 0.1;      // CWU,CWH
			break;                                        // (msx10->ms)
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
			control_var_val.sht = (rcvbuf[0] << 8) + rcvbuf[1];   // AWEVL,H,AWCL,H,IACStart
			break;
		case 7:
		case 8:
			tmpu = (rcvbuf[0] << 8) + rcvbuf[1];  // PrimePL, H (msx10->ms)
			control_var_val.flt = (float)tmpu * 0.1;  
			break; 
		case 9:
			tmpu = (rcvbuf[0] << 8) + rcvbuf[1];  // ReqFuel (usec -> ms)
			control_var_val.flt = (float)tmpu * 0.001;  
			break;
		case 10:
		case 11:
			control_var_val.uch = rcvbuf[0];     // divider, alternate
			break;
		case 12:
		case 13:
		case 14:
			control_var_val.flt = (float)rcvbuf[0] * 0.1;  // InjOpen, 
					                      // BattFac, InjPWMTim (msx10 -> ms)
			break;
		case 15:
			control_var_val.flt = (float)rcvbuf[0] * 0.001;  // InjPWMPd
			break;                                           // (usec->ms)
		case 16:
			control_var_val.uch = rcvbuf[0];                 // InjPWMDty 
			break;
		case 17:
			tmps = (rcvbuf[0] << 8) + rcvbuf[1];  // Adv Offset (degx10-> deg)
			control_var_val.flt = (float)tmps * 0.1;
			break;
		case 18:
			control_var_val.uch = rcvbuf[0];     // AMC Otion
			break;
		}    // end switch
		
	}
	else if(CnvtSw == 2)  {
		// convert display control data to ecu data
		switch(curr_var_no)  {
		case 0:
		case 1:
		  // CWU, CWH (ms -> msx10)
		  tmpu = (U16)(control_var_val.flt * 10.001);
		  ctl_var_ecu_val = (U8)(tmpu >> 8);                 // msb
		  ctl_var_ecu_val2 = (U8)(tmpu & 0x00FF);            // lsb
			break;
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		  // AWEVL,H, AWCL,H, IACStart
		  tmps = control_var_val.sht;
		  ctl_var_ecu_val = (U8)(tmps >> 8);                 // msb
		  ctl_var_ecu_val2 = (U8)(tmps & 0x00FF);            // lsb
		  break;
		case 7:
		case 8:
		  // PrimePL, H (ms -> msx10)
		  tmpu = (U16)(control_var_val.flt * 10.001);
		  ctl_var_ecu_val = (U8)(tmpu >> 8);                 // msb
		  ctl_var_ecu_val2 = (U8)(tmpu & 0x00FF);            // lsb
		  break;
		case 9:
		  // ReqFuel (ms -> usec)
		  tmpu = (U16)(control_var_val.flt * 1000.001);
		  ctl_var_ecu_val = (U8)(tmpu >> 8);                 // msb
		  ctl_var_ecu_val2 = (U8)(tmpu & 0x00FF);            // lsb
		  break;
		case 10:
		case 11:
		  // divider, alternate
		  ctl_var_ecu_val = control_var_val.uch;
		  break;
		case 12:
		case 13:
		case 14:
		  // InjOpen, Batfac, InjPWMTim (ms -> msx10)
		  ctl_var_ecu_val = (U8)(control_var_val.flt * 10.001);
		  break;
		case 15:
		  // InjPWMPd (ms -> usec)
		  ctl_var_ecu_val = (U8)(control_var_val.flt * 1000.001);
		  break;
		case 16:
		  // InjPWMDty 
		  ctl_var_ecu_val = control_var_val.uch;
		  break;
		case 17:
		  // Adv Offset (deg-> degx10)
		  tmps = (short)(control_var_val.flt * 10.001);
		  ctl_var_ecu_val = (U8)(tmps >> 8);                 // msb
		  ctl_var_ecu_val2 = (U8)(tmps & 0x00FF);            // lsb
		  break;
		case 18:
		  // AMCOption
		  ctl_var_ecu_val = control_var_val.uch;
		  break;
		}    // end switch
	}   // end main else
	return;
}

void itoa(short value, U8 *bufi, U8 width, U8 prec) {
short tmp2;
U8 ix,kx,lz;
unsigned short div,tmp1;
//    value = "width digits . prec digits"
//      width + prec <= 5
		lz = 1;
		kx = 0;
		tmp2 = value;
		if(tmp2 < 0)  {
		  tmp2 = -tmp2;
		  bufi[kx] = '-';
		  kx++;
		}
		div = diva[width-1];
		for(ix=width + prec; ix > prec; ix--)  {
			tmp1 = tmp2 / div;     // leading digit
			tmp2 = tmp2 % div;     // remainder
			if((tmp1 == 0) && lz)     // blank leading zeros
				bufi[kx] = ' ';
			else  {
				bufi[kx] = 48 + tmp1;
				lz = 0;
			}
			kx++;
			div /= 10;
		}
		if(!prec)return;
		bufi[kx] = '.';
		kx++;
		for(ix=prec; ix > 0; ix--)  {
			tmp1 = tmp2 / div;     // leading digit
			tmp2 = tmp2 % div;     // remainder
			kx++;
			bufi[kx] = 48 + tmp1;
			div /= 10;
		}
		return;
}

void VFDsprintf(U8 *bufo, U8 type, U8 neg, U8 width, U8 *bufi, U8 alarm)  {
U8 ix;

if(!alarm)  {
	if(type == 0)  {         // string
		for(ix=0;ix < width; ix++)  {
			bufo[ix] = bufi[ix];
		}
	}
	else if (type == 1)  {   // char
		if(neg)  {
			bufo[0] = '-';
			bufo[1] = asc_cnvt_tbl2[bufi[0]];
			bufo[2] = asc_cnvt_tbl1[bufi[0]];
		}
		else  {
			bufo[0] = asc_cnvt_tbl3[bufi[0]];
			bufo[1] = asc_cnvt_tbl2[bufi[0]];
			bufo[2] = asc_cnvt_tbl1[bufi[0]];
		}
	}
	else if (type == 2)  {   // float
		bufo[0] = asc_cnvt_tbl3[bufi[0]];
		bufo[1] = asc_cnvt_tbl2[bufi[0]];
		bufo[2] = '.';
		bufo[3] = asc_cnvt_tbl1[bufi[0]];
	}
}
else  {
		bufo[0] = ' ';
		bufo[1] = ' ';
		bufo[2] = ' ';
		if(type == 2)
			bufo[3] = ' ';
}

	return;
}

void VFDSend(char line, char col, char value)  {

	// Set up to send a byte to VFD character RAM
	PortClr(PORT_B, 4);   // EN = 0
	PortClr(PORT_B, 5);   // R/~W = 0 (write operation)
	PortClr(PORT_B, 6);   // RS = 0 (sending an instruction)
	// send address of where cursor/ data byte will go
	// format of byte is 0x1lcccccc where l = line # (0,1)
	//  and cccccc = 6-bit col #.	
	VFDData(0x80 | (line << 6) | col);   // Send position

	// Send data byte
	PortSet(PORT_B, 6);   // RS = 1 (sending a data byte)
	VFDData(value);

	return;
}

void VFDData(char value)  {
U8 ix;

	// data bit 4 (PTC0)
	if(value & 0x10)
		PortSet(PORT_C, 0);
	else
		PortClr(PORT_C, 0);
	// data bit 5 (PTC1)
	if(value & 0x20)
		PortSet(PORT_C, 1);
	else
		PortClr(PORT_C, 1);
	// data bit 6 (PTC2)
	if(value & 0x40)
		PortSet(PORT_C, 2);
	else
		PortClr(PORT_C, 2);
	// data bit 7 (PTC3)
	if(value & 0x80)
		PortSet(PORT_C, 3);
	else
		PortClr(PORT_C, 3);

	for(ix=0; ix < 245; ix++);      //delay to let data settle
	PortSet(PORT_B, 4);      // set enable line to send over
	// delay > .5 us to satisfy VFD timing reqts.
	for(ix=0; ix < 5; ix++)  {
		asm nop;
	}
	PortClr(PORT_B, 4);      // unset enable to set up for next xmt
	// delay > .5 us to satisfy VFD timing reqts.
	for(ix=0; ix < 5; ix++)  {
		asm nop;
	}

	// data bit 0 (PTC0)
	if(value & 0x01)
		PortSet(PORT_C, 0);
	else
		PortClr(PORT_C, 0);
	// data bit 1 (PTC1)
	if(value & 0x02)
		PortSet(PORT_C, 1);
	else
		PortClr(PORT_C, 1);
	// data bit 2 (PTC2)
	if(value & 0x04)
		PortSet(PORT_C, 2);
	else
		PortClr(PORT_C, 2);
	// data bit 3 (PTC3)
	if(value & 0x08)
		PortSet(PORT_C, 3);
	else
		PortClr(PORT_C, 3);
	for(ix=0; ix < 245; ix++);      //delay to let data settle
	PortSet(PORT_B, 4);      // set enable line to send over
	// delay > .5 us to satisfy VFD timing reqts.
	for(ix=0; ix < 5; ix++)  {
		asm nop;
	}
	PortClr(PORT_B, 4);      // unset enable to set up for next xmt
	// delay > .5 us to satisfy VFD timing reqts.
	for(ix=0; ix < 5; ix++)  {
		asm nop;
	}
	
    return;
}