/*************************************************************************
**************************************************************************
**   M E G A S Q U I R T  II - 2 0 0 4 - V1.000
**
**   (C) 2003 - B. A. Bowling And A. C. Grippo
**
**   This header must appear on all derivatives of this code.
**
***************************************************************************
**************************************************************************/

/*************************************************************************
**************************************************************************
**   GCC Port
**
**   (C) 2004,2005 - Philip L Johnson
**
**   This header must appear on all derivatives of this code.
**
***************************************************************************
**************************************************************************/

/*------------------------
  Version 1.000
    This version has all the basic capabilities of MS, but much higher precision -
  in the usec range. It adds larger tuning tables (12x12), WBO2, IAC, dual tables, 
  and ignition control which assumes 1 coil and a mechanical distributor to 
  distribute spark or EDIS for multicoil wasted spark.
  Version 1.100
    Made serial comms more robust: added one word read, write + verify commands, 
    write to ram inhibit when overrun detection, receiver timeout check.
  Version 1.200
    Fixed various problems with trigger offsets ATDC (charge time can occur after
    input capture at very low rpms and other related ignition timing problems.)
  Version 1.300
    Changed tps, tpsdot units to % x 10; added accel tailoff algorithm.
  Version 2.000
    This version works with the CAN v3 pcb and has a template for CAN comms.
    Additional changes: increased input area to 4 x 512 byte blocks;
    inhibit coil charge 2 sec after turning ign key on;
    changed ports for FP, Idle (but connect to same pins on motherboard);
    knock ADC input in Vx100; all unused ports initialized as inputs.
    Made IACstart, PrimeP, CWU,CWH, AWEV,AWC,asecount ints instead of bytes.
    PrimeP,AWEV,AWC made function of initial coolant temperature.
    Expanded IAC control options for cranking plus switch to time based after
    start; added pulse tolerance inputs for cranking and after start; IAC
    option(5) to always enable stepper motor chip for 15 minutes after start,
    then revert to option 2. 
  Version 2.100
    Added crank trigger mode.Ignition fires with only mech adv during cranking;
    added kpa to display outputs.
  Version 2.200
    Modified/ added ADC averaging: provided lag filter averaging for all 
    ADC inputs (also for rpm). Relocated VE tables in anticipation of 
    Block Learn Mode.
  Version 2.300
    Improved ego closed loop correction algorithms; flex fuel correction for
    alternative fuels; generic spare output ports;fixed logic error for IdleCtl=5;
    added fix to maintain coherence of inputs when changed thru serial port.
  Version 2.310, 2.320, 2.330, 2340, 2.350, 2.360
    Fixed bug in ASE logic;set PW=0 in RevLimit mode;gcc compatibility changes;
    fixed bug in spare port hysteresis; changed some defaults; fixed bug in masking
    of TimerIn interrupts for dt/2; limited pw_open in case of battery spikes.
    Fixed stepper motor bug.
  Version 2.400
    Added automatic mixture control;mod to AE;expanded warmup inputs;
    moved DoOnStack to permanent ram;added option for 2 NBs;added knock
    retard based on ADC7 sensor.
  Version 2.500
    Added wheel decoding; added simple AE taper based on rpm;added time,% parameters
    to control tach input interrupt masking; 5 sec min for persistence of AS pulse
    tolerance; extended pulse tol range from 100 to 255.
  Version 2.510, 2.520, 2.530, 2.540
    Added fix to extend rpm range for wheel decoding; fixed AMC logic.
  Version 2.600, 2.610, 2.620, 2.630, 2.640, 2.660, 2.670, 2.671, 2.672, 2.681-2.687
    Added small model multi-page memory; added X-tau transient compensation; spark
    retard as function of manifold air temperature; added table fuel corrections for
    air density and barometric pressure; added spk retard correction for flex fuel;
    added injector test mode. Fixed bug in table burner; added ego volt outputs;
    shortened serial ISR time to expand wheel decoder range. Provided input for pwm
    stepper freq. Added check for serial overrun flag. Limit baro to 80-120 kPa for 
    option 1. Put COP timeout in main loop. Made NB AFRtarget Vx100 in code. Reduced
    default tau table. Fixed burn in Inj Test mode. Added wheel decode improvements
    to check for lost synch, individual tooth timing correction.Omit check for xtra/
    missing tach pulse if accelerating/ decelerating. Omit immed. f/p off. RPM limit
    for AMC burns. Made fixes to improve IAC performance.
  Version 2.700
    Added dual spark outputs; allow alpha-N at lo rpm or at hi rpm.
  Version 2.800
    Added MAF capability, including MAP/ MAF blend, 3-port selection for maf input;
    added option to multiply pw eq x AFRStoich / AFRtgt[map,rpm]; added option to
    use map/baro as index in ve,spk,afr tables. Set pw to 0 when ve = 0 to avoid
    any fuel dribble. Added option for wasted spark/ COP (requires external hw).
    For PredOpt=2, use 1st deriv ign prediction if slow rate of change. Allow tau=0.
    Fixed code to make all longs atomic. Put in AMCdve logic.
  Version 2.810, 2.820-2.823, 2.830-2.831
    Removed option for wasted spark/ COP with router chip (will be moved to router
    board). Added 2 cyl cop/ 4 cyl wasted spark using cam sync and/or toothed wheel.
    Changes to make starting faster.Shortened cpu time in TimerClock. At startup set
    IAC step to crankpos instead of fully open. Replaced derivative next tach predi-
    ction algorithms with alpha, beta, gamma prediction filter. Added serial/ CAN  
    code to interface between MT and auxilliary boards. Improved serial reliability. 
 -----------------------*/
#include "hcs12def.h"      /* common defines and macros */
#include "flash.h"         /* flashburner defines, structures */
#include <string.h>

//#define CAN_TEST

#ifdef GCC_BUILD
#define FAR_TEXT1_ATTR __attribute__ ((far)) __attribute__ ((section (".text1")))
#define TEXT1_ATTR __attribute__ ((section (".text1")))
#define EEPROM_ATTR __attribute__ ((section (".eeprom")))
#define INTERRUPT
#define POST_INTERRUPT __attribute__((interrupt))
#define ENABLE_INTERRUPTS __asm__ __volatile__ ("cli");
#define DISABLE_INTERRUPTS __asm__ __volatile__ ("sei");
#define NEAR
#define VECT_ATTR __attribute__ ((section (".vectors")))
extern void _start(void);       /* Startup routine */

#else
#define FAR_TEXT1_ATTR
#define TEXT1_ATTR
#define EEPROM_ATTR
#define INTERRUPT interrupt
#define POST_INTERRUPT
#define ENABLE_INTERRUPTS asm cli;
#define DISABLE_INTERRUPTS asm sei;
#define NEAR near
#define VECT_ATTR @0xFF80
#pragma CODE_SEG NON_BANKED /* Interrupt section for this module. 
                          Placement will be in NON_BANKED area. */

extern void near _Startup(void);       /* Startup routine */
#endif
INTERRUPT void UnimplementedISR(void) POST_INTERRUPT;
INTERRUPT void ISR_Ign_TimerIn(void) POST_INTERRUPT;
INTERRUPT void ISR_Ign_TimerOut(void) POST_INTERRUPT;
INTERRUPT void ISR_Inj1_TimerOut(void) POST_INTERRUPT;
INTERRUPT void ISR_Inj2_TimerOut(void) POST_INTERRUPT;
INTERRUPT void ISR_TimerOverflow(void) POST_INTERRUPT;
INTERRUPT void ISR_Timer_Clock(void) POST_INTERRUPT;
INTERRUPT void ISR_SCI_Comm(void) POST_INTERRUPT;
INTERRUPT void CanTxIsr(void) POST_INTERRUPT;
INTERRUPT void CanRxIsr(void) POST_INTERRUPT;

typedef void (* NEAR tIsrFunc)(void);
const tIsrFunc _vect[] VECT_ATTR = {      /* Interrupt table */
        UnimplementedISR,                 /* vector 63 */
        UnimplementedISR,                 /* vector 62 */
        UnimplementedISR,                 /* vector 61 */
        UnimplementedISR,                 /* vector 60 */
        UnimplementedISR,                 /* vector 59 */
        UnimplementedISR,                 /* vector 58 */
        UnimplementedISR,                 /* vector 57 */
        UnimplementedISR,                 /* vector 56 */
        UnimplementedISR,                 /* vector 55 */
        UnimplementedISR,                 /* vector 54 */
        UnimplementedISR,                 /* vector 53 */
        UnimplementedISR,                 /* vector 52 */
        UnimplementedISR,                 /* vector 51 */
        UnimplementedISR,                 /* vector 50 */
        UnimplementedISR,                 /* vector 49 */
        UnimplementedISR,                 /* vector 48 */
        UnimplementedISR,                 /* vector 47 */
        UnimplementedISR,                 /* vector 46 */
        UnimplementedISR,                 /* vector 45 */
        UnimplementedISR,                 /* vector 44 */
        UnimplementedISR,                 /* vector 43 */
        UnimplementedISR,                 /* vector 42 */
        UnimplementedISR,                 /* vector 41 */
        UnimplementedISR,                 /* vector 40 */
        CanTxIsr,                         /* vector 39 */
        CanRxIsr,                         /* vector 38 */
        CanRxIsr,                         /* vector 37 */
        UnimplementedISR,                 /* vector 36 */
        UnimplementedISR,                 /* vector 35 */
        UnimplementedISR,                 /* vector 34 */
        UnimplementedISR,                 /* vector 33 */
        UnimplementedISR,                 /* vector 32 */
        UnimplementedISR,                 /* vector 31 */
        UnimplementedISR,                 /* vector 30 */
        UnimplementedISR,                 /* vector 29 */
        UnimplementedISR,                 /* vector 28 */
        UnimplementedISR,                 /* vector 27 */
        UnimplementedISR,                 /* vector 26 */
        UnimplementedISR,                 /* vector 25 */
        UnimplementedISR,                 /* vector 24 */
        UnimplementedISR,                 /* vector 23 */
        UnimplementedISR,                 /* vector 22 */
        UnimplementedISR,                 /* vector 21 */
        ISR_SCI_Comm,                     /* vector 20 */
        UnimplementedISR,                 /* vector 19 */
        UnimplementedISR,                 /* vector 18 */
        UnimplementedISR,                 /* vector 17 */
        ISR_TimerOverflow,                /* vector 16 */
        UnimplementedISR,                 /* vector 15 */
        UnimplementedISR,                 /* vector 14 */
        ISR_Ign_TimerOut,                 /* vector 13 */
        ISR_Ign_TimerOut,                 /* vector 12 */
        ISR_Inj2_TimerOut,                /* vector 11 */
        ISR_Ign_TimerIn,                  /* vector 10 */
        ISR_Inj1_TimerOut,                /* vector 09 */
        ISR_Ign_TimerIn,                  /* vector 08 */
        ISR_Timer_Clock,                  /* vector 07 */
        UnimplementedISR,                 /* vector 06 */
        UnimplementedISR,                 /* vector 05 */
        UnimplementedISR,                 /* vector 04 */
        UnimplementedISR,                 /* vector 03 */
        UnimplementedISR,                 /* vector 02 */
        UnimplementedISR,                 /* vector 01 */
#ifdef GCC_BUILD
        _start                            /* Reset vector */
   };
#else
        _Startup                          /* Reset vector */
   };

#pragma CODE_SEG ROM_7400

void switch_page(unsigned char sub_no,char carg1,char carg2);
int intrp_1dctable(char sgnx, int x, unsigned char n, int * x_table, 
  char sgnz, unsigned char * z_table) TEXT1_ATTR;
int intrp_2dctable(unsigned int x, int y, unsigned char nx, unsigned char ny,
  unsigned int * x_table, int * y_table, unsigned char * z_table) TEXT1_ATTR;
int CW_table(int clt, int *table) TEXT1_ATTR;

#pragma CODE_SEG DEFAULT
   
#endif

#include "cltfactor.inc"
#include "matfactor.inc"
#include "egofactor.inc"
#include "maffactor.inc"

#define NO_TBLES 	7
#define NO_FMAPS 	12
#define NO_SMAPS 	12
#define NO_FRPMS 	12
#define NO_SRPMS 	12
#define NO_ARPMS  6
#define NO_ATPSS  6
#define NO_INJ		2
#define NO_TEMPS 	10
#define NO_MAT_TEMPS 	6
#define NO_BARS 	6
#define NO_MATS 	6
#define NO_TPS_DOTS   4
#define NO_MAP_DOTS   4
#define NO_MAFS   12
#define NO_KNKRPMS  6
#define NPORT    7
#define NO_COILCHG_PTS  5
#define EGODT  78        /* 78 .128 ms tics = 10 ms */
#define END_SYNCH  3
#define AMCBURN_RPM_THRESH  1200

#define	MSG_CMD	0
#define	MSG_REQ	1
#define	MSG_RSP	2
#define	MSG_XSUB	3
#define	MSG_BURN	4
#define	NO_VAR_BLKS	16
#define NO_CANMSG 10
#define NO_CANBOARDS 16
#define CANID_THIS_BOARD 0
// Error status words: 
//    -bits 0-7 are current errors
//    -bits 8-15 are corresponding latched errors
#define	XMT_ERR			0x0101
#define	CLR_XMT_ERR		0xFFFE
#define	XMT_TOUT		0x0202
#define	CLR_XMT_TOUT	0xFFFD
#define	RCV_ERR			0x0404
#define	CLR_RCV_ERR		0xFFFB
#define	SYS_ERR		0x0808

// User inputs - 1 set in flash, 1 in ram
typedef struct {
unsigned char no_cyl,no_skip_pulses,      // skip >= n tach pulses at startup
    ICIgnOption,     // Bit 0: Input capture: 0 = falling edge, 1 rising
                     // Bit 1: 1= trigger return mode in cranking; for this
          // mode, set bit 0 to normal edge, then opp.edge used in cranking.
          // trigger return not applicable to EDIS
                     // Bit 2: 1 = fire on tach trigger with no advance
          // while cranking; not applicable for EDIS. Bits 1,2 cannot both=1
                     // Bit 3: spare
	                   // Bits 4-7: 0 = standard - charge coil, spark
	                   //           1 = Ford  EDIS option
	                   //           2 = EDIS with multispark
    spkout_hi_lo,    // Ign Output compare: 0 = spark low (gnd)
    max_coil_dur,max_spk_dur,DurAcc;        // ms x 10
char deltV_table[NO_COILCHG_PTS],deltDur_table[NO_COILCHG_PTS], // Vx10,msx10
    RevLimOption,     // 0=none, 1=spark retard, 2=fuel cut
    RevLimMaxRtd,     // max amount of spark retard (degx10) (at Rpm2 below)            
    PredOpt;          // Option for prediction algorithm for next tach plse
                      //  0=use last time interval
                      //  1=use alpha-beta-gamma filter
int crank_rpm;        // rpm at which cranking is through (~300 - 400 rpm)
int cold_adv_table[NO_TEMPS],
   adv_offset,        // all in deg x 10
   //     adv_offset is no deg(x10) between trigger (Input Capture) and TDC.
   //     It may be +btdc (IC1 to TDC) or -atdc (TDC to IC2).
   //
   //    |               |     |
   //    |               |     |
   //   -|---------------|-----|-
   //   IC1             TDC    IC2
   RevLimRpm1,RevLimRpm2;  // Optn 1:Retard spk by 0 deg at Rpm1 and increase
                           //    to RevLimMaxRtd at Rpm2
                        // Optn 2:Cut fuel above Rpm2, restore below Rpm1
unsigned char afr_table[NO_INJ][NO_FMAPS][NO_FRPMS],            // afr x 10
   warmen_table[NO_TEMPS], // % enrichment vs temp
   tpsen_table[NO_TPS_DOTS], // accel enrichment pw in .1 ms units vs tpsdot
   mapen_table[NO_MAP_DOTS]; // accel enrichment pw in .1 ms units vs mapdot
int iacstep_table[NO_TEMPS]; // iac steps vs temp
unsigned int frpm_table[NO_FRPMS],srpm_table[NO_SRPMS];  // fuel, spk rpm tables
int fmap_table[NO_FMAPS],smap_table[NO_SMAPS],	    // kpa x 10, 
   temp_table[NO_TEMPS],             // deg x 10 (C or F)
   tpsdot_table[NO_TPS_DOTS],        // change in % x 10 per .1 sec
   mapdot_table[NO_MAP_DOTS];        // change in kPa x 10 per .1 sec
int map0,mapmax,clt0,cltmult,mat0,matmult,tps0,tpsmax,batt0,battmax,
                  // kPa x 10, deg F or C x 10, adc counts, volts x 10
   ego0,egomult,baro0,baromax,bcor0,bcormult,knock0,knockmax;
                  // afr x 10, kpa x 10, %, volts x 100
char cpad3,cpad4; // pad bytes
unsigned char CrnkTol,ASTol,PulseTol,   // % tolerance for next input pulse
      // timing during cranking, after start/warmup and normal running    
 IdleCtl,         // idle: 0 = none, 1= solenoid, 2= iac stepper motor
                  //  - enabled only when moving, 3 = iac motor - always enabled.
                   // 4 = Ford pwm, 5 = 3 for 15 min, then = 2.
 IACtstep,        // iac stepper motor nominal time between steps (.1 ms units)
 IACaccstep,      // iac stepper motor accel/decel step time (.1 ms)-future use
 IACnaccstep,     // iac stepper motor no. of accel/decel steps - future use
 IACpwm_step;     // controls freq of pwm FIdle solenoid = 80 Hz * IACpwm_step. 
int IACStart,     // no. of steps to send at startup to put stepper 
                  //    motor at reference (wide open) position
 IdleHyst,        // amount clt temp must move before Idle position is changed
 IACcrankpos,     // IAC pos must be open(<) at least this much(steps) in cranking
 IACcrankxt,      // no. seconds from end of cranking for IAC pos to blend into 
                  // coolant dependent pos.
 IACcoldtmp,IACcoldpos,IACcoldxt,  // when startup coolant temp < IACcoldtmp,
          // then when reach (>=) IACcoldpos switch to time based ctl: interpolate
          // bet IACcoldpos and final table IAC pos for period IACcoldxt secs.
 PrimePU,PrimePH, // priming pulsewidths at min, max temps in temp_table (ms x 10)
 CWU,CWH,         // crank pulsewidths at min, max temps in temp_table (ms x 10)
 AWEVU,AWEVH,     // after start warmup % enrich add-on value at min,max temps in 
                  // temp_table
 AWCU,AWCH;       // after start enrichment no. cycles at min,max temps in temp_table
unsigned char  Tpsacold,     // cold (-40F) accel amount in .1 ms units
 AccMult,         // cold (-40F) accel multiply factor (%)
 TpsThresh,       // tpsdot threshhold for acc/decel enrichment(change in %x10 per .1 sec) 
 MapThresh,       // mapdot threshhold for acc/decel enrichment(change in kPax10 per .1 s) 
 TpsAsync,        // clock duration (in .1 sec tics) for accel enrichment
 TPSDQ;           // deceleration fuel cut option (%)
int TPSWOT,       // TPS value at WOT (for flood clear) in %x10
 TPSOXLimit;      // Max tps value (%x10) where O2 closed loop active
unsigned char Tps_acc_wght,    // weight (0-100) to be given to tpsdot for accel enrichment.
                  //  100 - Tps_acc_wght will then be given to mapdot.
 BaroOption,      // 0=no baro, 1=baro is 1st reading of map (before cranking),
                  // 2=independent barometer (=> EgoOption not 2 or 4)
 EgoOption,       // 0 = no ego;1= nb o2;2=2 nb o2;3=single wbo2;4=dual wbo2. NOTE:
                  //  BaroOption MUST be < 2 if EgoOption = 2 or 4.
 EgoCountCmp,     // Ign Pulse counts between when EGO corrections are made
 EgoStep,         // % step change for EGO corrections
 EgoLimit,        // Upper/Lower rail limit (egocorr inside 100 +/- limit)
 AFRTarget,       // NBO2 AFR(Vx100) determining rich/ lean
 Temp_Units,      // 0= coolant & mat in deg F; 1= deg C
 MAFOption,       // MAF options. Bits 0-3: =0 is no MAF; =1 is MAF-only; =2 is MAF -
                  // MAP blend. Bits 4-7: =0, MAF signal on ADC0=MAP pin. Must put MAF
                  // here if MAFOption = 0. =1, MAF signal on ADC6=Baro/Ego2 pin; =2,
                  // MAF signal on ADC7 (knock pin).
 DualSpkOptn;     // Option = 1 for 2 ignition inputs and 2 outputs. Specifying this option
                  //  uses the 2 pwm1,2 pins (TC2,TC4) for the extra ignition in, out;
                  //  therefore ** NO injector PWM ** with this option.Also, NO trigger
                  //  return, NO wheel decode, NO EDIS. Also set ISR_tmask=0,_pmask < 20.
                  // Option = 2,3 for 2 cyl COP/ 4 cyl wasted spark with normal tach pulse
                  //  on input 1, cam synch on input 2;option 2= falling, 3= rising edge for
                  //  cam synch. When cam synch occurs, fire Output 1, then fire O/P 2 on 
                  //  next tach, etc. No inj pwm, NO trig_ret, No EDIS.	Can have tooth wheel.
                  // Option = 4 is same as 2,3 but 1 toothed wheel on input 1 (input 2 
                  //  is unused). On tooth after missing tooth(teeth) fire  O/P 1, then 
                  //  fire O/P 2 after skip teeth (= next tach), etc.
                  // Option = 5 same as 4 except wheel on cam, so use 1/2 crank miss teeth.     
int FastIdle,     // fast idle Off temperature (idle_ctl = 1 only)
 EgoTemp,         // min clt temp where ego active
 RPMOXLimit;      // Min rpm where O2 closed loop is active
unsigned int ReqFuel;     // fuel pulsewidth (usec) at wide open throttle
unsigned char Divider,    // divide factor for input tach pulses
 Alternate,       // option to alternate injector banks
 InjOpen,         // Injector open time (.1 ms units)
 InjPWMTim,       // Time (.1 ms units) after Inj opening to start pwm
 InjPWMPd,        // Inj PWM period (us) - keep between 10-25 KHz (100-40 us)
 InjPWMDty,       // Inj PWM duty cycle (%)
 BatFac,          // Battery fuel pw correction factor (msx10)
 EngStroke,       // 0 = 4 stroke
                  // 1 = 2 stroke     
 InjType,         // 0 = port injection
                  // 1 = throttle body
 NoInj,           // no. of injectors (1-12)
 OddFire1,        // > 0 = odd fire option - smaller angle bet firings (deg)
 OddFire2,        // > 0 = odd fire option - larger angle bet firings (deg)
 rpmLF,mapLF,tpsLF,   // Lag filter coefficient for Rpm,Map/ Maf,Tps. Acts like
 egoLF,           // averager: xnew = xold + xLF * (xmeas - xold), so xLF=0
 adcLF,knkLF,     // means value will never change, xLF=100 means no filtering.
                  // egoLF is coefficient for ego filter, adcLF is for clt,mat,batt,
                  // knkLF is for knock.
 AMCOption,       // Automatic Mixture Control Option: 0 = none; 1= automatic updates
                  // to ram ve table(s); 2= also automatically updates flash ve tables.
 dual_tble_optn,  // 1 = use dual table option - ve, afr tables for each inj
 FuelAlpha,       // option for alpha-N mode: bits 0-3: 0=none;1=use map,tps blend 
                  // algorithm; 2=same as 1, but don't use kpa multiplier in pwidth eq;
                  //   bit 4 used to specify whether alphaN used below alpha_lorpm
                  //   or above alpha_hirpm.
 // Next two options can be implemented if needed. Presently Ign and afr table will
 //  be handled as AlphaN if FuelAlpha selected, regardless of their settings.
 IgnAlpha,        // option to use map,tps(alphaN) blend algorithm for ignition
 AfrAlpha,        // option to use map,tps(alphaN) blend algorithm for WBO2 AFR
 AFRStoich;       // stoich afr ratio (afrx10 or Vx100) for fuel used. If AFRMult=1, this 
                  //   input used to normalize afrtgt or afr_table multiplier in fuel pw eq.
int alpha_lorpm,  // option to use map,tps(alphaN) blend algorithm for fuel
 alpha_hirpm,     //  use tps if < lorpm; use map if > hirpm; else blend, in which
                  //  case lo, hirpm should be consecutive entries in frpm_table.
                  //    set lorpm=hirpm =0 to use map enrichment only,
                  //    set lorpm =25K to use alpha-N enrichment only,
                  //  If bit 4 set in FuelAlpha, use tps if > hirpm,map if < lorpm.
 alpha_map_table[NO_ATPSS][NO_ARPMS], // Table of ave map values (kpa x 10) for 
                  //  [tps][rpm] pairs. Used only for alpha-N algorithm.
                  //  Allows ve table to retain same ve values
                  //  whether in alpha-N mode or not,
 amap_tps[NO_ATPSS];     // Tps values (% x 10) for alpha_map table
unsigned int amap_rpm[NO_ARPMS];     // Rpm values for alpha_map table
unsigned long baud;         // baud rate
int MAPOXLimit;   // Max map value (kPax10) where O2 closed loop active
unsigned char board_id_type[NO_CANBOARDS];  // board type (1-255) of ith board;
                  // type=0, no board present at ith slot.
                  // type=1, ECU (MS II)
                  // type=2. Router board
                  // type=3, I/O board, ......
// Generic spare port parameters: spr_port = 0(don't use)/1(use),where
// spr_port[0-7] = PM2 - PM5; PTT6,7; PORTA0; they are set as outs to main
//  pcb; out_offset,out_byte= byte offset from start of outpc structure and
// size in bytes of 1st, 2nd variables to be tested for setting port;
// condition='<', '>', '=';cond12 = '&','|',' ' connects the conditions for
// the two variables with ' ' meaning only the first variable condition is 
// desired; thresh = value for the condition(e.g., var1 > thresh1); init_val,
// port_val=value (0/1) to which the pin will be set at startup and when the 
// condition(s) is met; hyst is a hysteresis delta and works as ff: if a 
// setpoint condition is > and it is met, set port to val and leave until 
// variable is < thresh - hyst, then set pin back to 1 - val. Similarly if 
// condition is <, wait til var > thresh + hyst. For dual conditions, the
// hysteresis conditions are evaluated the same way, but use the opposite
// of cond12 to connect them (if cnd12 is &, use | and vice versa).
char
   spr_port   [NPORT], // NPORT == 7
   condition1 [NPORT],
   condition2 [NPORT],
   cond12     [NPORT],
   init_val   [NPORT],
   port_val   [NPORT],
   out_byte1  [NPORT],
   out_byte2  [NPORT];
int
   out_offset1[NPORT],
   out_offset2[NPORT],
   thresh1    [NPORT],
   thresh2    [NPORT],
   hyst1      [NPORT],
   hyst2      [NPORT];
unsigned char
   TpsAsync2,  // accel tailoff duration (sec x 10)
   AFRMult;		 // = 1 means multiply fuel pw eq by AFRStoich/AFRTgt(NB)
               // = 2 means multiply fuel pw eq by AFRStoich/afr_table(WB).
int
   TpsAccel2;      // end pulsewidth of accel enrichment (ms x 10)
unsigned char
   EgoAlg, // 0=simple prop error algorithm;
           // 1=same algorithm with variable transport delay;
	   // 2=full PID with Smith Pred correction.
   egoKP,
   egoKI,
   egoKD;    // PID coefficients in %; egoKP also gain for 
                       // EgoAlg=0, with 100 = no gain; egoKD includes
                       // 1/dt factor since fixed time step. 
unsigned int
   egoKdly1, // coefficients used to calculate ego transport
 egoKdly2;      // delay (ms) = Kdly1 + Kdly2*120000 / (map(kPax10)*rpm)
unsigned char
   FlexFuel, // Flex fuel option - modifies pw and spk adv based on % alcohol.
   prime_delay,	  // wait for fuel pump to prime, sec
   fuelFreq[2],	  // Table of fuel sensor freq(Hz) vs %fuel corr;
   fuelCorr[2],
   AMCStep,		    // % of AMC correction to be applied when ramve updated.
   AMCdve;        // AMC correction must be > AMCdve % to be applied in burn
unsigned int
   AMCve_drpm,AMCve_dmap,  // current rpm, map(kPax10) must be within this 
   AMCramve_dt,			// tolerance of a ve table vertex for AMCramve_dt secs  
   						      // before AMC ve correction is applied
   AMCT_thresh,     // Min time (secs) between flash burns of ram ve table 
   AMCupdate_thresh;// Min number of AMC ram ve updates before burn table.
unsigned char
   CWOption,        // Cold warmup option - 0 = linear, 1 = custom table.
   knk_option,      // Bits 0-3: 0=no knock detection;1=operate at table value or 1 
                    // step below knock; 2=operate at table value or edge of knock.
         // Bits 4-7: 0/1 = knock signal < or > knk_thresh indicates knock occurred.
   knk_maxrtd,      // max total retard when knock occurs, (degx10), 
 knk_step1,knk_step2, // ign retard/ adv steps when 1st knock or after stopped,
                      // (degx10); step1 large to quickly retard/ stop knock
 knk_trtd,knk_tadv,   // time between knock retard, adv corrections, (secx10);
        // allows short time step to quickly retard, longer to try advancing.
 knk_dtble_adv, // change in table advance required to restart adv til knock or
                    // reach table value (0 knock retard) process, deg x10.
                    // This only applies with knk_option = 1.
 knk_ndet,cpad1;    // number knock detects required for valid detection; pad byte.
unsigned int 
 knk_maxmap,        // no knock retard above this map (kPax10).
 knk_lorpm,knk_hirpm,  // no knock retard below, above these rpms.
 knk_rpm[NO_KNKRPMS],  // tables of rpm vs knock threshhold(Vx100).
 knk_thresh[NO_KNKRPMS], 
 No_Teeth;          // Nominal (include missing) teeth for wheel decoding. (0=
                    // no wheel decoding).
unsigned char
 No_Miss_Teeth,     // Number of consecutive missing teeth.
 No_Skip_Teeth,     // Number of teeth (between tach pulses) to be skipped.
 Delay_Teeth,       // Number of teeth to delay after 1st tooth after the missing 
                    // teeth before 1st tach synch declared(< No_Teeth- Miss_Teeth)
 //  In this mode, for 16 - 2 wheel, 8 cyl,4 cycle, No_Skip_Teeth= 4,Delay_Teeth=0,
 //			
 //    \/       \/      \/      \/
 //    /\/\__ __/\/\/\/\/\/\/\/\/\/\/\/\
 //
 //    \/ => when the first tooth after all the missing teeth is input captured, 
 //  plus Delay_Teeth after, then we are at adv offset deg btdc, that is, this tooth 
 //  and every 'No_Skip_Teeth' thereafter mark the tach pulses.
 //  Note: No_Miss_Teeth must be < No_Skip_Teeth. Also, Delay_Teeth must be such that
 //  every tach pulse tooth is a real tooth - not a missing one.
 ICISR_tmask,				// Time (msx10) after tach input capture during which further
                    // interrupts are inhibited to mask coil ring or VR noise.
 ICISR_pmask,				// % of dtpred after tach input capture during which further
                    // interrupts are inhibited to mask coil ring or VR noise.
                    // This and prior mask not applicable with wheel decoding.
 InjTestOption;   // Puts program in injector test mode: program fires both
                  // injectors according to parameters further below. when done
                  // program halts until hard or soft reset issued.
unsigned int 
 ae_lorpm,ae_hirpm; // lorpm is rpm at which normal accel enrichment just starts
  // to scale down, and is reduced to 0 at ae_hirpm. To omit scaling, set _lorpm
  // = _hirpm= very large number.
int
   ffSpkDel[2];	  // Table of fuel sensor freq(Hz) vs spk corr (degx10);
unsigned int
   InjTestNoSqrts,  // No of squirts in test/clean mode.
   InjTestPW,     // Pulse width(usec) for testing/ cleaning injectors.
   InjTestOff;    // Time(msx10) between inj test squirts.
unsigned int
   CID;           // Engine cubic in displacement
unsigned char
   alpha,beta,    // coefficients for ignition time prediction(%)
   gamma,cpad2;
} inputs1;

typedef struct {
   unsigned char
      ve_table[NO_INJ][NO_FMAPS][NO_FRPMS];
   int
      adv_table[NO_SMAPS][NO_SRPMS];
   unsigned int
      AMCNBurns;
   int
      CWPrime[NO_TEMPS],CWCPW[NO_TEMPS],CWAWEV[NO_TEMPS],CWAWC[NO_TEMPS];
   int
      MatTemps[NO_MAT_TEMPS];  // MAT temperatures for spark retard, degx10 F or C
   unsigned char
      MatSpkRtd[NO_MAT_TEMPS]; // degx10 of spark retard vs mfld air temp
   unsigned char
      XTauOption,   // Bits 0-3:
                    // 0 = no XTau algorithm; 1 = XTau for accel/ decel only;
                    // 2 = XTau for warmup also, based on initial coolant temp.
                    //  This option presently incomplete - need to make 2nd inde-
                    //  pendent loop and add 2nd XTfcor for warmup, plus add uchar
                    //  array for XTX vs clt temps. Will be completed if real need.
                    // Note X-Tau applies to both injectors. If needed (for
                    // example with dual table option), Bit 4 (=1) can be used to 
                    // apply correction only to Inj1.
      XTDecel,      // % change in XTauTable while in decel (tpsdot < -thresh) 
      XTX,XTScl,    // % fuel(1-99) going into wall puddling; tau scale factor,
                    //  1-255, to scale XTauTable to larger values.
      XTauTable[NO_FMAPS][NO_FRPMS];	 // time constant (secx100) for fuel puddle 
                                       // dissipation vs map and rpm.
   int
      XTauClt[NO_TEMPS],   // same tau, but % scale as function of coolant temp.       
      XTauMat[NO_TEMPS];	 // same tau, but % scale as function of mfld air temp
   int
      BaroVals[NO_BARS],   // Barometric pressures (kPa x 10) for baro correction table
      MatVals[NO_MATS];    // air temperatures (degx10) for air density correction table 
   char
      BaroCorDel[NO_BARS],   // Barometric corrections (+/-%) - added to eq. value
                               // to correct for varying exhaust size
      AirCorDel[NO_MATS];    // Air density orrections (+/-%) - added to eq. value
                               // to correct for varying mat sensor location
   unsigned int
      MAFFlow[NO_MAFS];    // MAF flows (mg/ secx10) for below corrections.       
   unsigned char
      MAFCor[NO_MAFS];     // Corrections to maf factor table (%) for real time tuning/ 
                           // intake mods.as function of MAFFlow. 100% = no correction.       
   unsigned int
      MAFRPM1,MAFRPM2;     // For MAFOption=2, if rpm < rpm1 use MAP only; > rpm2 use MAF
                           // only. In between, use MAP/ MAF blend.       
   unsigned char
      MAFDir,              // If = 0, use above for MAP/ MAF blend; =1, reverse - use 
                           // MAF only below rpm1 and MAP only above rpm2.
      VEIXOptn;            // =1 means use (kpa*1000)/baro instead of kpa as the index
                           // into ve, afr, spk tables. (per Ben Strader (EFI Univ), verified
                           // from Heywood, Aquino,Taylor, and Moskwa. When this done pw =
                           //   .....*kpa/1000... instead of *kpa/baro.
} inputs2;

#ifndef GCC_BUILD
#pragma ROM_VAR INP_ROM
#endif
// flash copy of inputs - initialized

#define SECTOR_BYTES         1024 // bytes
#define SECTOR_WORDS         ((int)(SECTOR_BYTES/2))
#define N_SECTORS(theStruct) ((int)((sizeof(theStruct)+SECTOR_BYTES-1)/SECTOR_BYTES))
#define N_PADDING(theStruct) ((int)(N_SECTORS(theStruct)*SECTOR_BYTES - sizeof(theStruct)))

const inputs1 in1flash EEPROM_ATTR = {
8,               // no_cyl (1-12)
3,               // no_skip_pulses (tach pulses)
0x05, 1,         // ICIgnOption,spkout_hi_lo,
31, 20, 6,       // max_coil_dur,max_spk_dur,DurAcc   msx10
{-40,-20,0,20,40},   // deltaV_table[], Vx10 = batt_voltx10 - 120
{24,  9, 0,-5,-9},   // deltaDur_table[], msx10 = correction for batt_volt
1,           // RevLimOption:0,none; 1,retard spk; 2,fuel cut
120,         // RevLimMaxRtd,   deg x 10
1,           // PredOpt	(For EDIS, toothed wheel > 12 PredOpt=0 is sufficient)
             // For EDIS keep total adv (incl. offset & cold adv) < 60 deg
300,         // rpm at which cranking through
{60,              // cold_adv_table[TEMP no = 0], deg x 10
   50,40,30,20,10,0,0,0,0},
0,                // adv_offset,   deg x 10
5500, 6000,       // RevLimRpm1,2
{{{130,             // afr_table[inj1][MAP/tps no =0][RPM no = 0], afrx10
    135,160,160,160,149,143,132,131,132,131,130},
{132,             // afr_table[inj1][MAP/tps no =1][RPM no = 0], afrx10
    137,157,157,155,149,142,132,130,129,128,127},
{134,             // afr_table[inj1][MAP/tps no =2][RPM no = 0], afrx10
    139,155,155,154,149,141,130,129,128,127,127},
{135,             // afr_table[inj1][MAP/tps no =3][RPM no = 0], afrx10
    140,152,152,150,147,140,130,129,128,127,126},
{136,             // afr_table[inj1][MAP/tps no =4][RPM no = 0], afrx10
    141,150,149,147,147,140,129,128,127,126,126},
{135,             // afr_table[inj1][MAP/tps no =5][RPM no = 0], afrx10
    138,145,143,141,141,135,128,127,126,126,126},
{134,             // afr_table[inj1][MAP/tps no =6][RPM no = 0], afrx10
    134,139,137,136,136,131,127,126,126,126,126},
{132,             // afr_table[inj1][MAP/tps no =7][RPM no = 0], afrx10
    132,135,133,133,132,130,126,125,125,125,125},
{130,             // afr_table[inj1][MAP/tps no =8][RPM no = 0], afrx10
    130,131,130,130,129,130,125,125,125,125,125},
{130,             // afr_table[inj1][MAP/tps no =9][RPM no = 0], afrx10
    129,129,128,128,127,126,125,125,125,125,124},
{130,             // afr_table[inj1][MAP/tps no =10][RPM no = 0], afrx10
    129,129,128,128,127,126,125,125,125,123,123},
{130,             // afr_table[inj1][MAP/tps no =11][RPM no = 0], afrx10
    129,129,128,128,127,126,125,125,122,122,122}},

{{130,             // afr_table[inj2][MAP/tps no =0][RPM no = 0], afrx10
    135,160,160,160,149,143,132,131,132,131,130},
{132,             // afr_table[inj2][MAP/tps no =1][RPM no = 0], afrx10
    137,157,157,155,149,142,132,130,129,128,127},
{134,             // afr_table[inj2][MAP/tps no =2][RPM no = 0], afrx10
    139,155,155,154,149,141,130,129,128,127,127},
{135,             // afr_table[inj2][MAP/tps no =3][RPM no = 0], afrx10
    140,152,152,150,147,140,130,129,128,127,126},
{136,             // afr_table[inj2][MAP/tps no =4][RPM no = 0], afrx10
    141,150,149,147,147,140,129,128,127,126,126},
{135,             // afr_table[inj2][MAP/tps no =5][RPM no = 0], afrx10
    138,145,143,141,141,135,128,127,126,126,126},
{134,             // afr_table[inj2][MAP/tps no =6][RPM no = 0], afrx10
    134,139,137,136,136,131,127,126,126,126,126},
{132,             // afr_table[inj2][MAP/tps no =7][RPM no = 0], afrx10
    132,135,133,133,132,130,126,125,125,125,125},
{130,             // afr_table[inj2][MAP/tps no =8][RPM no = 0], afrx10
    130,131,130,130,129,130,125,125,125,125,125},
{130,             // afr_table[inj2][MAP/tps no =9][RPM no = 0], afrx10
    129,129,128,128,127,126,125,125,125,125,124},
{130,             // afr_table[inj2][MAP/tps no =10][RPM no = 0], afrx10
    129,129,128,128,127,126,125,125,125,123,123},
{130,             // afr_table[inj2][MAP/tps no =11][RPM no = 0], afrx10
    129,129,128,128,127,126,125,125,122,122,122}}},
    
{180,             // warmen_table[TEMP no = 0],   % enrichment vs temp
    180,160,150,135,125,113,108,102,100},
{20,              // tpsen_table[TPS_DOT no = 0], enrichment in .1ms vs tpsdot
  50,105,150}, 
{0,               // mapen_table[TPS_DOT no = 0], enrichment in .1ms vs mapdot
  0,0,0}, 
{40,              // iacstep_table[TEMP no = 0],
  60,75,90,105,120,130,140,150,160}, 
{500,             // frpm_table[RPM no = 0] , use in VE, AFR tables
  800,1100,1400,2000,2600,3100,3700,4300,4900,5400,6000},
{700,             // srpm_table[RPM no = 0] , use in spark advance table
  900,1200,1500,2000,2600,3100,3700,4300,4900,5400,6000},
{300,             // fmap_table[MAP/tps no = 0], kPa x 10 , use for VE, AFR
  350,450,500,550,600,700,750,800,850,950,1000},
{200,             // smap_table[MAP/tps no = 0], kPa x 10 , use for spk adv
  250,300,350,400,450,500,600,700,800,900,1000},
{-400,            // temp_table[TEMP no = 0],  deg x 10
     -200,0,200,400,600,800,1000,1300,1600},
{100,              // tpsdot_table[TPS_DOT no =0],
  400,800,1540},   //    change in % x 10 per .1 sec
{0,               // mapdot_table[TPS_DOT no =0],
  0,0,0},         //    change in kPa x 10 per .1 sec
93,              // map0,         kPa x 10, value @ 0 ADC counts
2609,            // mapmax,       kPa x 10, value @ max(1023) ADC counts
0,               // clt0,         deg (C or F) x 10
100,             // cltmult,      %
0,               // mat0,         deg (C or F) x 10
100,             // matmult,      %
0,               // tps0,         adc counts
1023,            // tpsmax,       adc counts
1,               // batt0,        v x 10
297,             // battmax,      v x 10
0,               // ego0,         afr x 10
100,             // egomult,      %
93,              // baro0,        kPa x 10
2609,            // baromax,      kPa x 10
147, -47,        // bcor0,bcormult  %, slope
0,               // knock0,       v x 100
500,             // knockmax,     v x 100
0,0,             // pad bytes
50,70,25,        // PulseTol,     % tolerance for next input pulse timing during    
                 // cranking, after start/ warmup, normal running    
5,               // IdleCtl, idle: 0 = none, 1= solenoid, 2= iac stepper motor
                 //  - enabled only when moving, 3 = iac motor - always enabled.
                 // 4 = Ford PWM iac, 5 = 3 for 15 min, then = 2
25,              // IACtstep,  .1 ms units (25 gives pulse freq of 400 Hz)
0,               // IACaccstep
0,               // IACnaccstep
1,               // IACpwm_step = freq of pwm FIdle solenoid= 80 Hz* IACpwm_step. 
160,             // IACStart,  no. of steps to send at startup to put stepper 
                 //    motor at reference (wide open) position
50,              // IdleHyst amount (degx10)
100,5,           // > IAC opening (< steps) during cranking and few secs after
0,120,40,        // when startup coolant temp < IACcoldtmp (degx10),
          // then when reach (>=) IACcoldpos switch to time based ctl: interpolate
          // bet IACcoldpos and final table IAC pos for period IACcoldxt secs.
60, 20,          // PrimePU,H priming pulsewidths at min, max temps in temp_table (ms x10)
120, 40,         // CWU,CWH, crank pw at min, max temps in temp_table (ms x 10)
45,25,           // AWEV, after start warmup % enrich add-on value at min, max clt temps,
350,150,         // AWC,  after start enrichment no. of cycles at min, max clt temps
30,              // Tpsacold,  cold (-40F) accel amount in .1 ms units
130,             // AccMult,   cold (-40F) accel multiply factor (%)
200,             // TpsThresh, tpsdot threshhold for accel enrichment(change in %x10 per .1 s) 
100,             // MapThresh, mapdot threshhold for accel enrichment(change in kPax10 per .1 s) 
2,               // TpsAsync,  clock duration (in .1 sec tics) for accel enrichment
90,              // TPSDQ,  deceleration fuel cut option (%)
700,             // TPSWOT, TPS value at WOT (for flood clear), %x10
700,             // TPSOXLimit,  Max tps value (%x10) where O2 closed loop active
100,             // Tps_acc_wght, weight to be given to tpsdot for accel enrichment.
                 //  100 - Tps_acc_wght will then be given to mapdot.
1,               // BaroOption,  0=no baro, 1=baro is 1st reading of map (before cranking),
                 //   2=independent barometer
1,               // 0 = no ego;1= nb o2;2=2 nb o2;3=single wbo2;4=dual wbo2.
16,              // EgoCountCmp,  Ign Pulse counts between when EGO corrections are made
1,               // EgoStep,   % step change for EGO corrections
15,              // EgoLimit,  Upper/Lower rail limit (egocorr inside 100 +/- limit)
45,              // AFRTarget,  NBO2 afr (Vx100) determining rich/ lean
0,               // Temp_Units,    0= coolant & mat in deg F; 1= deg C
0,               // MAFOption. Bits 0-3: =0 is no MAF; =1 is MAF-only; =2 is MAF -
                 // MAP blend. Bits 4-7: =0, MAF signal on ADC0=MAP pin. Must put MAF
                 // here if MAFOption = 0. =1, MAF signal on ADC6=Baro/Ego2 pin; =2,
                 // MAF signal on ADC7 (knock pin).
0,               // DualSpkOptn; Option = 1 for 2 ignition inputs and 2 outputs. This option
                 //  uses the 2 pwm1,2 pins (TC2,TC4) for the extra ignition in, out;
                 //  therefore ** NO injector PWM ** with this option.Also, NO trigger
                 //  return, NO wheel decode, NO EDIS. Also set ISR_tmask=0,_pmask < 20.
                 // Option = 2,3 for 2 cyl COP/ 4 cyl wasted spark with normal tach pulse
                 //  on input 1, cam synch on input 2;option 2= falling, 3= rising edge for
                 //  cam synch. When cam synch occurs, fire Output 1, then fire O/P 2 on 
                 //  next tach, etc. No inj pwm, NO trig_ret, No EDIS.	Can have tooth wheel.
                 // Option = 4 is same as 2,3 but 1 toothed wheel on input 1 (input 2 
                 //  is unused). On tooth after missing tooth(teeth) fire  O/P 1, then 
                 //  fire O/P 2 after skip teeth (= next tach), etc.
                 // Option = 5 same as 4 except wheel on cam, so use 1/2 crank miss teeth.     
1400,            // FastIdle, fast idle temperature (degx10) (idle_ctl = 1 only)
1600,            // EgoTemp,  min clt temp where ego active, degx10
1300,            // RPMOXLimit,  Min rpm where O2 closed loop is active
15500,           // ReqFuel;  fuel pulsewidth (usec) at wide open throttle
4,               // Divider,   divide factor for input tach pulses
1,               // Alternate,   option to alternate injector banks
10,              // InjOpen,  Injector open time (.1 ms units)
255,             // InjPWMTim,   Time (.1 ms units) after opening to start pwm
66,              // InjPWMPd,    Injector PWM period (us)
75,              // InjPWMDty,   Injector PWM duty cycle (%)
12,              // BatFac,  Battery fuel pw correction factor (msx10)
0,               // EngStroke,  0 = 4 stroke,  1 = 2 stroke     
0,               // InjType,  0 = port injection,  1 = throttle body
8,               // NoInj,    no. of injectors (1-12)
0,0,             // OddFire smaller, larger angle between firings
50,50,75,        // Lag filter coefficients (1-100%) for Rpm,Map/Maf,Tps,
60,              // ego1,2
50,80,           // Lag filter coefficients for other adc(clt,mat,batt), knock.
0,               // AMC option
0,               // dual table option
0,               // fuel alpha-N, map blend option
0,               // ign alpha-N, map blend option(not implemented-always use 
                 //   alphaN if FuelAlphaN=1, else don't).
0,               // WBO2 AFR alpha-N, map blend option(same note as above);
45,              // AFRStoich, stoich afr (afrx10 or Vx100) for fuel used. If AFRMult= 
                 //  1, this input used to normalize AFRtarget or afr_table multiplier
                 //  in fuel pw eq.
0,               // lo rpm for alpha-N, map blend option
0,               // alpha-N hi rpm
{{1000,600,300,350,400,400}, // alpha_map_table[tps no =0][RPM nos 0-5] (kpa x 10)
{1000,650,350,400,450,450},  // alpha_map_table[tps no =1][RPM nos 0-5]
{1000,700,400,450,500,500},  // alpha_map_table[tps no =2][RPM nos 0-5]
{1000,750,450,500,550,550},  // alpha_map_table[tps no =3][RPM nos 0-5]
{1000,800,500,550,600,600},  // alpha_map_table[tps no =4][RPM nos 0-5]
{1000,800,500,550,600,600}}, // alpha_map_table[tps no =5][RPM nos 0-5]
{0,120,160,300,600,600},     // amap_tps[0 - 5] (% x 10);
{0,500,1000,1500,2000,2000},  // amap_rpm[0 - 5]
115200,                   // baud rate
900,          // MAPOXLimit, Max MAP value (kPax10) where O2 closed loop active
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},    // board types
{0,0,0,0,0,0,0}, // spr_port
{'<','<','<','<','<','<','<'}, // condition1
{'<','<','<','<','<','<','<'}, // condition2
{' ',' ',' ',' ',' ',' ',' '}, // cond12
{0,0,0,0,0,0,0},
{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},  {0,0,0,0,0,0,0},{0,0,0,0,0,0,0},
{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},  // spr pin params
1,            // accel tail duration (sec x 10).
0,            // AFRMult,= 1 means multiply fuel pw eq by AFRStoich/AFRTgt(NB)
              // = 2 means multiply fuel pw eq by AFRStoich/afr_table(WB).
20,           // accel end pwidth enrichment (ms x 10)
0,            // EgoAlg, 0=simple algorithm; 1=Prop err alg;2=PID w. Smith pred
100,20,0,     // KP,KI,KD, PID coefficients in %
10,4000,      // egoKdly1,2 = coefficients used to calculate ego transport
              //   delay (ms) = Kdly1 + Kdly2*120000 / (map(kPax10)*rpm).					 
              //   Defaults based on xpt delay of .1 s at wot, 1 s at idle
0,            // Flex fuel option - modifies pw and spk adv based on freq signal  
              //   for % alcohol.
0,            // prime_delay, (sec) 
{50, 150},  	// Table of fuel sensor freq(Hz) vs 
{100,163},    // fuel pw corr in %; 1st element is pure gas, 2nd is pure Alcohol.
10,      // AMCStep, % of AMC correction to be applied when ramve updated.
2,       // AMCdve,  AMCcorrection must be > AMCdve % to be applied for burn
200,100,   // AMCve_drpm,AMCve_dmap,  current rpm, map(kPax10) must be within
           // this tolerance of a ve table vertex for
20,        // AMCramve_dt secs before AMC correction is applied
1800,      // AMCT_thresh, min time (secs) between flash burns of ram ve table 
2,         // AMCupdate_thresh, min number of AMC ram ve updates before burn table.	           
0,         // CWOption, cold warmup option - 0 = linear, 1 = custom table.
0x00,    // knk_option: Bits 0-3: 0=no knock detection;1=operate at table value or 1  
         // step below knock; 2=operate at table value or edge of knock.
         // Bits 4-7: 0/1 = knock signal < / > knk_thresh indicates knock occurred.
100,     // knk_maxrtd, max total retard when knock, (degx10). 
30,10,   // knk_step1, _step2, ign retard/ adv steps when 1st knock or after stopped,
         // (degx10); step1 large to quickly retard/ stop knock
2,20,    // knk_trtd,_tadv, time between knock retard, adv corrections, (secx10);
         // allows short time step to quickly retard, longer to try advancing.
30,      // knk_dtble_adv, change in table advance required to restart adv til knock
         // or reach table value (0 knock retard) process, deg x10.
         // This only applies with knk_option = 1.
2,0,     // knk_ndet, number of knock detects required for valid detection; pad byte.
700,     // knk_maxmap, no knock retard above this map (kPax10).
700,3500,  // knk_lorpm,knk_hirpm,  no knock retard below, above these rpms.
500,       // knk_rpm[0-NO_KNKRPMS], tables of rpm vs knock threshhold.
1000,
2000,		 
3000,
4000,
5000,
200,       //knk_thresh[NO_KNKRPMS], Vx100 
200,                 
200,
200,
200,
200,
0,         // No_Teeth, nominal (include missing) teeth for wheel decoding. (0=
           // no wheel decoding).
2,         // No_Miss_Teeth, number of consecutive missing teeth.
9,         // No_Skip_Teeth, number of teeth (between tach pulses) to be skipped.
0,         // Delay_Teeth, number of teeth to delay after 1st tooth after the missing 
           // teeth before 1st tach synch declared (< No_Teeth- Miss_Teeth)
0,         // ICISR_tmask, time (msx10) after tach input capture during which further
           // interrupts are inhibited to mask coil ring or VR noise.
50,        // ICISR_pmask, % of dtpred after tach input capture during which further
           // interrupts are inhibited to mask coil ring or VR noise.
           // This and prior mask not applicable with wheel decoding.
0,         // InjTestOption;  Puts program in injector test mode: program fires both
           // injectors according to parameters further below. when done
           // program halts until hard or soft reset issued.
2500, 5000, // ae_lorpm,ae_hirpm, lorpm is rpm at which normal accel enrichment just 
  // starts to scale down, and is reduced to 0 at ae_hirpm. To omit scaling, set
  //  _lorpm = _hirpm= very large number.
{0,-130},  // flex fuel spk corr (degx10); 1st element for pure gas, 2nd for pure 
           // Alcohol; last is normally - to retard spk since Alch burns faster.
400,       // InjTestNoSqrts,  No of squirts in test/clean mode.
20000,     // InjTestPW,  Pulse width(usec) for testing/ cleaning injectors.
600,       // InjTestOff, Time(msx10) between inj test squirts.
350,       // CID, engine cubic in displacement.
90,80,10,0 // alpha,beta,gamma: coefficients for ignition time prediction(%).pad byte.
};
const unsigned char in1padding[N_PADDING(inputs1)] EEPROM_ATTR ={0}; 

const inputs2 in2flash EEPROM_ATTR = {
{{{26,            // ve_table[inj1][MAP/tps no =0][RPM no = 0]
   26,29,34,40,46,50,55,58,61,61,60},
{29,              // ve_table[inj1][MAP/tps no =1][RPM no = 0]
   29,32,38,46,53,56,60,63,66,65,62},
{38,              // ve_table[inj1][MAP/tps no =2][RPM no = 0]
   37,39,44,53,61,65,67,69,73,71,66},
{43,              // ve_table[inj1][MAP/tps no =3][RPM no = 0]
   42,43,45,54,63,66,69,71,75,73,67},
{48,              // ve_table[inj1][MAP/tps no =4][RPM no = 0]
   46,48,50,58,65,69,71,73,77,75,69},
{52,              // ve_table[inj1][MAP/tps no =5][RPM no = 0]
   51,52,55,62,67,71,73,75,79,77,71},
{57,              // ve_table[inj1][MAP/tps no =6][RPM no = 0]
   59,61,65,69,72,76,78,81,85,85,80},
{61,              // ve_table[inj1][MAP/tps no =7][RPM no = 0]
   62,65,69,72,75,79,82,85,89,88,84},
{65,              // ve_table[inj1][MAP/tps no =8][RPM no = 0]
   66,69,73,76,78,82,86,90,93,92,88},
{68,              // ve_table[inj1][MAP/tps no =9][RPM no = 0]
   70,73,78,81,83,86,90,94,98,97,93},
{72,              // ve_table[inj1][MAP/tps no =10][RPM no = 0]
   77,82,87,90,93,95,100,105,109,108,103},
{74,              // ve_table[inj1][MAP/tps no =11][RPM no = 0]
   81,86,91,95,97,100,105,111,114,113,108}},
       
{{26,             // ve_table[inj2][MAP/tps no =0][RPM no = 0]
   26,29,34,40,46,50,55,58,61,61,60},
{29,              // ve_table[inj2][MAP/tps no =1][RPM no = 0]
   29,32,38,46,53,56,60,63,66,65,62},
{38,              // ve_table[inj2][MAP/tps no =2][RPM no = 0]
   37,39,44,53,61,65,67,69,73,71,66},
{43,              // ve_table[inj2][MAP/tps no =3][RPM no = 0]
   42,43,45,54,63,66,69,71,75,73,67},
{48,              // ve_table[inj2][MAP/tps no =4][RPM no = 0]
   46,48,50,58,65,69,71,73,77,75,69},
{52,              // ve_table[inj2][MAP/tps no =5][RPM no = 0]
   51,52,55,62,67,71,73,75,79,77,71},
{57,              // ve_table[inj2][MAP/tps no =6][RPM no = 0]
   59,61,65,69,72,76,78,81,85,85,80},
{61,              // ve_table[inj2][MAP/tps no =7][RPM no = 0]
   62,65,69,72,75,79,82,85,89,88,84},
{65,              // ve_table[inj2][MAP/tps no =8][RPM no = 0]
   66,69,73,76,78,82,86,90,93,92,88},
{68,              // ve_table[inj2][MAP/tps no =9][RPM no = 0]
   70,73,78,81,83,86,90,94,98,97,93},
{72,              // ve_table[inj2][MAP/tps no =10][RPM no = 0]
   77,82,87,90,93,95,100,105,109,108,103},
{74,              // ve_table[inj2][MAP/tps no =11][RPM no = 0]
   81,86,91,95,97,100,105,111,114,113,108}}},
   
{{157,             // adv_table[MAP/tps no=0][RPM no=0], deg x 10
    175,200,286,328,375,370,375,380,380,380,380},
{157,             // adv_table[MAP/tps no=1][RPM no=0], deg x 10
    178,201,282,324,370,370,370,375,380,380,380},
{156,             // adv_table[MAP/tps no=2][RPM no=0], deg x 10
    180,202,278,324,368,370,375,375,380,380,380},
{155,             // adv_table[MAP/tps no=3][RPM no=0], deg x 10
    182,204,274,323,364,370,370,375,375,380,380},
{155,             // adv_table[MAP/tps no=4][RPM no=0], deg x 10
    184,206,272,322,360,368,374,374,376,376,380},
{157,             // adv_table[MAP/tps no=5][RPM no=0], deg x 10
    186,207,268,321,360,366,372,375,375,375,375},
{158,             // adv_table[MAP/tps no=6][RPM no=0], deg x 10
    188,208,258,320,360,365,365,365,370,370,370},
{160,             // adv_table[MAP/tps no=7][RPM no=0], deg x 10
    185,205,250,317,358,360,362,362,362,362,362},
{160,             // adv_table[MAP/tps no=8][RPM no=0], deg x 10
    183,203,241,308,353,360,360,360,360,360,360},
{155,             // adv_table[MAP/tps no=9][RPM no=0], deg x 10
    175,200,235,299,348,360,360,360,360,360,360},
{151,             // adv_table[MAP/tps no=10][RPM no=0], deg x 10
    172,195,228,295,343,360,360,360,360,360,360},
{148,             // adv_table[MAP/tps no=11][RPM no=0], deg x 10
    168,190,216,282,335,360,360,360,360,360,360}},
 0,               // AMCNBurns
{ 60, 56, 52, 48, 44, 40, 36, 32, 26, 20},   // CWPrime, (msx10)
{120,112,104, 96, 88, 80, 72, 64, 52, 40},   // CWCPW,   (msx10)
{ 45, 43, 41, 39, 37, 35, 33, 31, 28, 25},   // CWAWEV,  %
{350,330,310,290,270,250,230,210,180,150},   // CWAWC,   cycles
{1600,1800,2000,2200,2400,2600},        // MatTemps, degx10 F or C
{0,0,20,40,60,80},                      // MatSpkRtd, degx10 
0,   // XTauOption:  0 = no XTau algorithm; 1 = XTau for accel/ decel only;
                  // 2 = XTau for warmup also, based on initial coolant temp.
                  //  This last option presently incomplete/ unused.
100, // XTDecel, % change in XTauTable while in decel (tpsdot < thresh) 
30,                   // XTX = % fuel going into wall puddling;
1,  			            // tau scale factor,1-255 to scale XTauTable.
{{ 19,										// XTauTable[MAP/tps no=0][RPM no=0], secx100
     15, 12, 9, 6, 5, 4, 3, 3, 2, 2, 1},
 { 21,										// XTauTable[MAP/tps no=1][RPM no=0], secx100
     19, 13, 10, 7, 6, 5, 4, 3, 3, 2, 1},
 { 22,										// XTauTable[MAP/tps no=2][RPM no=0], secx100
     19, 15, 12, 9, 8, 6, 5, 4, 3, 2, 2},
 { 23,										// XTauTable[MAP/tps no=3][RPM no=0], secx100
     20, 16, 13, 10, 9, 7, 5, 4, 3, 2, 2},
 { 24,										// XTauTable[MAP/tps no=4][RPM no=0], secx100
     21, 17, 14, 11, 10, 8, 6, 4, 4, 3, 2},
 { 26,										// XTauTable[MAP/tps no=5][RPM no=0], secx100
     23, 18, 15, 13, 11, 9, 7, 5, 4, 3, 2},
 { 27,										// XTauTable[MAP/tps no=6][RPM no=0], secx100
     24, 19, 17, 14, 12, 10, 7, 5, 4, 3, 2},
 { 28,										// XTauTable[MAP/tps no=7][RPM no=0], secx100
     25, 20, 18, 15, 13, 11, 8, 6, 5, 3, 3},
 { 29,										// XTauTable[MAP/tps no=8][RPM no=0], secx100
     26, 21, 19, 16, 14, 12, 8, 6, 5, 4, 3},
 { 30,										// XTauTable[MAP/tps no=9][RPM no=0], secx100
     27, 22, 21, 18, 15, 13, 9, 6, 5, 4, 3},
 { 31,										// XTauTable[MAP/tps no=10][RPM no=0], secx100
     28, 23, 22, 19, 16, 14, 10, 7, 6, 4, 3},
 { 33,										// XTauTable[MAP/tps no=11][RPM no=0], secx100
     29, 24, 23, 20, 17, 15, 10, 7, 6, 4, 3}},
{20000,15000,10000,8000,5800,4800,3600,2400,1200,100},  // XTauClt[NO_TEMPS], %       
{140,136,126,116,110,106,104,102,100,100},     // XTauMat[NO_TEMPS], %
{    // BaroVals[NO_BARS]: barometric pressures, (kPa x 10)) for baro correction table
  800, 900, 1000, 1050, 1100, 1200 }, 
{    // MatVals[NO_MATS]: air temperatures (degx10) for air density correction table 
  400, 600, 1000, 1300, 1600, 1800 },
{    // BaroCorDel[NO_BARS], barometric correction table(%) - added to eq. value
  0, 0, 0, 0, 0, 0 }, 
{    // AirCorDel[NO_MATS], air density correction table(%) - added to eq. value
  0, 0, 0, 0, 0, 0 },
{    // MAFFlow[NO_MAFS], MAF flows (mg/ secx10) for below corrections.
  100, 2000, 4000, 6000, 8000, 10000, 12000, 14000, 16000, 18000, 20000, 25000 }, 
{    // MAFCor[NO_MAFS], Corrections to maf factor table (%) for real time tuning/ 
  100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 },   // intake mods.as 
                              // function of MAFFlow. 100% = no correction.       
1200, 3000,      // MAFRPM1,MAFRPM2. For MAFOption=2, if rpm < rpm1 use MAP only; 
                 // > rpm2 use MAF only. In between, use MAP/ MAF blend.(see next).       
0,               // MAFDir: If = 0, use above for MAP/ MAF blend; =1, reverse 
                 // - use MAF only below rpm1 and MAP only above rpm2. 
0                // VEIXOptn =1 means use (map*1000)/baro instead of map as the index
                 // into ve, afr, spk tables.
};    
const unsigned char in2padding[N_PADDING(inputs2)] EEPROM_ATTR ={0}; 
                                // 1024 bytes = 1 block flash
#ifndef GCC_BUILD
#pragma ROM_VAR DEFAULT

#pragma ROM_VAR OVF_ROM
#endif
// IAC stepper motor sequence
const unsigned char IACCoilA[8] = {0,0,1,1,0,0,1,1};
const unsigned char IACCoilB[8] = {1,0,0,1,1,0,0,1};
// array of TC overflow numbers at which to increment secs cntr.
const unsigned int TC_ovfla[60] = {
23,
46,
69,
92,
114,
137,
160,
183,
206,
229,
252,
275,
298,
320,
343,
366,
389,
412,
435,
458,
481,
504,
526,
549,
572,
595,
618,
641,
664,
687,
710,
732,
755,
778,
801,
824,
847,
870,
893,
916,
938,
961,
984,
1007,
1030,
1053,
1076,
1099,
1122,
1144,
1167,
1190,
1213,
1236,
1259,
1282,
1305,
1328,
1350,
1373
};
const char RevNum[20] =  {    // revision no:
 // only change for major rev and/or interface change.
  "MSII Rev 2.83000   "
},
 Signature[32] = {            // program title.
 // Change this every time you tweak a feature.
  "** V2.83Embedded Code by B&G **"
 };
#ifndef GCC_BUILD
#pragma ROM_VAR DEFAULT
#endif

// ram copy of inputs
inputs1 inpram;
inputs2 in2ram;

// sensor variables
int last_tps,last_map,tpsdot_ltch,mapdot_ltch;
// fuel variables
unsigned int pwcalc1,pwcalc2,pw_open,PrimeP,AWEV,AWC;
unsigned int Fl1OCt_overflow,Fl2OCt_overflow;
unsigned long Fl1TimerComp,Fl2TimerComp;
unsigned char pwm1_on,pwm2_on,cut_fuel;
unsigned long pwm1_time,pwm2_time;
int wrmtmp;
// ignition variables
char ICtt_ovflo; 
unsigned char SPK,CHG, pulse_no,ign_state[2],ign_setpin[2],ICintmask[2],
  OCintmask[2],IgnOCpinstate,first_edis,PulseTol,cyl_no,mask_teeth,
  last_tooth,tooth_no;
char
  ICint,OCint,cam_sync;
long adv_us,charge_time,coil_dur_set,coil_dur_out[2];
int ICt_overflow[2],IgnOCt_overflow[2],coil_dur,ic_adv_deg;
volatile unsigned short *pTIC[2],*pTOC[2];
unsigned long IgnTimerComp[2],dtpred;
// IAC variables
unsigned long motor_time;
int motor_step, IACmotor_pos,last_iacclt;
char IAC_moving,IACmotor_reset,IdleCtl;
// General variables
unsigned int mms,millisec,burn_flag,iacpwmctr;
unsigned int TC_ovflow,TC_ov_ix;
unsigned long lmms,t_enable_IC[2],t_chgoff,Rpm_Coeff,ltch_lmms,
  rcv_timeout,adc_lmms;
unsigned int asecount,mafraw;
unsigned char flocker,tpsaclk,egocount,igncount,altcount,next_adc,first_adc,
	txmode,tble_idx,burn_idx,synch,trig_ret_mode,crank_trig_mode,reinit_flag,
	egopstat,afrSL, FSensStat,knk_clk,knk_clk_test,knk_stat,knk_count,
	amc_burn_flag,blend,pump_delay;
int ego1err,ego1errm1,ego2err,ego2errm1,sego1err,sego2err,egoKPX,
  blendve1,blendve2;
unsigned int afrdl1,afrdl2,afrtgt_skip, FSens_Pd,FSensFreq;
unsigned long MAFCoef;
/* Clocks:
  	- igncount: counts up each tach pulse, cleared when hit Divider pulses
               (and injection occurs).
  	- asecount: counts up each time igncount = 0 (each Divider pulses).
  	- egocount: counts up each tach pulse, cleared when hits EgoCountCmp
  	- tpsaclk: counts every .1 sec
  	- altcount: flips 0,1,0,1... on each injection, resulting in firing alternate
                injector banks if Alternate option.
*/
unsigned int txcnt,txgoal,rxoffset,rxnbytes,rxcnt,kill_ser_t;
char AMCmode,AMCdve_change,ego_cloop,bad_ego_flag,bad_ego_ltch,XTcalc,first_clt,
  MAFPin,kill_ser;
unsigned int AMCclk,AMCve_clk,AMCNSum,AMCburn_clk,AMCram_updates,
  XTau,XTm,XTf,burn_tooth_no;
int knk_tble_adv,warmup_Tclt,ffspkdel,mafmap,kpaix,kpadiv;
long XTM;

// CAN variables
unsigned long cansendclk,ltch_CAN=0xFFFFFFFF;
char *canvar_blkptr[NO_VAR_BLKS];
unsigned short can_status;
unsigned char can_clr_stat,can_reset,can_id,getCANdat=0;
struct canmsg {
  /* CAN Xmt mssge ring buffer:
      can[0] is to hold Rx,TxISR messages,
      can[1] for main loop messages (so don't get clobbered by ISR)
      cxno = no msgs in queue waiting to be sent out
      cxno_in = index for inserting a msg in queue (incr after insert)
      cxno_out = index for sending out a msg (incr after load CAN buf)
      msg_type = CMD,REQ,RESP,XSUB (= set value, request value, respond 
          to a request for value, execute a subroutine)
      varblk,varoffset,varbyte = blk no of data structure, byte offset 
          from start of structure, no. bytes of data
      datbuf = the actual data (max of 8 bytes)
      dest = id no. of device to which msg being sent. 
  */
  unsigned char cxno,cxno_in,cxno_out;
  unsigned char cx_msg_type[NO_CANMSG], cx_varblk[NO_CANMSG], 
	  cx_dest[NO_CANMSG],cx_varbyt[NO_CANMSG];
  unsigned short cx_varoff[NO_CANMSG];
  unsigned char cx_datbuf[NO_CANMSG][8];  // max msg data = 8 bytes
}  can[2];

// pointers for spare port pins
volatile unsigned char *pPTMpin[8], *pPTTpin[8], *pPTApin0;
unsigned char dummyReg,lst_pval[NPORT];

// allocate space in ram for flash burner core
volatile unsigned char RamBurnPgm[36]; 

// rs232 Outputs to pc
typedef struct {
unsigned int seconds,pw1,pw2,rpm;           // pw in usec
int adv_deg;                                // adv in deg x 10
unsigned char squirt,engine,afrtgt1,afrtgt2;    // afrtgt in afr x 10 (or Vx100)
/* 
; Squirt Event Scheduling Variables - bit fields for "squirt" variable above
inj1:    equ    0       ; 0 = no squirt; 1 = inj squirting
inj2:    equ    1

; Engine Operating/Status variables - bit fields for "engine" variable above
ready:  equ     0       ; 0 = engine not ready; 1 = ready to run 
                                               (fuel pump on or ign plse)
crank:  equ     1       ; 0 = engine not cranking; 1 = engine cranking
startw: equ     2       ; 0 = not in startup warmup; 1 = in startw enrichment
warmup: equ     3       ; 0 = not in warmup; 1 = in warmup
tpsaen: equ     4       ; 0 = not in TPS acceleration mode; 1 = TPS acceleration mode
tpsden: equ     5       ; 0 = not in deacceleration mode; 1 = in deacceleration mode
*/

unsigned char wbo2_en1,wbo2_en2; // from wbo2 - indicates whether wb afr valid
int baro,map,mat,clt,tps,batt,ego1,ego2,knock,   // baro - kpa x 10 
                                                 // map - kpa x 10
                                                 // mat, clt deg(C/F)x 10
                                                 // tps - % x 10
                                                 // batt - vlts x 10
                                                 // ego1,2 - afr x 10
                                                 // knock - volts x 100
 egocor1,egocor2,aircor,warmcor,                 // all in %
 tpsaccel,tpsfuelcut,barocor,gammae,             // tpsaccel - acc enrich(.1 ms units)
                                                 // tpsfuelcut - %
                                                 // barocor,gammae - %
 vecurr1,vecurr2,iacstep,cold_adv_deg,           // vecurr - %
                                                 // iacstep - steps
                                                 // cold_adv_deg - deg x 10
tpsdot,mapdot,                                   // tps, map rate of change - %x10/.1 sec,
                                                 // kPax10 / .1 sec
coil_dur;                                        // msx10 coil chge set by ecu
unsigned int maf;                                // maf (mg/secx10)
int kpa,																				 // kpa (=map,mafmap or tps)
  fuelcor;                                       // fuel composition correction - %
unsigned char port_status,                       // Bits indicating spare port status.
  knk_rtd;               // amount of ign retard (degx10) subtracted from normal advance.
int XTfcor;            // X,tau fuel correction,%, to ensure correct amount of fuel gets
                         //  to the combustion chamber.
int egoV1,egoV2;				                         // ego sensor readbacks in Vx100
unsigned int amc_updates;
int spare[13];
unsigned char ospare,cksum;
unsigned long dt3;                               // delta t bet. rpm pulses (us)
} variables;

variables outpc, txbuf;

#ifndef GCC_BUILD
#pragma ROM_VAR OVF_ROM
#endif

typedef struct {
   unsigned int *addrRam;
   unsigned int *addrFlash;
   unsigned int  n_bytes;
} tableDescriptor;

const tableDescriptor tables[NO_TBLES] =  { 
  { NULL,                    (unsigned int *)cltfactor_table, sizeof(cltfactor_table) }, 
  { NULL,                    (unsigned int *)matfactor_table, sizeof(matfactor_table) }, 
  { NULL,                    (unsigned int *)egofactor_table, sizeof(egofactor_table) }, 
  { NULL,                    (unsigned int *)maffactor_table, sizeof(maffactor_table) }, 
  { (unsigned int *)&inpram, (unsigned int *)&in1flash,       sizeof(inputs1)         }, 
  { (unsigned int *)&in2ram, (unsigned int *)&in2flash,       sizeof(inputs2)         },
  { (unsigned int *)&txbuf,  NULL,                            sizeof(txbuf)           } 
};

#ifndef GCC_BUILD
#pragma ROM_VAR DEFAULT
#endif

#define tableInit(iTable) (void)memcpy(tables[iTable].addrRam, tables[iTable].addrFlash, tables[iTable].n_bytes)
#define tableByteRam(iTable, iByte)   ((unsigned char *)tables[iTable].addrRam + iByte)
#define tableWordRam(iTable, iWord)   (tables[iTable].addrRam + iWord)
#define tableByteFlash(iTable, iByte) ((unsigned char *)tables[iTable].addrFlash + iByte)
#define tableWordFlash(iTable, iWord) (tables[iTable].addrFlash + iWord)
#define tableBytes(iTable)            (tables[iTable].n_bytes)
#define tableWords(iTable)            ((tables[iTable].n_bytes+1)/2) // Round up

// Prototypes - Note: ISRs prototyped above.
void main(void) FAR_TEXT1_ATTR;
void ign_reset(void);
void get_adc(char chan1, char chan2);
int move_IACmotor(void) TEXT1_ATTR;
void fburner(unsigned int* progAdr, unsigned int* bufferPtr, 
  					 unsigned int no_words) TEXT1_ATTR;
int barocor_eq(int baro) TEXT1_ATTR;
int aircor_eq(int mat) TEXT1_ATTR;
void set_spr_port(char port, char val) TEXT1_ATTR;
int coil_dur_table(int delta_volt) TEXT1_ATTR;
int knk_thresh_calc(unsigned int rpm) TEXT1_ATTR;
int intrp_2ditable(unsigned int x, int y, unsigned char nx, unsigned char ny,
  unsigned int * x_table, int * y_table, int * z_table) TEXT1_ATTR;
void CanInit(void) TEXT1_ATTR;
void can_xsub01(void) FAR_TEXT1_ATTR;
void Flash_Init(unsigned long oscclk) TEXT1_ATTR;
void Flash_Erase_Sector(unsigned int *address) FAR_TEXT1_ATTR;
void Flash_Write_Word(unsigned int *address, unsigned int data) TEXT1_ATTR;

#ifndef GCC_BUILD
#pragma CODE_SEG OTHER_ROM
#endif

void tpsaen(void);
void ego_calc(void);
void amc(void);
unsigned char afrLF_calc(long t) TEXT1_ATTR; 

#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT
#endif

extern void reboot(void) FAR_TEXT1_ATTR;
extern void monitor(void) FAR_TEXT1_ATTR;
extern void SpSub(void);
extern void NoOp(void);

void main(void) {
int ix;
int tmp1,tmp2,tmp3,tmp4;
unsigned int utmp1,utmp2,utmp3,ppw[2];
long lsum,lsum1,lsum2,beta,ltmp;
unsigned long ultmp,ultmp2;
int start_clt;
unsigned int tcrank_done,tcold_pos,tASTol;
char ctmp1,ctmp2,XTau0,firstIAC;
unsigned char uctmp,mx_bnk;

  PPAGE = 0x3C;
  // initalize PLL - reset default is Oscillator clock
  // 8 MHz oscillator, PLL freq = 48 MHz, 24 MHz bus, 
  //  divide by 16 for timer of 2/3 usec tic
  PLLCTL &= 0xBF;     // Turn off PLL so can change freq
  SYNR = 0x02;        // set PLL/ Bus freq to 48/ 24 MHz
  REFDV = 0x00;
  PLLCTL |= 0x40;     // Turn on PLL
  // wait for PLL lock
  while (!(CRGFLG & 0x08));
  CLKSEL = 0x80;      // select PLL as clock
  // wait for clock transition to finish
  for (ix = 0; ix < 60; ix++);
    
  // open flash programming capability
  Flash_Init(8000);
  //inp_spare used to force inpflash, flashve_table into sectors.
  // Must use in program or it won't use up the entire sector. POS CW 
  // ignores the pragma making this stupid statement necessary.
  ix = in1padding[0];
  ix = in2padding[0];
  if((int)RamBurnPgm & 0x0001)	{   // odd address - cpy to even one
    (void)memcpy((void *)RamBurnPgm,NoOp,1);         // cpy noop to 1st location
    (void)memcpy((void *)&RamBurnPgm[1],SpSub,32);   // cpy flashburn core pgm to ram
  }
  else
    (void)memcpy((void *)RamBurnPgm,SpSub,32);       // cpy flashburn core pgm to ram

  // load all user inputs from Flash to RAM
  tableInit(4);
  tableInit(5);
  
  // set up i/o ports
  //    - port M2 is fast idle solenoid
  //    - port M3 is inj led
  //    - port M4 is accel led
  //    - port M5 is warmup led
  //    - port E0 is flex fuel sensor input
  //    - port E4 is fuel pump
  //    - port P5 is bootload pin (input)
  //    - port T6 is IAC Coil A
  //    - port T7 is IAC Coil B
  //    - port B4 is IAC Enable
  //    - port A0 is Knock Enable (if set, means retard timing)
  DDRM |= 0xFC;    // port M - all outputs, full drive by default
  DDRE |= 0x10;	   // port E4 - output, E0 is input
  DDRT |= 0xC0;    // port T6-7 - outputs
  DDRB |= 0x10;    // port B4 - output
  DDRA |= 0x01;    // port A0 - output
  //  Set pointers to real port addresses
  for(ix = 0; ix < 8; ix++)  {
    pPTMpin[ix] = pPTM;
    pPTTpin[ix] = pPTT;
  }
  pPTApin0 = pPORTA;
  // reset those pointers to pins which are to be used as alternate outputs
  outpc.port_status = 0;
  for(ix = 0;ix < NPORT; ix++)  {
    if(inpram.spr_port[ix])  {
       // pin output from normal function goes to dumy reg in ram
      if(ix < 4)
        pPTMpin[ix + 2] = &dummyReg;
      else if(ix < 6)
        pPTTpin[ix + 2] = &dummyReg;
      else
        pPTApin0 = &dummyReg;
      set_spr_port((char)ix, inpram.init_val[ix]);
      lst_pval[ix] = inpram.init_val[ix];
    }
  }
  // turn off fidle solenoid, leds
  *pPTMpin[2] &= ~0x04;
  *pPTMpin[3] &= ~0x08;
  *pPTMpin[4] &= ~0x10;
  *pPTMpin[5] &= ~0x20;
  PORTE &= ~0x10;            // turn off fuel pump
  *pPTTpin[6] &= ~0xC0;      // turn off IAC coils (do both pins[6,7] in 1 cmd)
  if(inpram.IdleCtl == 2)  {
    PORTB |= 0x10;   // disable current to motor (set bit= 1)
  }
  else  {    //  IdleCtl = 3, 5 or no stepper motor (set bit = 0)
    PORTB &= ~0x10;  // enable current to motor always(set bit= 0)
  }
  *pPTApin0 &= ~0x01;    // no knock signal
  
  // set all unused (even unbonded) ports to inputs with pullups
  DDRA &= 0x01;
  DDRB &= 0x10;
  DDRE &= 0x10;
  PUCR |= 0x13;    // enable pullups for ports E, B and A
  DDRP = 0x00;
  PERP = 0xFF;     // enable pullup resistance for port P 
  DDRJ &= 0x3F;
  PERJ |= 0xC0;    // enable pullup resistance for port J6,7
  DDRS &= 0xF3;
  PERS |= 0x0C;    // enable pullup resistance for port S2,3

  reinit_flag = 0;

  // set up CRG RTI Interrupt for .128 ms clock. CRG from 8MHz oscillator.
  mms = 0;        // .128 ms tics
  millisec = 0;   // 1.024 ms clock (8 tics) for adcs
  lmms = 0;
  cansendclk = 7812;
  ltch_lmms = 0;
  outpc.seconds = 0;   // (1.0035) secs
  burn_flag = 0;
  amc_burn_flag = 0;
  RTICTL = 0x10;   // load timeout register for .128 ms (smallest possible)
  CRGINT |= 0x80;  // enable interrupt
  CRGFLG = 0x80;   // clear interrupt flag (0 writes have no effect)

  // Set up SCI (rs232): SCI BR reg= BusFreq(=24MHz)/16/baudrate
//inpram.baud = 115200; // Use this for debugging corrupted flash sectors.
  SCI0BDL = (unsigned char)(1500000/inpram.baud);
  ltmp = (150000000/inpram.baud) - ((long)SCI0BDL*100);
  if(ltmp > 50)SCI0BDL++;   // round up 
//inpram.baud = in1flash.baud; // See above.
  SCI0CR1 = 0x00;
  SCI0CR2 = 0x24;   // TIE=0,RIE = 1; TE=0,RE =1
  txcnt = 0;
  rxcnt = 0;
  txmode = 0;
  txgoal = 0;
  kill_ser = 0;
  rcv_timeout = 0xFFFFFFFF;

  //  Initialize timers:
  //   Note: Inj OC and PWM are Nanded.  
  //    -when turn on inj1 i/o, also enable pwm1 @ 100 % duty & 
  //      set pwm1 timer = 0 (.1 ms units).
  //    -When pwm1 timer = InjPWMTim, set duty cycle to InjPWMDty.
  //    -when done injecting, turn off inj1 i/o and pwm1. 
  //   TC0: Input capture (tach) - no pullup (default) for MSII
  //   TC1: Output compare for injector output - bank 1
  //   TC2: PWM2 for injector bank 1 (or IC for 2nd ignition input)
  //   TC3: Output compare for injector output - bank 2
  //   TC4: PWM4 for injector bank 2 (or OC for 2nd ignition output)
  //   TC5: Ouput compare for 1st ignition output
  SPK = 1 - inpram.spkout_hi_lo;  // 0=spk low, but inverted logic
  CHG = 1 - SPK;                  // after transistor
  // Set prescaler to 16. This divides bus clk (= PLLCLK/2 = 24 MHz)
  //   by 16. This gives 1.5 MHz timer, 1 tic = 2/3 us.
  TSCR2 = 0x04;
  TSCR2 |= 0x80;    // enable timer overflow interrupt (TOI)
  pTIC[0] = pTC0;
  ICintmask[0] = 0x01;
  pTOC[0] = pTC5;
  OCintmask[0] = 0x20;
  if(!inpram.DualSpkOptn) {
    TIOS |= 0x3E; // Timer ch 0 = IC, ch 1-5 = OC & PWM, 
                   // ch 6,7 = I/O output
    TCTL2 |= 0x88;   // bit OM1,3,5 = 1. OC output line high or 
	                 // low iaw OL1,3,5 (not toggle or disable)
    TCTL1 |= 0x08;
    TSCR2 |= 0x80;   // enable timer overflow interrupt (TOI)
    // ensure not charging ignition coil
    TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
    CFORC |= 0x20;           // force output in OL5 onto OC pin
  
    // Set up injector PWMs - PWM2, 4
    MODRR = 0x14;     // Make Port T pins 2,4 be PWM
    PWME = 0;         // disable pwms initially
    PWMPOL = 0x14;    // polarity = 1, => go hi when start
    PWMCLK = 0x14;    // select scaled clocks SB, SA
    PWMPRCLK = 0x00;  // prescale A,B clocks = bus = 24 MHz
    PWMSCLA = 0x0C;   // pwm clk = SA clk/(2*SCLA) = 24MHz/24 = 1 us clk
    PWMSCLB = 0x0C;   // pwm clk = SB clk/(2*SCLB) = 24MHz/24 = 1 us clk
    PWMCAE = 0x00;    // standard left align pulse: -----
                      //                           |     |______
                      //                             duty
                      //                           <---period---> 
    PWMPER2 = inpram.InjPWMPd;   // set PWM period (us)
    PWMPER4 = inpram.InjPWMPd;   // set PWM period (us)
    pTIC[1] = pTC0;
    ICintmask[1] = ICintmask[0];
    pTOC[1] = pTC5;
    OCintmask[1] = OCintmask[0];
  } 
  else  {
    PWME = 0;         // disable pwms with dual/wasted/COP spark option
    pwm1_time = 0xFFFFFFFF;
    pwm2_time = 0xFFFFFFFF;
    TIOS |= 0x3A;     // Timer ch 0,2 = IC, ch 1,3,4,5 = OC, 
                      // ch 6,7 = I/O output
    TCTL2 |= 0x88;    // bit OM1,3,4,5 = 1. OC output line high or 
	                    // low iaw OL1,3,4,5 (not toggle or disable)
    TCTL1 |= 0x0A;
    // ensure not charging ignition coils
    TCTL1 = (TCTL1 & 0xFA) | SPK | (SPK << 2);  // set OC o/p in OL4, OL5
    CFORC |= 0x30;    // force output in OL4,OL5 onto OC pin
    pTIC[1] = pTC2;
    if(inpram.DualSpkOptn < 4)
      ICintmask[1] = 0x04;
    else
      ICintmask[1] = ICintmask[0];
    pTOC[1] = pTC4;
    OCintmask[1] = 0x10;
  }

  ign_reset();
  first_edis = 1;
  
  // IACStart = enough steps to set IAC wide open.
  //  Set current IAC position to IACStart (all way closed), then move
  //  to the 0 position (wide open - fast idle) and zero out. After 
  //  this all subsequent IAC commands will gradually close air passage
  //  as clt temp rises.
  //  Note: Ignore all move commands until finished current move.
  IdleCtl = inpram.IdleCtl;
  firstIAC = 1;
  IAC_moving = 0;
  motor_step = -1;
  if(IdleCtl == 4) IACmotor_reset = 1;
     else IACmotor_reset = 0;
  outpc.iacstep = inpram.IACStart;  // set current motor step position
                       //  to closed since don't have actual position.
  IACmotor_pos = 0;    // command motor step position to wide open
  if((IdleCtl > 1) && (IdleCtl != 4))
    (void)move_IACmotor();
  last_iacclt = -3200;
  iacpwmctr = 0;

  // set up ADC processing
  // ATD0
  //    - AN0 is MAP or MAF (MAFOption = 1, MAFPin = 0)
  //    - AN1 is MAT
  //    - AN2 is CLT
  //    - AN3 is TPS
  //    - AN4 is BAT
  //    - AN5 is EGO1 (NB or WB)
  //    - AN6 is Spare or BARO (BaroOption =2) or EGO2 (EgoOption = 2 or 4)
  //          or MAF (MAFOption = 2, MAFPin = 1).
  //    - AN7 is Spare or KNOCK or MAF (MAFOption = 2, MAFPin = 2).
  // Set up ADCs so they continuously convert, then read result registers
  //  every millisecond
  MAFPin = (inpram.MAFOption & 0xF0) >> 4;
  next_adc = 0;	   	// specifies next adc channel to be read
  ATD0CTL2 = 0x40;  // leave interrupt disabled, set fast flag clear
  ATD0CTL3 = 0x00;  // do 8 conversions/ sequence
  ATD0CTL4 = 0x67;  // 10-bit resoln, 16 tic cnvsn (max accuracy),
                    // prescaler divide by 16 => 2/3 us tic x 18 tics
  ATD0CTL5 = 0xB0;  // right justified,unsigned, continuous cnvsn,
                    // sample 8 channels starting with AN0
  ATD0CTL2 |= 0x80;  // turn on ADC0
  // wait for ADC engine charge-up or P/S ramp-up
  for(ix = 0; ix < 160; ix++)  {
    while(!(ATD0STAT0 >> 7));	 // wait til conversion complete
    ATD0STAT0 = 0x80;
  }
  // get all adc values
  first_adc = 1;
  get_adc(0,7);
  first_adc = 0;
  if((inpram.BaroOption == 0) || (inpram.MAFOption == 1))
    outpc.baro = 1000;         // kPa x 10
  else if(inpram.BaroOption == 1)	 {
    outpc.baro = outpc.map;    // kPa x 10
    // limit baro in case reset while car running
    if(outpc.baro < 800)
      outpc.baro = 800;
    else if(outpc.baro > 1200)
      outpc.baro = 1200;
  }
  adc_lmms = lmms;
  // Initialize variables
  flocker = 0;  
  if(!inpram.DualSpkOptn)  {
    if(inpram.EngStroke == 0)       // 4 stroke
      Rpm_Coeff = 120000000;
    else                            // 2 stroke
      Rpm_Coeff = 60000000;
  } 
  else  {								            // Dual spark
      Rpm_Coeff = 120000000;
  }
  Rpm_Coeff /= inpram.no_cyl;
  pw_open = inpram.InjOpen;
  pwcalc1 = 0;    // us
  outpc.pw1 = pwcalc1;
  pwcalc2 = 0;    // us
  outpc.pw2 = pwcalc2;
  pump_delay = 2 + inpram.prime_delay;		 // sec
  XTM = 0;
  XTcalc = 1;
  outpc.adv_deg = 0;    // crank deg x 10
  ic_adv_deg = outpc.adv_deg - inpram.adv_offset;
  coil_dur = inpram.max_coil_dur;   // msx10
  coil_dur_set = coil_dur * 100;    // us
  outpc.coil_dur = coil_dur;        // msx10
  tASTol = 0;
  PulseTol = inpram.CrnkTol;
  outpc.kpa = outpc.map;  // kPa x 10
  kpaix = outpc.kpa;
  if(!in2ram.VEIXOptn)
    kpadiv = outpc.baro;
  else
    kpadiv = 1000;
  last_tps = outpc.tps; // % x 10
  if((inpram.MAFOption & 0x0F) != 1)
    last_map = outpc.map;     // kPa x 10
  else
    last_map = 1000;          // kpa x 10
  if(inpram.EgoOption <= 2)
    outpc.afrtgt1 = 45;   // Vx100
  else
    outpc.afrtgt1 = 147;  // afr x 10
  outpc.afrtgt2 = outpc.afrtgt1;
  outpc.aircor = 100;
  outpc.vecurr1 = 100;
  outpc.vecurr2 = outpc.vecurr1;
  outpc.barocor = 100;
  outpc.warmcor = 100;
  outpc.cold_adv_deg = 0;  // crank deg x 10
  outpc.wbo2_en1 = 1;
  outpc.wbo2_en2 = 1;
  outpc.egocor1 = 100;
  outpc.egocor2 = 100;
  outpc.tpsfuelcut = 100;
  outpc.gammae = 100;
  outpc.XTfcor = 100;
  outpc.tpsaccel = 0;
  tcrank_done = 0xFFFF;
  tcold_pos = 0xFFFF;
  FSensStat = 0;
  FSens_Pd = 0;
  outpc.fuelcor = 100;    // %
  ffspkdel = 0;           // degx10
  AMCmode = 0;
  AMCclk = 0;
  ego_cloop = 0;
  bad_ego_flag = 0;
  AMCram_updates = 0;
  outpc.amc_updates = 0;
  AMCburn_clk = outpc.seconds;
  tooth_no = 0;
  last_tooth = inpram.No_Teeth - inpram.No_Miss_Teeth;
  // best tooth to do flash erase
  if(inpram.No_Teeth > 30)
    burn_tooth_no = (3 * inpram.No_Teeth) / 4;
  else if(inpram.No_Teeth > 0)
    burn_tooth_no = last_tooth;
  else  
    burn_tooth_no = 0;
  first_clt = 1;
  MAFCoef = 6184075;
  if(inpram.CID)
    MAFCoef /= inpram.CID;

	/* set variable block addresses to be used for 
	    CAN communications */
  canvar_blkptr[0] = (char *)&inpram;
  canvar_blkptr[1] = (char *)&outpc;
  for(ix = 2;ix < NO_VAR_BLKS; ix++)  {	 // rest spares for now
    canvar_blkptr[ix] = 0;
  }
	/* Initialize CAN comms */
	can_reset = 0;
	can_id = 0;    // MS-II is processor 0
	CanInit();

  // make IC highest priority interrupt
  HPRIO = 0xEE;

  // enable global interrupts
  ENABLE_INTERRUPTS
  
  // Check for Injector Test Mode: Squirt n pulses of duration pw us, then 
  //  wait until: MT repeat cmd (will repeat the squirts) or user clears
  //  InjTestOption in MT, which will cause reboot back to normal operation.
  //  To remain in test mode, leave Test Mode on, burn ecu, and turn off power.
RESTRT_IT:
  if(inpram.InjTestOption)  {
    utmp1 = 0;
    outpc.pw1 = inpram.InjTestPW;
    outpc.pw2 = outpc.pw1;
    outpc.engine = 0x01;
    PORTE |= 0x10;	 // Turn on fuel pump
IT_SCHED:
    TIE &= 0xFE;     // disable ign IC
    // Turn On Inj1 and 2
    TCTL2 |= 0x44;   // set output hi in OL1,OL3
    CFORC |= 0x0A;   // force high
    *pPTMpin[3] |= 0x08;   // turn on inj led
    outpc.squirt |= 0x03;  // inj1,2 squirting
    PWMPER2 = inpram.InjPWMPd;   // set PWM period (us)
    PWMPER4 = inpram.InjPWMPd;   // set PWM period (us)
    PWMDTY2 = PWMPER2;           // set PWM duty to 100 %
    PWMDTY4 = PWMPER4;           // set PWM duty to 100 %  
    pwm1_on = 0;
    DISABLE_INTERRUPTS
    ultmp = lmms;
    ENABLE_INTERRUPTS
    pwm1_time = ultmp + ((inpram.InjPWMTim*8)/10); // .128 ms
    pwm2_on = 0;
    pwm2_time = ultmp + ((inpram.InjPWMTim*8)/10); // .128 ms
    PWMCNT2 = 0x00;  // clear counter
    PWMCNT4 = 0x00;  // clear counter
    PWME |= 0x14;    // enable PWM2, 4
    // Set up to turn Off Inj1,2 when get to pw us
    Fl1TimerComp = TCNT + ((3*(long)inpram.InjTestPW)>>1);  // 2/3 us
    Fl2TimerComp =  Fl1TimerComp;
    Fl1OCt_overflow = (unsigned short)(Fl1TimerComp >> 16);
    Fl2OCt_overflow = Fl1OCt_overflow; 
    TC1 = (unsigned short)(Fl1TimerComp & 0xFFFF); // load OC compare reg
    TC3 = TC1;
    if(Fl1OCt_overflow <= 1)  {
      // don't need to handle <= 1 overflow because TCNT will not
      // = TimerComp until after the overflow as long as pw < ~43 ms
		  Fl1OCt_overflow = 0;
		  Fl2OCt_overflow = 0;
      TCTL2 = TCTL2 & 0xBB;    // set output lo in OL1, OL3
      TIE |= 0x0A;             // Enable Inj1,2 OC interrupt
      TFLG1 = 0x0A;	           // clear OC interrupt flags
    }
    else  {  // Disable Inj1,2 OC interrupt until ovflows done
  	  // do below to allow enough time to set OC pin in overflow isr
  	  if(TC1 < 30)TC1 = 30;   
  	  if(TC3 < 30)TC3 = 30;   
  	  TIE &= ~0x0A;
    }

    // wait til injections done - indicated when pins go low
    while((PTIT & 0x0A));
    //  wait specified time off
    DISABLE_INTERRUPTS
    ultmp = lmms;
    ENABLE_INTERRUPTS
    ltch_lmms = ultmp;
    for (;;)  {
      while(lmms == ultmp);		// wait til increments
      if((((lmms - ltch_lmms) * 128) / 100) > inpram.InjTestOff) // msx10
        break;
      ultmp = lmms;
    }
    utmp1++;
    if(utmp1 < inpram.InjTestNoSqrts)
      goto IT_SCHED;
    PORTE &= ~0x10;	 // Turn off fuel pump
    while(inpram.InjTestOption == 1);	 // wait for user command
    if(inpram.InjTestOption > 1)  {		 // repeat test
      inpram.InjTestOption = 1;
      goto RESTRT_IT;
    } 														 
    else  {	 // Test mode turned Off - want to reboot for norm operation
      // if flash changed, reburn to 0 which is the ram value
      if(in1flash.InjTestOption) {
        // burn flash 512 byte(256 word) sector(s)
        burn_flag = 1;
        flocker = 0xCC;
        while(burn_flag <= tableWords(4))  {
          fburner(tableWordFlash(4, 0), tableWordRam(4, 0), tableWords(4));
          burn_flag++;
        }
        (void)reboot();
      }
    }
  }
  
  // Set up COP timeout for main loop
  COPCTL = 0x44;     // 131 ms timeout for reboot
  
  // Prime Pulse - shoot 1 prime pulse of length PrimeP ms x 10
  start_clt = outpc.clt;
  if(!inpram.CWOption)  {
    if(outpc.clt >= inpram.temp_table[NO_TEMPS-1])  {
      PrimeP = (unsigned short)inpram.PrimePH;
      AWEV = (unsigned short)inpram.AWEVH;
      AWC = (unsigned short)inpram.AWCH;
    }
    else if(outpc.clt <= inpram.temp_table[0])  {
      PrimeP = (unsigned short)inpram.PrimePU;
      AWEV = (unsigned short)inpram.AWEVU;
      AWC = (unsigned short)inpram.AWCU;
    }
    else {
      ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.PrimePH - 
            inpram.PrimePU);
      PrimeP = (unsigned short)(inpram.PrimePU + (ltmp / 
          (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // msx10
      ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWEVH - 
            inpram.AWEVU);
      AWEV = (unsigned short)(inpram.AWEVU + (ltmp / 
          (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // %
      ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWCH - 
            inpram.AWCU);
      AWC = (unsigned short)(inpram.AWCU + (ltmp / 
          (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // cycles
    }
  }
  else  {	     // custom warmup tables
    PrimeP = (unsigned short)CW_table(outpc.clt,in2ram.CWPrime);  // msx10
    AWEV = (unsigned short)CW_table(outpc.clt,in2ram.CWAWEV);     // %
    AWC = (unsigned short)CW_table(outpc.clt,in2ram.CWAWC);       // cycles
  }
    
  if(PrimeP)  {
  	outpc.pw1 = PrimeP * 100;       // us
  	outpc.pw2 = outpc.pw1;
  	// Turn on fuel pump
  	PORTE |= 0x10;
  	// wait prime delay secs unless start cranking
  	while((outpc.seconds < inpram.prime_delay) && !outpc.rpm);
  	// Turn On injectors & PWMs
  	TCTL2 |= 0x44;   // set outputs hi in OL1,3
  	CFORC |= 0x0A;   // force high
  	// set PWM duty (on time) to 100 %
  	if(!inpram.DualSpkOptn) {
  	  PWMDTY2 = PWMPER2;
  	  PWMDTY4 = PWMDTY2;
  	  pwm1_on = 0;
  	  pwm1_time = 0;
  	  pwm2_on = 0;
  	  pwm2_time = 0;
  	  PWMCNT2 = 0x00;
  	  PWMCNT4 = 0x00;
  	  PWME |= 0x14;     // enable PWMs
  	}
  	// Set up to turn Off injectors when get to pw us
  	Fl1TimerComp = TCNT + ((3*(long)outpc.pw1)>>1);     // 2/3 us
  	TCTL2 &= ~0x44;   // set outputs lo in OL1,3
  	Fl1OCt_overflow = Fl1TimerComp >> 16; 
  	Fl1TimerComp &= 0xFFFF;
  	Fl2TimerComp = Fl1TimerComp;
  	Fl2OCt_overflow = Fl1OCt_overflow;
  	TC1 = (unsigned short)Fl1TimerComp;     // load OC compare register
  	TC3 = (unsigned short)Fl2TimerComp;     // load OC compare register
  	if(Fl1OCt_overflow <= 1)  {
  	  Fl1OCt_overflow = 0;
  	  Fl2OCt_overflow = 0;
		  // Enable OC interrupt
		  TIE |= 0x0A;
  	}
  	else  {  // Disable OC interrupt
  		TIE &= ~0x0A;
  		TFLG1 = 0x0A;   // clear OC interrupt flag
  	}
  	// turn on inj led
  	*pPTMpin[3] |= 0x08;
  	outpc.squirt |= 0x03;   // both injectors squirting
  	outpc.engine |= 0x01;   // engine in ready to run status
  	// Note: prime pulse should be over (65 ms max) before cranking starts
  }
    
  // Set up COP timeout for main loop
  COPCTL = 0x44;     // 131 ms timeout for reboot

  //  main loop
  for (;;)  {
  
    DISABLE_INTERRUPTS
    ultmp = lmms;
    ENABLE_INTERRUPTS
    if((ultmp - adc_lmms) > 78)  {          // every 10 ms (78 x .128 ms clk)
      adc_lmms = ultmp;
      // read 10-bit ADC results, convert to engineering units and filter
      next_adc++;
      if(next_adc > 7)next_adc = 1;
      switch(next_adc)  {
        case 0:      // skip map/ maf (in Timer_Clk Int)
        case 3:      // skip tps (in Timer_Clk Int)
        case 5:      // do ego1 in ego algorithm
          break;
        case 1:
        case 2:
        case 4:
          get_adc(next_adc,next_adc);    // get one channel on each pass
          break;
        case 6:      // do ego2 in ego algorithm, maf in Timer_Clk Int
          if(inpram.BaroOption == 2)
            get_adc(next_adc,next_adc);	 // get one channel on each pass
          break;
        case 7:      // if knock option selected, do in knock algorithm,
                     //                          do maf in Timer_Clk Int
          if(!(inpram.knk_option & 0x0F) && (inpram.MAFOption != 0x22))
            get_adc(next_adc,next_adc);	 // get one channel on each pass
      }        // end switch
    }
    
  if(inpram.BaroOption == 0)  {
  	outpc.barocor = 100;
  }
  else  {                                     // barometric correction (%)
    outpc.barocor = barocor_eq(outpc.baro) + intrp_1dctable(1, outpc.baro, 
        NO_BARS, in2ram.BaroVals, 1, (unsigned char *)in2ram.BaroCorDel); 
  }
   // airdensity correction (%)
  outpc.aircor = aircor_eq(outpc.mat) + intrp_1dctable(1, outpc.mat,  
      NO_MATS, in2ram.MatVals, 1, (unsigned char *)in2ram.AirCorDel);
      
  // clear COP timeout counter
  ARMCOP = 0x55;
  ARMCOP = 0xAA;

  // If using MAF, calculate equivalent MAF pressure, mafmap (kpax10)
  if(inpram.MAFOption & 0x0F)	 {
    // calculate corrected maf here to avoid interp in ISR
    utmp1 = intrp_1dctable(0, (int)mafraw, NO_MAFS, (int *)in2ram.MAFFlow, 0, 
                                                           in2ram.MAFCor);
    asm {
      ldy  mafraw
      ldd  utmp1
      EMUL
      ldx  #100
      EDIV
      sty outpc.maf
    };
    if(outpc.rpm > 0)	 {
      mafmap = (int)(((MAFCoef / outpc.aircor) * outpc.maf) / outpc.rpm);
      if(mafmap < 100)
        mafmap = 100;
    }
    else  {
      mafmap = 1000;
    }
    if((inpram.MAFOption & 0x0F) == 2)  {
      if(in2ram.MAFDir == 0)  {		
        if(outpc.rpm <= in2ram.MAFRPM1)  {
          mafmap = outpc.map;      // use map at low rpm
          blend = 1;
        }
        else if(outpc.rpm < in2ram.MAFRPM2)	 {
        	// use maf, map blend
        	tmp1 = (in2ram.MAFRPM2 - outpc.rpm);
        	tmp2 = (outpc.rpm - in2ram.MAFRPM1);
        	tmp3 = (in2ram.MAFRPM2 - in2ram.MAFRPM1);
        	mafmap = (int)((((long)tmp1 * outpc.map) +
        	  ((long)tmp2 * mafmap)) / tmp3);
        	blend = 2;
        }
        else  {  // else leave mafmap as calculated
          blend = 3;
        }
      }
      else  {
        if(outpc.rpm > in2ram.MAFRPM2)  {
          mafmap = outpc.map;	     // use map at high rpm
          blend = 1;
        }
        else if(outpc.rpm > in2ram.MAFRPM1)	 {
        	// use maf, map blend
        	tmp1 = (outpc.rpm - in2ram.MAFRPM1);
        	tmp2 = (in2ram.MAFRPM2 - outpc.rpm);
        	tmp3 = (in2ram.MAFRPM2 - in2ram.MAFRPM1);
        	mafmap = (int)((((long)tmp1 * outpc.map) +
        	  ((long)tmp2 * mafmap)) / tmp3);
        	blend = 2;
        }
        else  {  // else leave mafmap as calculated
          blend = 3;
        }
      }
    }    // end MAFOptn = 2
  } 		 // end MAFOptions
  else
    mafmap = outpc.map;   // needed for alpha-N processing
  
  /* Determine if in Speed-density or Alpha-N mode. If in Alpha-N mode,
    set the variable "kpa" = "tps". This will not break anything, since this 
    check is performed again when multiplying MAP against the enrichments, 
    and the SCI version of the variable is MAP, not kpa.  */
    if(!(inpram.FuelAlpha & 0x0F))  {
      outpc.kpa = mafmap;                             // kpa x 10
    }
    else  {
      switch((inpram.FuelAlpha & 0xF0) >> 4)  {
      case 0:						 // use alphaN if rpm < lorpm, map or maf if rpm > hirpm
        if(outpc.rpm < inpram.alpha_lorpm)  {
    	    outpc.kpa = intrp_2ditable(outpc.rpm,outpc.tps, NO_ARPMS, NO_ATPSS,
    	      &inpram.amap_rpm[0],&inpram.amap_tps[0],&inpram.alpha_map_table[0][0]); 
    	                                                   // kpa x 10
        }
        else if(outpc.rpm > inpram.alpha_hirpm)  {
    	    outpc.kpa = mafmap;                            // kpa x 10
        }
        else  {    
    	    beta = ((long)100 * (outpc.rpm - inpram.alpha_lorpm)) / 
    		    (inpram.alpha_hirpm - inpram.alpha_lorpm);
    	    tmp4 = (short)((intrp_2ditable(inpram.alpha_lorpm,outpc.tps,NO_ARPMS,
    	      NO_ATPSS,&inpram.amap_rpm[0],&inpram.amap_tps[0],
    	          &inpram.alpha_map_table[0][0]) * (100 - beta)) / 100);
    	    outpc.kpa =(short)(tmp4 + ((mafmap * beta) / 100));
        }
        break;
      case 1:						 // use alphaN if rpm > hirpm, map or maf if rpm < lorpm
        if(outpc.rpm > inpram.alpha_hirpm)  {
    	    outpc.kpa = intrp_2ditable(outpc.rpm,outpc.tps, NO_ARPMS, NO_ATPSS,
    	      &inpram.amap_rpm[0],&inpram.amap_tps[0],&inpram.alpha_map_table[0][0]); 
    	                                                   // kpa x 10
        }
        else if(outpc.rpm < inpram.alpha_lorpm)  {
    	    outpc.kpa = mafmap;                            // kpa x 10
        }
        else  {    
    	    beta = ((long)100 * (outpc.rpm - inpram.alpha_lorpm)) / 
    		    (inpram.alpha_hirpm - inpram.alpha_lorpm);
    	    tmp4 = (short)((intrp_2ditable(inpram.alpha_hirpm,outpc.tps,NO_ARPMS,
    	      NO_ATPSS,&inpram.amap_rpm[0],&inpram.amap_tps[0],
    	          &inpram.alpha_map_table[0][0]) * beta) / 100);
    	    outpc.kpa =(short)(tmp4 + ((mafmap * (100 - beta)) / 100));
        }
        break;
      }   // end switch
    }
    if(!in2ram.VEIXOptn)  {
      kpaix = outpc.kpa;		 // baro correction not in index
      switch(inpram.MAFOption & 0x0F)  {
       case 0:        // use pw= ...../baro
        kpadiv = outpc.baro;
        break;
       case 1:        // maf only or baro, use pw= ..../1000
        kpadiv = 1000;
        break;
       case 2:
        if(blend == 1)
          kpadiv = outpc.baro;
        else if(blend == 2)
          kpadiv = (int)((((long)tmp1 * outpc.baro) +
        	    ((long)tmp2 * 1000)) / tmp3);
        else
          kpadiv = 1000;
        break;
      };  
    }
    else  {                  // baro correction in index
      asm {
        ldy  outpc.kpa
        ldd  #1000
        EMULS
        ldx  outpc.baro
        EDIVS
        sty  tmp4
      };
      if(blend == 1)  	// use (kpa*1000)/baro as index to ve, spk, tau tbles + AMC indx
        kpaix = tmp4;
      else if(blend == 2)  				// blend (kpa*1000)/baro and kpa index
        kpaix = (int)((((long)tmp1 * tmp4) +
        	  ((long)tmp2 * outpc.kpa)) / tmp3);
      else
        kpaix = outpc.kpa;
      kpadiv = 1000;         
    }

  	// check rpm for stall condition (< 50 rpm) after engine synch
  	DISABLE_INTERRUPTS
  	ultmp = lmms - ltch_lmms;			// .128 tics
  	ENABLE_INTERRUPTS
  	if(synch == END_SYNCH)  {
  	  if(ultmp > (18750/inpram.no_cyl))  {
  	    // Engine is stalled, reset
  	    ign_reset();
  	    // if coil charging, set flg to cease aft 1 s if no resynch
  	    // re-do this outside ign_reset so can force lmms is atomic
  	    DISABLE_INTERRUPTS
  	    t_chgoff = lmms + 7812;
  	    ENABLE_INTERRUPTS
  	  }
  	}
  	// if no tach pulse within pump_delay sec
  	else if(ultmp > pump_delay * (unsigned long)7812)  {
  	  PORTE &= ~0x10;   // Turn off fuel pump
  	  *pPTMpin[2] &= ~0x04;    	// Turn off fast idle ** Bug Fix By Guy Hill **
  	}

  	// check idle control
  	if(IdleCtl == 1)  {
  		if(outpc.clt < inpram.FastIdle)
  			*pPTMpin[2] |= 0x04;             // turn on fast idle solenoid
  		if(outpc.clt < inpram.FastIdle - inpram.IdleHyst)
  			*pPTMpin[2] |= 0x04;             // turn on fast idle solenoid
  		else if(outpc.clt > inpram.FastIdle)
  			*pPTMpin[2] &= ~0x04;            // turn off fast idle solenoid
  	}
  	else if(IACmotor_reset && ((IdleCtl >= 2) && (IdleCtl <= 5)))  {
  		// In cranking mode. The very first time we crank we want to
  		//  open to crankpos, but after cranking we don't want to do
  		// this because of an ignition reset (in which rpm = 0)
  		if((outpc.rpm + firstIAC) && (outpc.rpm < inpram.crank_rpm))  {
  		  tmp1 = CW_table(outpc.clt,inpram.iacstep_table);
  		  if(tmp1 > inpram.IACcrankpos)  {
  		    tmp1 = inpram.IACcrankpos;   // want IAC open at 
  		         // least this much during cranking regardless of temp
  		  }
  		  IACmotor_pos = tmp1;
  		}
  		// after cranking flare back to normal temperature dependent pos
  		else if((outpc.seconds >= tcrank_done) &&
  		         (outpc.seconds <= tcrank_done + inpram.IACcrankxt))  {
  		  // 0 steps is fully open (fast idle) and IACStart steps is fully closed
  		  tmp1 = CW_table(outpc.clt,inpram.iacstep_table);
  		  if(tmp1 > inpram.IACcrankpos) {
  		      tmp2 =(int)((long)(inpram.IACcrankpos - tmp1) * 
  		        (tcrank_done + inpram.IACcrankxt - outpc.seconds) / 
  		                                              inpram.IACcrankxt);
  		      tmp1 += tmp2;
  		  }
  		  IACmotor_pos = tmp1;
  		  firstIAC = 0;
  		}
  		//  switch to time based control if cold enough at startup
  		else if((outpc.seconds >= tcold_pos) &&
  		         (outpc.seconds <= tcold_pos + inpram.IACcoldxt))  {
  		  tmp1 = CW_table(outpc.clt,inpram.iacstep_table);
  		  if(tmp1 > inpram.IACcoldpos) {
  		        tmp2=(int)((long)(inpram.IACcoldpos - inpram.iacstep_table[NO_TEMPS-1]) 
  		          *(tcold_pos + inpram.IACcoldxt - outpc.seconds) /inpram.IACcoldxt);
  		        tmp1 = inpram.iacstep_table[NO_TEMPS-1] + tmp2;
  		  }
  		  IACmotor_pos = tmp1;
  		}
  		// check if there has been a significant change in clt temp
  		else if((outpc.clt < last_iacclt - inpram.IdleHyst) ||
  		   (outpc.clt > last_iacclt))  {
  		  IACmotor_pos = CW_table(outpc.clt,inpram.iacstep_table);
  		}
  		if(outpc.iacstep != IACmotor_pos)  {
  			  // move IAC motor to new step position
  			  if(IdleCtl != 4)  {
  			    if(move_IACmotor())
  			      last_iacclt = outpc.clt;
  			  } 
  			  else
  			    last_iacclt = outpc.clt;
  		}
  		// check if/ when to start extended time-based idle control
  		if(start_clt < inpram.IACcoldtmp)  { 
  		      if((outpc.seconds > tcrank_done) && (tcold_pos == 0xFFFF) &&
  		         (IACmotor_pos > inpram.IACcoldpos))
  		                tcold_pos = outpc.seconds;
  		}
  		if((inpram.IdleCtl == 5) && (outpc.seconds > 900))  {
  		  IdleCtl = 2;
  		  if(!IAC_moving)
  		    PORTB |= 0x10;  // disable current to stepper motor(bit=1)
  		}
   	}

  	// check for stall
  	if(((outpc.engine & 0x01) == 0) || (outpc.rpm == 0))goto BURN_FLASH;

/**************************************************************************
**
** Cranking Mode
**
** Pulsewidth is directly set by the coolant temperature to a value of
**  CWU (at temp_table[0]) and CWH (at temp_table[NO_TEMPS -1]).
**  The value is interpolated at clt.
**
**************************************************************************/
  	if(outpc.rpm < inpram.crank_rpm)  {
  		PulseTol = inpram.CrnkTol;
  		tcrank_done = 0xFFFF;
  		outpc.engine |= 0x02;    // set cranking bit
  		outpc.engine &= ~0x0C;   // clr starting warmup bit & warmup bit
  		if(outpc.tps > inpram.TPSWOT)  {
  			pwcalc1 = 300;    // usec  (.3 ms for Flood Clear)
  			pwcalc2 = pwcalc1;
  			goto KNK;
  		}
  		if(!inpram.CWOption)  {
  		  // KJW if outside the interpolation range, rail to CWU or CWH as 
  		  // appropriate, else interpolate
  		  if(outpc.clt >= inpram.temp_table[NO_TEMPS-1])
  		    pwcalc1 = (unsigned short)inpram.CWH * 100;
  		  else if(outpc.clt <= inpram.temp_table[0])
  		    pwcalc1 = (unsigned short)inpram.CWU * 100;
  		  else {
  		    ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.CWH - 
  		      inpram.CWU) * 100;
  		    pwcalc1 = (unsigned short)(inpram.CWU * (long)100 + (ltmp / 
  		      (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // usec
  		  }
  		}
  		else  {   // custom warmup table
  		  pwcalc1 = (unsigned short)CW_table(outpc.clt,in2ram.CWCPW) * 100;  // usec
  		}
  		pwcalc2 = pwcalc1;
  		goto KNK;	
  	}
  	else if(tcrank_done == 0xFFFF)  {
  	  tcrank_done = outpc.seconds;
  	  pump_delay = 2;
  	}
/**************************************************************************
**
** Warm-up and After-start Enrichment Section
**
** The Warm-up enrichment is a linear interpolated value for the current clt
** temperature from warmen_table[NO_TEMPS] vs temp_table[NO_TEMPS]. The 
** interpolation is done in subroutine warmcor_table.
**
** Also, the after-start enrichment value is calculated and applied here - it
**  is an added percent value on top of the warmup enrichment, and it is applied
**  for the number of ignition cycles specified in AWC. This enrichment starts
**  at a value of AWEV at first, then it linearly interpolates down to zero
**  after AWC cycles.
**
**  If (startw, engine is set) then:
**   compare if (AWC > 0) then:
**    interpolate for warmup enrichment
**   else clear startw bit in engine
**
**************************************************************************/
  	wrmtmp = outpc.warmcor; 
  	if(outpc.engine & 0x02)  {     // if engine cranking
  		outpc.engine &= ~0x02;       // clear crank bit
  		outpc.engine |= 0x0C;        // set starting warmup bit & warmup bit
  		asecount = 0;
  		PulseTol = inpram.ASTol;
  		tASTol = outpc.seconds;
  	}
  	wrmtmp = intrp_1dctable(1, outpc.clt, NO_TEMPS, inpram.temp_table, 0,
  	         inpram.warmen_table);    // %
  	outpc.cold_adv_deg = CW_table(outpc.clt,inpram.cold_adv_table); // deg x 10 

  	if(wrmtmp == 100)  {     // done warmup
  		outpc.engine &= ~0x0C;        // clear start warmup bit & warmup bit  		
  		*pPTMpin[5] &= ~0x20;         // clear warmup led
  		if(outpc.seconds > tASTol + 5)
  		  PulseTol = inpram.PulseTol;
  		goto END_WRM;
  	}
  	*pPTMpin[5] |= 0x20;            // set warmup led
  	outpc.engine |= 0x08;           // set warmup bit
  	if(!(outpc.engine & 0x04))      // if starting warmup bit clear
  		goto END_WRM;
  	if(asecount > AWC)  {
  		outpc.engine &= ~0x04;        // clear start warmup bit
  		if(outpc.seconds > tASTol + 5)
  		  PulseTol = inpram.PulseTol;
  		goto END_WRM;
  	}
  	if(AWC > 0)  {
  		utmp1 = AWEV * asecount;
  		wrmtmp += (AWEV - (utmp1 / AWC));
  	}
END_WRM:
    outpc.warmcor = wrmtmp;

/**************************************************************************
**
**  Throttle Position Acceleration Enrichment
**
**************************************************************************/
    
  switch_page(3,0,0);  // call tpsaen in page 3D
  
/**************************************************************************
**
**  Exhaust Gas Oxygen Sensor Measurement Section
**
**************************************************************************/

  switch_page(0,0,0);		 // call ego_calc in page 3D
  	
/**************************************************************************
**
** Automatic Mixture Control - enter once a second
**
***************************************************************************/
  if(inpram.AMCOption)  {
    switch_page(2,0,0);    // call amc in page 3D
  }			// AMC option selected

/**************************************************************************
**
** Computation of Fuel Parameters
** Note that GAMMAE only includes Warm, Tpsfuelcut, Barocor, and Aircor 
** (EGO no longer included)
**
**************************************************************************/
  	lsum = ((outpc.warmcor * outpc.tpsfuelcut) / 100);
  	lsum = (lsum * ((outpc.barocor * outpc.aircor) / 100)/100);
  	outpc.gammae = (int)lsum;
  	
  	if((inpram.FuelAlpha & 0x0F) != 2)
  	  lsum2 = (long)outpc.kpa;
  	else
  	  lsum2 = 1000;	         // normalizes to ~1 when divide by baro or 1000
  	if((inpram.MAFOption & 0x0F) == 1)	 {	 // pure maf
  	  blendve1 = 100;
  	  blendve2 = 100;
  	}
  	else  {	                                 // pure map or maf/ map blend								 
      // Look up volumetric efficiency as function of rpm and map/ mafmap/ tps
      // (in alpha N mode) or (kpa * 1000) / baro
  	  blendve1 = intrp_2dctable(outpc.rpm, kpaix, NO_FRPMS, NO_FMAPS,
  	    &inpram.frpm_table[0], &inpram.fmap_table[0], &in2ram.ve_table[0][0][0]);  //  %
  	  if(inpram.dual_tble_optn)
  	    blendve2 = intrp_2dctable(outpc.rpm, kpaix, NO_FRPMS, NO_FMAPS,
  	      &inpram.frpm_table[0], &inpram.fmap_table[0], &in2ram.ve_table[1][0][0]); // %
  	  else
  	    blendve2 = blendve1;
      if((inpram.MAFOption & 0x0F) == 2)	 {	// maf/ map blend ve
        if(in2ram.MAFDir == 0)  {		 					// map at lo rpm
          if((outpc.rpm > in2ram.MAFRPM1) && (outpc.rpm < in2ram.MAFRPM2))	 {
        	  // use blend
        	  blendve1 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * blendve1) +
        	    ((long)(outpc.rpm - in2ram.MAFRPM1) * 100)) / 
        	    (in2ram.MAFRPM2 - in2ram.MAFRPM1));
        	  blendve2 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * blendve2) +
        	    ((long)(outpc.rpm - in2ram.MAFRPM1) * 100)) / 
        	    (in2ram.MAFRPM2 - in2ram.MAFRPM1));
          }
          else if(outpc.rpm > in2ram.MAFRPM2)	 {
            blendve1 = 100;
            blendve2 = 100;
          }
        } 
        else  {																// map at hi rpm
          if(outpc.rpm < in2ram.MAFRPM1)  {
            blendve1 = 100;
            blendve2 = 100;
          }
          else if((outpc.rpm > in2ram.MAFRPM1) && (outpc.rpm < in2ram.MAFRPM2))	 {
        	  // use blend
        	  blendve1 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * 100) +
        	    ((long)(outpc.rpm - in2ram.MAFRPM1) * blendve1)) / 
        	    (in2ram.MAFRPM2 - in2ram.MAFRPM1));
        	  blendve2 = (int)((((long)(in2ram.MAFRPM2 - outpc.rpm) * 100) +
        	    ((long)(outpc.rpm - in2ram.MAFRPM1) * blendve2)) / 
        	    (in2ram.MAFRPM2 - in2ram.MAFRPM1));
          }
        }
      }		       // end maf/ map blendve
  	}            // end pure map or maf/ map blend
  	outpc.vecurr1 = blendve1;
  	outpc.vecurr2 = blendve2;

  	lsum1 = (lsum * ((outpc.egocor1 * lsum2) / kpadiv)/100);
  	lsum1 = (lsum1 * ((outpc.vecurr1 * (long)inpram.ReqFuel)/ 100)/ 100); // usec
  	lsum2 = (lsum * ((outpc.egocor2 * lsum2) / kpadiv)/100);
  	lsum2 = (lsum2 * ((outpc.vecurr2 * (long)inpram.ReqFuel)/ 100)/ 100); // usec
  	if(inpram.AFRMult)  {			 // factor in AFRTgt if this facilitates tuning 
  	  lsum1 = (lsum1 * inpram.AFRStoich) / outpc.afrtgt1;
  	  lsum2 = (lsum2 * inpram.AFRStoich) / outpc.afrtgt2;
  	}

/**************************************************************************
**
** Calculation of Battery Voltage Correction for Injector Opening Time
**
** Injector open time is implemented as a linear function of
**  battery voltage, from 7.2 volts to 19.2 volts,
**  with 13.2 volts being the nominal operating voltage
**
** INJOPEN = injector open time at 13.2 volts in ms x 10
** BATTFAC = injector open adjustment factor 6 volts from 13.2V in ms x 10
**
**
** + (INJOPEN + BATTFAC)
** +   *
** +                     (INJOPEN)
** +                         *
** +                                       (INJOPEN - BATTFAC)
** +                                               *
** +
** ++++++++++++++++++++++++++++++++++++++++++++++++++++++
**    7.2V                 13.2V                19.2
**
**************************************************************************/
  	tmp1 = (inpram.InjOpen + inpram.BatFac) - ((inpram.BatFac * 
  				(outpc.batt - 72)) / 60);         // ms x 10
  	if(tmp1 > 0)
  	  pw_open = (unsigned int)tmp1;	 // if < 0, => bat spike, use prior val.

/**************************************************************************
**
** Calc. of Flex Fuel Sensor %alcohol and PW,spk correction (fuelcor,ffspkdel)
**
**************************************************************************/
  if(inpram.FlexFuel && (FSens_Pd > 0))  {
    FSensFreq = (int)(7812 / FSens_Pd);   // Hz, (FSens_Pd in .128 tics)
    if((FSensFreq >= 10)  || (FSensFreq <= 300))  {
      outpc.fuelcor = inpram.fuelCorr[0] + 
        (((short)(FSensFreq - inpram.fuelFreq[0]) *
        (inpram.fuelCorr[1] - inpram.fuelCorr[0])) / 
        (inpram.fuelFreq[1] - inpram.fuelFreq[0])); // %
      ffspkdel = inpram.ffSpkDel[0] + 
        (((char)(FSensFreq - inpram.fuelFreq[0]) *
        (inpram.ffSpkDel[1] - inpram.ffSpkDel[0])) / 
        (inpram.fuelFreq[1] - inpram.fuelFreq[0])); // degx10
    }
    else  {          // sensor reading bad - use default
      outpc.fuelcor = 100;                   // %
      ffspkdel = 0;                          // degx10
    }
  	lsum1 = (lsum1 * outpc.fuelcor)/ 100;           // usec
  	lsum2 = (lsum2 * outpc.fuelcor)/ 100;           // usec
  }
  else  {            // no flex fuel or fuel sensor not yet ready/ broken
    outpc.fuelcor = 100;                 // %
    ffspkdel = 0;                        // degx10
  }

/**************************************************************************
**
** Calculation of Fuel Pulse Width
**
**************************************************************************/
   	if(!in2ram.XTauOption || XTcalc)  {
  	  lsum = (pw_open + outpc.tpsaccel) * 100;     // usec
  	  if((lsum1 > 0) && !cut_fuel)  {
  	    lsum1 += lsum;      // usec
  	    if(lsum1 > 32000)lsum1 = 32000; // rail at 32 ms
  	    ppw[0] = (unsigned int)lsum1;
  	  } 
  	  else  {
  	    ppw[0] = 0;
  	  }
  	  if((lsum2 > 0) && !cut_fuel)  {
  	    lsum2 += lsum;     // usec
  	    if(lsum2 > 32000)lsum2 = 32000;
  	    ppw[1] = (unsigned int)lsum2;
  	  } 
  	  else  {
  	    ppw[1] = 0;
  	  }
  	  if(ppw[0] >= ppw[1])
  	    mx_bnk = 0;
  	  else
  	    mx_bnk = 1;
  	  if(ppw[mx_bnk] == 0)  {   // no fuel - skip x-tau
  	    pwcalc1 = 0;
  	    pwcalc2 = 0;
  	    outpc.XTfcor = 100;
  	    if(XTcalc == 2)
  	      XTcalc = 0;     // clear flag - will be reset on next tach pulse
  	    goto KNK;
  	  }
   	}

/**************************************************************************
**
**  X,Tau Transient Enrichment Section:
**    
**     fi = [ mi  -  (Mi / (tau / dltau)) ] / (1 - X)
**
**     Mi+1 = Mi + X * fi - (Mi / (tau / dltau))
**
**   where,
**          fi = total fuel injected
**
**          mi = total fuel going directly into combustion chamber (want this 
**                to be the calculated fuel)
**          
**          Mi = net fuel entering/leaving port wall puddling
**          
**          dltau = time (secx100) between tach pulses = dtpred in secx100
**
**          X = fraction of fuel injected which goes into port wall puddling
**          tau = puddle fuel dissipation time constant (secx100) as function 
**                 of map, rpm, coolant temp and air temp.
**                 
**         XTfcor = % correction to calculated fuel to ensure the calculated
**                 amount gets into the comb chamber.  
**
**************************************************************************/
   	if(in2ram.XTauOption && (XTcalc == 2))  {
   	  // calculate tau as function of map,rpm,clt,mat (in secx100)
 			tmp1 = in2ram.XTScl * intrp_2dctable(outpc.rpm, kpaix, NO_FRPMS, NO_FMAPS,
 			  &inpram.frpm_table[0], &inpram.fmap_table[0], &in2ram.XTauTable[0][0]);
 			if(in2ram.XTauOption == 2)  {
 	 			// use X-Tau for warmup based on clt temp at startup
 	 			if(first_clt)  {
 			    warmup_Tclt = CW_table(outpc.clt,in2ram.XTauClt); // %
 			    first_clt = 0;
 	 			}
 			  asm {
 			    ldy  tmp1
 			    ldd  warmup_Tclt
 			    EMULS
 			    ldx  #100
 			    EDIVS
 			    sty  tmp2
 			  };
 			}
 			else	 {            // X-Tau for accel/ decel only
 			  tmp2 = tmp1;
 			}
 			tmp3 = CW_table(outpc.mat,in2ram.XTauMat); // %
 			asm {
 			  ldy  tmp2
 			  ldd  tmp3
 			  EMULS
 			  ldx  #100
 			  EDIV
 			  sty  XTau
 			};
 			if((-tpsdot_ltch) > inpram.TpsThresh)  {
 			  utmp1 = in2ram.XTDecel;
 			  asm {
 			    ldy  XTau
 			    ldd  utmp1
 			    EMUL
 			    ldx  #100
 			    EDIV
 			    sty XTau
 			  };
 			}
 			if(XTau < 1)  {
 			  XTau = 1;
 			  XTau0 = 1;		 // continue x-tau calc with tau= 1, but make no correction.
 			} 
 			else
 			  XTau0 = 0;
 			utmp1 = pw_open * 100; 
 			XTm = ppw[mx_bnk] - utmp1;  // just use max pw to get ratio & apply to both pw
 			DISABLE_INTERRUPTS
 			ultmp = dtpred;
 			ENABLE_INTERRUPTS
 			ltmp = (XTau * (unsigned long)10000) / ultmp;  // tau / dltau
 			if(ltmp < 1)ltmp = 1;
 			ltmp = XTM / ltmp;
 			if(ltmp > XTm)
 			  ltmp = XTm;
 			// must leave beta and (int)cast below because of pos CW
 			beta = ((XTm - ltmp) * 100) / (int)(100 - in2ram.XTX);
 			if(beta > 65000)beta = 65000;
 			XTf = (unsigned int)beta;
 			utmp2 = in2ram.XTX;
 			asm {
 			  ldy  utmp2
 			  ldd  XTf
 			  EMUL
 			  ldx  #100
 			  EDIV
 			  sty utmp2
 			};
 			XTM = XTM + utmp2 - ltmp; 
 			if((XTm > 0) && !XTau0)  {
 			  asm {
 			    ldy  XTf
 			    ldd  #100
 			    EMUL
 			    ldx  XTm
 			    EDIV
 			    sty outpc.XTfcor
 			  };
 			} 
 			else
 			  outpc.XTfcor = 100;
 			if(!XTau0)
 			  ppw[mx_bnk] = XTf + utmp1;
 			// X-tau done only on max bank - the correction is then applied to other.
 			utmp2 = ppw[1-mx_bnk] - utmp1;
 			if((utmp2 == XTm) || XTau0)
 			  ppw[1-mx_bnk] = ppw[mx_bnk];
 			else  {
 			  utmp3 = (unsigned int)outpc.XTfcor;
 			  asm {
 			    ldy  utmp3
 			    ldd  utmp2
 			    EMUL
 			    ldx  #100
 			    EDIV
 			    sty utmp2
 			  };
 			  ppw[1-mx_bnk] = utmp2 + utmp1;
 			}
 			if(ppw[0] > 32000)ppw[0] = 32000; // rail at 32 ms
 			if(ppw[1] > 32000)ppw[1] = 32000; // rail at 32 ms
 			XTcalc = 0;     // clear flag - will be reset on next tach pulse
   	}
   	pwcalc1 = ppw[0];
   	pwcalc2 = ppw[1];

/**************************************************************************
**
** Calculation of Knock retard, Distributor Advance & coil charge time correction
**
**************************************************************************/
KNK:

    if(!(inpram.knk_option & 0x0F) || 
         (outpc.map > inpram.knk_maxmap) || (outpc.rpm < inpram.knk_lorpm) ||
         (outpc.rpm > inpram.knk_hirpm))	 {  // no knock correction
      outpc.knk_rtd = 0;
      goto DIST_ADV;
    }
    else  {   // latch any knocks that occur during the knk_clk_test interval
      // calculate knk_thresh
			get_adc(7,7);
      tmp1 = knk_thresh_calc(outpc.rpm);
      ix = (inpram.knk_option & 0xF0) >> 4;
      if(((ix == 0) && (outpc.knock < tmp1)) ||
       	 ((ix == 1) && (outpc.knock > tmp1)))  {
        // signal(Vx100) indicates knock occurred
        if(knk_count < 255)		 // don't want to overflow to 0
          knk_count++;
        if((knk_stat == 0) && (knk_count > inpram.knk_ndet))
          knk_clk = knk_clk_test;		// just caught knock - force 1st retard
                                    // immediately (as fast as main loop allows)
      } 
      if(knk_clk >= knk_clk_test)  {	 // time to check for retard/ adv spark
        if(knk_count > inpram.knk_ndet)	 {	  // got valid knock. req ndet consec 
                                              // knocks to be sure
          knk_clk_test = inpram.knk_trtd;
          if(knk_stat == 4)   // thought had enough retard, but need more
            knk_stat = 2;
          if(knk_stat < 2)
            uctmp = inpram.knk_step1;		// haven't stopped knock yet
          else
            uctmp = inpram.knk_step2;		// stopped once - use smaller step
          uctmp = outpc.knk_rtd + uctmp; 
          if(uctmp < inpram.knk_maxrtd)
            outpc.knk_rtd = uctmp;
          else
            outpc.knk_rtd = inpram.knk_maxrtd; 
          if(knk_stat == 0)					// 1st knock
            knk_stat = 1;
          else if(knk_stat == 2)	{  // this is knk returning due to too much adv
            if((inpram.knk_option & 0x0F) == 1)	 // operate one step below knock
              knk_stat = 3;
          }
        } 
        else  {				              // did not get unambiguous knock
          knk_clk_test = inpram.knk_tadv;		// change time interval
          switch (knk_stat)  {
            case 0:
              break;
            case 1:
            case 2:
              if((knk_stat == 2) && (outpc.knk_rtd == 0))
                knk_stat = 0;  // back to table value & no knock- restart process
              else  {
                if(outpc.knk_rtd >= inpram.knk_step2)
                  outpc.knk_rtd -= inpram.knk_step2;   // remove some retard
                else
                  outpc.knk_rtd = 0;
                knk_stat = 2;			// had knock, eliminated it, now try get closer
                                  //  to threshhold
              }
              break;
            case 3:    // had knock, eliminated it, couldn't get closer to thresh
              // This is as good as can get; save tps, rpm
              knk_tble_adv = outpc.adv_deg;
              knk_stat = 4; 
              break;
            case 4:		// maintain status until knock or change in tps or map
              tmp1 = outpc.adv_deg - knk_tble_adv;
              if(tmp1 < 0)tmp1 = -tmp1;
              if(tmp1 > inpram.knk_dtble_adv)  {
                outpc.knk_rtd = 0;    // conditions changed- restart process
                knk_stat = 0;
              }
              break;
          }	 // end status switch
        }
        knk_count = 0;
        knk_clk = 0;
      }
    }
    
DIST_ADV:    
    // Calculate ignition advance
  	lsum = intrp_2ditable(outpc.rpm, kpaix, NO_SRPMS, NO_SMAPS,
  	        &inpram.srpm_table[0], &inpram.smap_table[0], &in2ram.adv_table[0][0]) + 
  	        outpc.cold_adv_deg - outpc.knk_rtd; // degx 10
    // Subtract retard vs manifold air temp
  	lsum -= intrp_1dctable(1, outpc.mat, NO_MAT_TEMPS, in2ram.MatTemps, 0, in2ram.MatSpkRtd);    // %
    // correct for flex fuel;
  	lsum += ffspkdel;    // degx10
    // rev limit
  	if(inpram.RevLimOption == 1)  {
  	  if(outpc.rpm > inpram.RevLimRpm2)
  	    lsum -= inpram.RevLimMaxRtd;                  // deg x 10
  	  else if(outpc.rpm > inpram.RevLimRpm1)  {
  	    lsum -= (((long)inpram.RevLimMaxRtd*(outpc.rpm- inpram.RevLimRpm1))/
  	         (inpram.RevLimRpm2 - inpram.RevLimRpm1));		// deg x 10
  	  }
  	}
    // reference relative to IC
  	ic_adv_deg = (int)(lsum - inpram.adv_offset); 
  	if((outpc.engine & 0x02)  &&    // cranking &	trigger rise/ return
  	   (crank_trig_mode || trig_ret_mode))
  	  outpc.adv_deg = inpram.adv_offset;	  // relative to true TDC
  	else   
  	  outpc.adv_deg = (int)lsum;			      // relative to true TDC
  	// Base dwell
  	tmp1 = inpram.max_coil_dur;
  	// Add correction if accelerating
  	if((outpc.tpsaccel > 0) || (outpc.XTfcor > 101))
  	  tmp1 += inpram.DurAcc;
  	// Correct for battery voltage
  	tmp1 += coil_dur_table(outpc.batt - 120);
  	coil_dur = tmp1;			// msx10, will be used in IC Isr.
  	// following belongs in Input Capture ISR, but this saves long div in ISR
  	DISABLE_INTERRUPTS
  	ltmp = coil_dur_set;
  	ENABLE_INTERRUPTS
  	outpc.coil_dur = (int)(ltmp / 100);   // msx10

/***************************************************************************
**
** Check whether to burn flash
**
**************************************************************************/
BURN_FLASH:
    if(amc_burn_flag && (outpc.rpm < AMCBURN_RPM_THRESH))  {
      amc_burn_flag = 0;
      burn_flag = 1;
      in2ram.AMCNBurns++;  // increment cumulative no. of AMC flash burns
      flocker = 0xCC;    // set sem to prevent burn flash thru runaway code       
    }
    if(burn_flag) {
      // burn flash 512 byte(256 word) sector(s)
      if((burn_flag > 1) || (outpc.rpm <= 50) || (tooth_no == burn_tooth_no))  {
        fburner(tableWordFlash(burn_idx, 0), tableWordRam(burn_idx, 0), tableWords(burn_idx));
        if(burn_flag >= tableWords(burn_idx))  {
          burn_flag = 0;
          flocker = 0;
        }
        else
          burn_flag++;
      }
    }
/***************************************************************************
**
** Check for reinit command
**
**************************************************************************/
    if(reinit_flag)  {
      reinit_flag = 0;
      // update initialization of variables/ registers dependent on
      // user config inputs
      SPK = 1 - inpram.spkout_hi_lo;  // 0=spk low, but inverted logic
      CHG = 1 - SPK;                  // after transistor
      if(!(inpram.ICIgnOption & 0x02))
        trig_ret_mode = 0;
      else if(outpc.rpm < inpram.crank_rpm)
        trig_ret_mode = 2;
      TCTL4 &= ~0x33;  // clear bits 0,1 and 4.5(won't hurt if not dual spk)
      if(inpram.DualSpkOptn < 2)  {
        if(inpram.ICIgnOption & 0x01)  {
          // normal rising edge input  capture
          if(!trig_ret_mode)
            // not trig ret mode
            TCTL4 |= 0x11; // rising edge input capture
          else
            // reverse in trigger return mode
            TCTL4 |= 0x22; // falling edge input capture
        }
        else  {
          // normal falling edge input capture
          if(!trig_ret_mode)
            // not trig ret mode
            TCTL4 |= 0x22; // falling edge input capture
          else
            // reverse in trigger return mode
            TCTL4 |= 0x11; // rising edge input capture
        }
      }
      else  {  
        if(inpram.ICIgnOption & 0x01)		// tach in is rising edge
          TCTL4 |= 0x01;
        else														// tach in is falling edge
          TCTL4 |= 0x02;
        if(inpram.DualSpkOptn == 3)     // cam_sync is rising edge
          TCTL4 |= 0x10;
        else if(inpram.DualSpkOptn == 2)  // cam_sync is falling edge
          TCTL4 |= 0x20;
      }
      if(!inpram.CWOption)  {  
        if(outpc.clt >= inpram.temp_table[NO_TEMPS-1])  {
          AWEV = (unsigned short)inpram.AWEVH;
          AWC = (unsigned short)inpram.AWCH;
        }
        else if(outpc.clt <= inpram.temp_table[0])  {
          AWEV = (unsigned short)inpram.AWEVU;
          AWC = (unsigned short)inpram.AWCU;
        }
        else {
          ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWEVH - 
            inpram.AWEVU);
          AWEV = (unsigned short)(inpram.AWEVU + (ltmp / 
            (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // %
          ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.AWCH - 
            inpram.AWCU);
          AWC = (unsigned short)(inpram.AWCU + (ltmp / 
            (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // cycles
        }
      }
      else  {	     // custom warmup tables
        AWEV = (unsigned short)CW_table(outpc.clt,in2ram.CWAWEV);     // %
        AWC = (unsigned short)CW_table(outpc.clt,in2ram.CWAWC);       // cycles
      }
      crank_trig_mode = (inpram.ICIgnOption & 0x04) >> 2;
      // Set up SCI (rs232): SCI BR reg= BusFreq(=24MHz)/16/baudrate
      SCI0BDL = (unsigned char)(1500000/inpram.baud);
      ltmp = (150000000/inpram.baud) - ((long)SCI0BDL*100);
      if(ltmp > 50)SCI0BDL++;   // round up 
    }

/***************************************************************************
**
** Determine spare port settings
**
**************************************************************************/
  for(ix = 0;ix < NPORT; ix++)  {
    if(inpram.spr_port[ix])  {
      // Evaluate first condition
      if(inpram.out_byte1[ix] == 1)
        tmp1 = *((char *)(&outpc) + inpram.out_offset1[ix]);
      else
        tmp1 = *( int *)((char *)(&outpc) + inpram.out_offset1[ix]);
      tmp1 = tmp1 - inpram.thresh1[ix];
      if(inpram.condition1[ix] == '<')
        tmp1 = -tmp1;              //  convert < condition to same as > condition
      if(inpram.condition1[ix] == '=')  {
        if((tmp1 >= -inpram.hyst1[ix]) && (tmp1 <= inpram.hyst1[ix]))
          ctmp1 = 1;               // 1st condition true
      }
      else if(tmp1 > 0)
        ctmp1 = 1;                 // 1st condition true
      else 
        ctmp1 = 0;                 // 1st condition false
      // Evaluate second condition if there is one
      if(inpram.cond12[ix] != ' ')  {
        if(inpram.out_byte2[ix] == 1)
          tmp2 = *((char *)(&outpc) + inpram.out_offset2[ix]);
        else
          tmp2 = *( int *)((char *)(&outpc) + inpram.out_offset2[ix]);
        tmp2 = tmp2 - inpram.thresh2[ix];
        if(inpram.condition2[ix] == '<')
          tmp2 = -tmp2;            //  convert < condition to same as > condition
        if(inpram.condition2[ix] == '=')  {
          if((tmp2 >= -inpram.hyst2[ix]) && (tmp2 <= inpram.hyst2[ix]))
            ctmp2 = 1;             // 2nd condition true
        }
        else if(tmp2 > 0)
          ctmp2 = 1;               // 2nd condition true
        else 
          ctmp2 = 0;               // 2nd condition false
      }
      // Evaluate final condition
      if(((inpram.cond12[ix] == '&') && (ctmp1 && ctmp2)) ||
         ((inpram.cond12[ix] == '|') && (ctmp1 || ctmp2)) ||
         ((inpram.cond12[ix] == ' ') && ctmp1))  {
            if(lst_pval[ix] != inpram.port_val[ix])  {
              set_spr_port((char)ix, inpram.port_val[ix]);
              lst_pval[ix] = inpram.port_val[ix];
            }
      }
      else  {
            // Evaluate hysteresis conditions
        if((inpram.condition1[ix] == '>') || (inpram.condition1[ix] == '<'))
              tmp1 = -tmp1 - inpram.hyst1[ix];
        if(inpram.condition1[ix] == '=')  {
              ctmp1 = 1 - ctmp1;         // 1st hyst. condition opposite of set cond
        }
        else if(tmp1 > 0)
              ctmp1 = 1;                 // 1st hysteresis condition true
        else 
              ctmp1 = 0;                 // 1st hysteresis condition false
        if(inpram.cond12[ix] != ' ')  {
            if((inpram.condition2[ix] == '>') || (inpram.condition2[ix] == '<'))
              tmp2 = -tmp2 - inpram.hyst2[ix];
            if(inpram.condition2[ix] == '=')  {
              ctmp2 = 1 - ctmp2;         // 2nd hyst. condition opposite of set cond
            }
            else if(tmp2 > 0)
              ctmp2 = 1;                 // 2nd hysteresis condition true
            else 
              ctmp2 = 0;                 // 2nd hysteresis condition false
        }
        // Evaluate final hysteresis condition
        if(((inpram.cond12[ix] == '&') && (ctmp1 || ctmp2)) ||
          ((inpram.cond12[ix] == '|') && (ctmp1 && ctmp2)) ||
          ((inpram.cond12[ix] == ' ') && ctmp1))  {
            if(lst_pval[ix] != 1 - inpram.port_val[ix])  {
                set_spr_port((char)ix, 1 - inpram.port_val[ix]);
                lst_pval[ix] = 1 - inpram.port_val[ix];
            }
        }
      }		// end eval of hysteresis conditions
      
    }		 // end if spr_port
  }			 // end for ix loop
/***************************************************************************
**
**  Check for serial, CAN receiver timeout
**
**************************************************************************/
    DISABLE_INTERRUPTS
    ultmp = lmms;
    ultmp2 = rcv_timeout;
    ENABLE_INTERRUPTS
    if(ultmp > ultmp2)  {
      txmode = 0;    // break out of current receive sequence
      rcv_timeout = 0xFFFFFFFF;
    }
    DISABLE_INTERRUPTS
    ultmp2 = ltch_CAN;
    ENABLE_INTERRUPTS
    if(ultmp > ultmp2)  {
      getCANdat = 0;    // break out of current receive sequence
      ltch_CAN = 0xFFFFFFFF;
    }
    if(kill_ser)  {
      if(outpc.seconds > kill_ser_t)  {
        kill_ser = 0;
        SCI0CR2 |= 0x24;     // rcv, rcvint re-enable
      }
    }
  
/***************************************************************************
**
**  Check for CAN reset
**
**************************************************************************/
	  if(can_reset)  {
		  /* Re-initialize CAN comms */
		  CanInit();
		  can_reset = 0;
	  }
#ifdef CAN_TEST    		// sample code to send periodic messages
    DISABLE_INTERRUPTS
    ultmp = lmms;
    ENABLE_INTERRUPTS
    if(ultmp > cansendclk)  {
      cansendclk = ultmp + 7812;      // 1 sec(7812 x .128 ms) clk
    for(ix = 0;ix < 6;ix++)  {
      // load ring buffer - send rpm
      utmp1 = outpc.rpm;
      *(&can[1].cx_datbuf[can[1].cxno_in][0]) = *((char *)&utmp1);
      *(&can[1].cx_datbuf[can[1].cxno_in][1]) = *((char *)&utmp1 + 1);
      can[1].cx_msg_type[can[1].cxno_in] = MSG_CMD; 
      can[1].cx_varblk[can[1].cxno_in] = 1;
      // Told to put rpm in outpc.spare[3]; below is offset
      can[1].cx_varoff[can[1].cxno_in] = (unsigned short)(&outpc.spare[3]) -
                                   (unsigned short)(&outpc);
      can[1].cx_dest[can[1].cxno_in] = 1;		// send to device 1
      can[1].cx_varbyt[can[1].cxno_in] = 2;	 // 2 bytes
      // This is where (in xmt ring buffer) to put next message
      if(can[1].cxno_in < (NO_CANMSG - 1))
        can[1].cxno_in++;
      else
        can[1].cxno_in = 0;
      // increment counter
      if(can[1].cxno < NO_CANMSG)
        can[1].cxno++;
      else
        can[1].cxno = NO_CANMSG;
    }
      if(!(CANTIER & 0x07))  {
        // Following will cause entry to TxIsr without sending msg
        // since when CANTIER = 0, CANTFLG left as buff empty(>0).
        // If CANTIER has at least 1 int buf enabled, will enter
        // TxIsr automatically.
        CANTBSEL = CANTFLG;
        CANTIER = CANTBSEL;
      }
    }
#endif	 // end sample CAN send code
    
  }     //  END Main while(1) Loop
}

void ign_reset(void)  {
  // enter here at init and when in stall condition
  // Disable IC, Ign,Inj OC interrupts
  TIE &= 0xC0;  
  TSCR1 &= 0x7F;      // disable all timers
  // reinitialize
  outpc.spare[1]++;
  synch = 0;
  cam_sync = 0;
  outpc.engine = 0;
  outpc.rpm = 0;
  ICt_overflow[0] = 0;
  ICt_overflow[1] = 0;
  ICtt_ovflo = 0;
  IgnOCt_overflow[0] = 0;
  IgnOCt_overflow[1] = 0;
  Fl1OCt_overflow = 0;
  Fl2OCt_overflow = 0;
  TC_ovflow = 0;
  TC_ov_ix = 0;
  pulse_no = 0;  // This will inhibit overflow counters
  t_enable_IC[0] = 0xFFFFFFFF;
  t_enable_IC[1] = 0xFFFFFFFF;
  igncount = 0;
  altcount = 0;
  egocount = 0;
  egopstat = 0;
  tpsaclk = 0;
  asecount = 0;
  cut_fuel = 0;
  knk_clk = 0;
  knk_clk_test = inpram.knk_trtd;
  knk_stat = 0;
  knk_count = 0;
  outpc.knk_rtd = 0;
  if(!(inpram.ICIgnOption & 0x02))
    trig_ret_mode = 0;
  else
    trig_ret_mode = 2;
  TCTL4 &= ~0x33;  // clear bits 0,1 and 4,5(won't hurt if not dual spk)
  if(inpram.DualSpkOptn < 2)  {
    if(inpram.ICIgnOption & 0x01)  {
      // normal rising edge input  capture
      if(!trig_ret_mode)
        // not trig ret mode
	      TCTL4 |= 0x11; // rising edge input capture
      else
        // reverse in trigger return mode
	      TCTL4 |= 0x22; // falling edge input capture
    }
    else  {
      // normal falling edge input capture
      if(!trig_ret_mode)
        // not trig ret mode
	      TCTL4 |= 0x22; // falling edge input capture
      else
        // reverse in trigger return mode
	      TCTL4 |= 0x11; // rising edge input capture
    }
  } 
  else  {  
    if(inpram.ICIgnOption & 0x01)		// tach in is rising edge
      TCTL4 |= 0x01;
    else														// tach in is falling edge
      TCTL4 |= 0x02;
    if(inpram.DualSpkOptn == 3)     // cam_sync is rising edge
      TCTL4 |= 0x10;
    else if(inpram.DualSpkOptn == 2)  // cam_sync is falling edge
      TCTL4 |= 0x20;
  }
  crank_trig_mode = (inpram.ICIgnOption & 0x04) >> 2;
  // if coil charging, set flg to cease aft 1 s if no resynch
  t_chgoff = lmms + 7812;
  // Turn Off injectors
  TCTL2 &= ~0x44;   // set outputs lo in OL1,3
  CFORC |= 0x0A;  	
  PWME &= ~0x14;	  // turn off pwms
  *pPTMpin[3] &= ~0x08;   // turn off inj led
  outpc.squirt = 0;       // injectors off
  IgnOCpinstate = SPK;
  // clear timer interrupt flags
  TFLG1 = 0x3F;
  // enable IC timer(s) & interrupt(s)
  TSCR1 |= 0x80;
  if(!inpram.DualSpkOptn || (inpram.DualSpkOptn >= 4)) {
    TIE |= 0x01;
  } 
  else  {				 // dual inputs
    TIE |= 0x05;
  }
  return;
}

void get_adc(char chan1, char chan2)  {
char chan;
long adcval;
int adcvalv,tmp1,tmp2;
  
  for (chan = chan1; chan <= chan2; chan++)  {
    switch(chan)  {
      case 0:
        if((inpram.MAFOption & 0x0F) != 1)  {		// map
          adcval = (long)inpram.map0 + 
            ((long)(inpram.mapmax - inpram.map0) * ATD0DR0) / 1023; // kPa x 10
          if(first_adc)
            outpc.map = (short)adcval;
          else
            outpc.map += (short)((inpram.mapLF * (adcval - outpc.map)) / 100);
        } 
        else  {			                            // maf only
          if(first_adc)
            mafraw = maffactor_table[ATD0DR0];		             // mg/ secx10
          else  {
            adcval = maffactor_table[ATD0DR0];
            mafraw += (short)((inpram.mapLF * (adcval - mafraw)) / 100); //	mg/secx 10
          }
        }
        break;
      case 1:
        adcval = (long)inpram.mat0 + 
          ((long)inpram.matmult * matfactor_table[ATD0DR1]) / 100; // deg F or C x 10
        if(first_adc)
          outpc.mat = (short)adcval;
        else
          outpc.mat += (short)((inpram.adcLF * (adcval - outpc.mat)) / 100);
        break;
      case 2:
        adcval = (long)inpram.clt0 + 
          ((long)inpram.cltmult * cltfactor_table[ATD0DR2]) / 100; // deg F or C x 10
        if(first_adc)
          outpc.clt = (short)adcval;
        else
          outpc.clt += (short)((inpram.adcLF * (adcval - outpc.clt)) / 100);
        break;
      case 3:
        adcval = (ATD0DR3 - (long)inpram.tps0) * 1000 / 
          (inpram.tpsmax - inpram.tps0);                           // % x 10            
        if(first_adc)
          outpc.tps = (short)adcval;
        else
          outpc.tps += (short)((inpram.tpsLF * (adcval - outpc.tps)) / 100);
        break;
      case 4:
        adcval = (long)inpram.batt0 + 
          ((long)(inpram.battmax - inpram.batt0) * ATD0DR4) / 1023; // V x 10
        if(first_adc)
          outpc.batt = (short)adcval;
        else
          outpc.batt += (short)((inpram.adcLF * (adcval - outpc.batt)) / 100);
        break;
      case 5:
        if(inpram.EgoOption >= 1)  {
          // check if sensor bad (near limits)
          if((ATD0DR5 < 3) || (ATD0DR5 > 1020))
            bad_ego_flag |= 0x01;
          else
            bad_ego_flag &= ~0x01;
          adcval = (long)inpram.ego0 + 
            ((long)inpram.egomult * egofactor_table[ATD0DR5]) / 100; // afr x 10
          tmp1 = (int)ATD0DR5;
          asm {
            ldy tmp1 
            ldd  #500
            EMULS
            ldx  #1023
            EDIVS
            sty adcvalv
          };
          if(first_adc)	 {
            outpc.ego1 = (short)adcval;
            outpc.egoV1 = adcvalv;				// Vx100
          }
          else  {
            outpc.ego1 += (short)((inpram.egoLF * (adcval - outpc.ego1)) / 100);
            tmp1 = (int)inpram.egoLF;
            tmp2 = adcvalv - outpc.egoV1;
            asm {
              ldy  tmp1
              ldd  tmp2
              EMULS
              ldx  #100
              EDIVS
              sty adcvalv
            };
            outpc.egoV1 += adcvalv;      // Vx100
          }
        }
        break;
      case 6:
        if(inpram.BaroOption == 2)  {
          adcval = (long)inpram.baro0 + 
            ((long)(inpram.baromax - inpram.baro0) * ATD0DR6) / 1023; // kPa x 10
          if(first_adc)
            outpc.baro = (short)adcval;
          else
            outpc.baro += (short)((inpram.adcLF * (adcval - outpc.baro)) / 100);
          outpc.ego2 = outpc.ego1;
          outpc.egoV2 = outpc.egoV1;
        } 
        else if((inpram.EgoOption == 2) || (inpram.EgoOption == 4))  {
          // check if sensor bad (near limits)
          if((ATD0DR6 < 3) || (ATD0DR6 > 1020))
            bad_ego_flag |= 0x02;
          else
            bad_ego_flag &= ~0x02;
          adcval = (long)inpram.ego0 + 
            ((long)inpram.egomult * egofactor_table[ATD0DR6]) / 100; // afr x 10
          tmp1 = (int)ATD0DR6;
          asm {
            ldy tmp1 
            ldd  #500
            EMULS
            ldx  #1023
            EDIVS
            sty adcvalv
          };
          if(first_adc)	 {
            outpc.ego2 = (short)adcval;
            outpc.egoV2 = adcvalv;				// Vx100
          }
          else  {
            outpc.ego2 += (short)((inpram.egoLF * (adcval - outpc.ego2)) / 100);
            tmp1 = (int)inpram.egoLF;
            tmp2 = adcvalv - outpc.egoV2;
            asm {
              ldy  tmp1
              ldd  tmp2
              EMULS
              ldx  #100
              EDIVS
              sty adcvalv
            };
            outpc.egoV2 += adcvalv;      // Vx100
          }
        }
        else if(inpram.MAFOption == 0x12)  {
          if(first_adc)
            mafraw = maffactor_table[ATD0DR6];		 // mg/ secx10
          else  {
            adcval = maffactor_table[ATD0DR6];
            mafraw += (short)((inpram.mapLF * (adcval - mafraw)) / 100);
          }
          outpc.ego2 = outpc.ego1;
          outpc.egoV2 = outpc.egoV1;
        }
        else  {
          outpc.ego2 = outpc.ego1;
          outpc.egoV2 = outpc.egoV1;
        }
        break;
      case 7:
        if(inpram.MAFOption != 0x22)  {
          adcval = (long)inpram.knock0 + 
            ((long)(inpram.knockmax - inpram.knock0) * ATD0DR7) / 1023; // V x 100
          if(first_adc)
            outpc.knock = (short)adcval;
          else
            outpc.knock += (short)((inpram.knkLF * (adcval - outpc.knock)) / 100);
        } else  {
          if(first_adc)
            mafraw = maffactor_table[ATD0DR7];		 // mg/ secx10
          else  {
            adcval = maffactor_table[ATD0DR7];
            mafraw += (short)((inpram.mapLF * (adcval - mafraw)) / 100);
          }
        }
        break;
      default:
        break;
    }			 // end of switch
  }				 // end of for loop
  
  return;
}

#ifndef GCC_BUILD
#pragma CODE_SEG  ROM_7400
#endif
void switch_page(unsigned char sub_no, char carg1, char carg2)  {
// This resides in non-banked memory and is used to call subroutines
//  from another page.

  switch(sub_no)  {
          
    case 0:
      PPAGE = 0x3D;
      ego_calc();
      PPAGE = 0x3C;
      break;
      
    case 1:
      PPAGE = 0x3C;
      get_adc(carg1,carg2);
      PPAGE = 0x3D;
      break;

    case 2:
      PPAGE = 0x3D;
      amc();
      PPAGE = 0x3C;
      break;
    
    case 3:
      PPAGE = 0x3D;
      tpsaen();
      PPAGE = 0x3C;
      break;
      
    default:
      break;
  }
  
  return;
}

int intrp_1dctable(char sgnx, int x, unsigned char n, int * x_table, char sgnz, 
        unsigned char * z_table)  {
  int ix;
  long interp, interp3;
  // bound input arguments
  if((sgnx && (x > x_table[n-1])) ||
     (!sgnx && ((unsigned int)x > (unsigned int)x_table[n-1])))  {
    if(!sgnz)
      return((int)z_table[n -1]);
    else
      return((int)((char)z_table[n -1]));
  }
  if((sgnx && (x < x_table[0])) ||
     (!sgnx && ((unsigned int)x < (unsigned int)x_table[0])))  {
    if(!sgnz)
      return((int)z_table[0]);
    else
      return((int)((char)z_table[0]));
  }
  for(ix = n - 2; ix > -1; ix--)  { 
    if((sgnx && (x > x_table[ix])) ||
       (!sgnx && ((unsigned int)x > (unsigned int)x_table[ix])))  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  if(sgnx)
    interp =	x_table[ix + 1] - x_table[ix];
  else
    interp =	(unsigned int)x_table[ix + 1] - (unsigned int)x_table[ix];
  if(interp != 0)  {
    if(sgnx)
      interp3 = x - x_table[ix];
    else
      interp3 = (unsigned int)x - (unsigned int)x_table[ix];
    interp3 = (100 * interp3);
    interp = interp3 / interp;
  }
  if(!sgnz)
    return((int)(z_table[ix] +
	    interp * (z_table[ix+1] - z_table[ix])/ 100));
	else
    return((int)((char)z_table[ix] +
	    interp * ((char)z_table[ix+1] - (char)z_table[ix])/ 100));
}

int intrp_2dctable(unsigned int x, int y, unsigned char nx, unsigned char ny,
  unsigned int * x_table, int * y_table, unsigned char * z_table)  {
  int ix,jx;
  long interp1, interp2, interp3;
  // bound input arguments
  if(x > x_table[nx-1])x = x_table[nx-1];
  else if(x < x_table[0])x = x_table[0];
  if(y > y_table[ny-1])y = y_table[ny-1];
  else if(y < y_table[0])y = y_table[0];
  // Find bounding indices in table
  for(ix = ny - 2; ix > -1; ix--)  {  // Start w highest index
	//  because will generally have least time for calculations at hi y
  	if(y > y_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
  for(jx = nx - 2; jx > -1; jx--)  {  // Start w highest index
	// because will generally have least time for calculations at hi x
	  if(x > x_table[jx])  {
	    break;
    }
  }
  if(jx < 0)jx = 0;
  // do 2D interpolate
  interp1 = y_table[ix + 1] - y_table[ix];
  if(interp1 != 0)  {
    interp3 = (y - y_table[ix]); 
    interp3 = (100 * interp3); 
    interp1 = interp3 / interp1; 
  }
  interp2 =	x_table[jx + 1] - x_table[jx];
  if(interp2 != 0)  {
    interp3 = (x - x_table[jx]); 
    interp3 = (100 * interp3); 
    interp2 = interp3 / interp2; 
  }
  return((int)(((100 - interp1) * (100 - interp2) * z_table[ix*nx+jx]
	  + interp1 * (100 - interp2) * z_table[(ix+1)*nx+jx]
	  + interp2 * (100 - interp1) * z_table[ix*nx+jx+1]
	  + interp1 * interp2 * z_table[(ix+1)*nx+jx+1]) / 10000));
}

int CW_table(int clt, int *table)  {
  int ix;
  long interp, interp3;
  // returns values for various cold warmup table interpolations
  // bound input arguments
  if(clt > inpram.temp_table[NO_TEMPS-1])  {
    return(table[NO_TEMPS -1]);
  }
  if(clt < inpram.temp_table[0])  {
    return(table[0]);
  }
  for(ix = NO_TEMPS - 2; ix > -1; ix--)  { 
  	if(clt > inpram.temp_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp = inpram.temp_table[ix + 1] - inpram.temp_table[ix];
  if(interp != 0)  {
    interp3 = (clt - inpram.temp_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
  }
  return((int)(table[ix] + interp * (table[ix+1] - table[ix])/ 100));
}

#ifndef GCC_BUILD
#pragma CODE_SEG  OTHER_ROM
#endif
void tpsaen(void)  {
  int tmp1,tmp2,tmp3,tmp4,ae_scaler;
  static int tpsatmp,tpsaccel_end; 
  
/**************************************************************************
**				 
**  Throttle Position Acceleration Enrichment
**
**   Method is the following:
**
**
**   ACCELERATION ENRICHMENT:
**   If (tpsdot < 0) goto DEACCELERATION_ENRICHMENT
**   If tpsdot > tpsthresh and TPSAEN bit = 0 then (acceleration enrichment):
**   {
**    1) Set acceleration mode
**    2) Continuously determine rate-of-change of throttle, and perform
**        interpolation of table values to determine acceleration
**        enrichment amount to apply.
**   }
**   If (TPSACLK > TpsAsync) and TPSAEN is set then:
**   {
**    1) Clear TPSAEN bit in engine
**    2) Set TPSACCEL to 0
**    3) Go to EGO Delta Step Check Section
**   }
**   Enrichment tail-off pulsewidth:
**   
**   ------------------ tpsaccel
**   |            |\
**   |            |   \
**   |            |      \
**   |            |         \		____ TpsAccel2
**   |            |            | 
**   |            |            |
**   ---------------------------
**   <--TpsAsync--><-TpsAsync2->
**
**
**   DEACCELERATION ENRICHMENT:
**   If (-tpsdot) > tpsthresh then (deceleration fuel cut)
**   {
**    If (TPSAEN = 1) then:
**    {
**      1) TPSACCEL = 0 (no acceleration)
**      2) Clear TPSAEN bit in ENGINE
**      3) Go to EGO Delta Step
**    }
**    If (RPM > 1500 then (fuel cut mode):
**    {
**      1) Set TPSACCEL value to TPSDQ
**      2) Set TPSDEN bit in ENGINE
**      3) Go to EGO Delta Step Check Section
**    }
**   }
**   else
**   {
**    If (TPSDEN = 1) then
**    {
**     1) Clear TPSDEN bit in ENGINE
**     2) TPSACCEL = 0
**     3) Go to EGO Delta Step Check Section
**    }
**   }
**
**************************************************************************/
  tpsatmp = outpc.tpsaccel;
  DISABLE_INTERRUPTS
  tpsdot_ltch = outpc.tpsdot;
  mapdot_ltch = outpc.mapdot;
  ENABLE_INTERRUPTS
  if(tpsdot_ltch < 0)goto TDE;			// decelerating
  if(outpc.engine & 0x10)goto AE_COMP_SHOOT_AMT;   // if accel enrich bit set
  if(((inpram.Tps_acc_wght == 0) || (tpsdot_ltch < inpram.TpsThresh)) &&
     ((inpram.Tps_acc_wght == 100) || (mapdot_ltch < inpram.MapThresh)))
  		goto TAE_CHK_TIME;
  // start out using first element - determine actual next time around
  tpsatmp = (((short)inpram.tpsen_table[0] * inpram.Tps_acc_wght) + 
    ((short)inpram.mapen_table[0] * (100 - inpram.Tps_acc_wght))) / 100;
  tpsaccel_end = tpsatmp;		  // latch last tpsaccel before tailoff
  tpsaclk = 0;          	    // incremented in .1 sec timer
  outpc.engine |= 0x10;       // set tpsaen bit
  outpc.engine &= ~0x20;      // clear tpsden bit
  *pPTMpin[4] |= 0x10;        // set accel led
  goto END_TPS;
/* First, calculate Cold temperature add-on enrichment value from coolant value 
  TPSACOLD at min temp & 0 at high temp.

 Then determine cold temperature multiplier value ACCELMULT (in percent).

 Next, Calculate Shoot amount (quantity) for acceleration enrichment from table.
 Find bins (between) for corresponding TPSDOT and MAPDOT, and linear interpolate
 to find enrichment amount from table. This is continuously
 checked every time thru main loop while in acceleration mode,
 and the highest value is latched and used.

 The final acceleration enrichment (in .1 ms units) applied is AE = 
 (((Alookup(TPSDOT)*Tps_acc_wght + Alookup(MAPDOT)*(100 - Tps_acc_wght))
    /100) * ACCELMULT/100) + TPSACOLD.  
*/

AE_COMP_SHOOT_AMT:
  
 if(tpsaclk <= inpram.TpsAsync)  {
  tmp1 = (short)inpram.Tpsacold  - (short)((inpram.Tpsacold * 
      (long)(outpc.clt - inpram.temp_table[0]) / (inpram.temp_table[NO_TEMPS-1] 
      - inpram.temp_table[0])));	    // in .1 ms
  tmp2 = (short)inpram.AccMult + (short)(((100 - inpram.AccMult) * 
      (long)(outpc.clt - inpram.temp_table[0]) / (inpram.temp_table[NO_TEMPS-1] 
      - inpram.temp_table[0])));	    // in %
  if(inpram.Tps_acc_wght > 0)  {
  	tmp3 = intrp_1dctable(1, tpsdot_ltch, NO_TPS_DOTS, inpram.tpsdot_table, 0,
  	         inpram.tpsen_table);   // .1 ms units  
  }
  else
  	tmp3 = 0;
  if(inpram.Tps_acc_wght < 100)  {
  	tmp4 = intrp_1dctable(1, mapdot_ltch, NO_MAP_DOTS, inpram.mapdot_table, 0,
  	         inpram.mapen_table);   // .1 ms units  
  }
  else
  	tmp4 = 0;
  tmp3 = ((tmp3 * inpram.Tps_acc_wght) + (tmp4 * (100 - inpram.Tps_acc_wght))) / 100;
  tmp3 = (tmp3 * tmp2) / 100;
  if(tmp3 > 200)
            tmp3 = 200;  // rail at 20 ms
  tmp3 += tmp1;
  if(tmp3 > tpsatmp)
       tpsatmp = tmp3; // make > tps/mapen_table entry for lowest tps/mapdot
                     // plus latch and hold largest pw
  tpsaccel_end = tpsatmp;		// latch last tpsaccel before tailoff
 }
 else  {      // tailoff enrichment pulsewidth
TAILOFF:
   if(inpram.TpsAsync2 > 0)  {
     tpsatmp = tpsaccel_end + (short)(((inpram.TpsAccel2 - tpsaccel_end) *
            (long)(tpsaclk - inpram.TpsAsync)) / inpram.TpsAsync2);
   }
   else
    tpsatmp = 0;
 }

TAE_CHK_TIME:
  // check if accel is done
  // if tps decel bit not set, accel bit is
  if(!(outpc.engine & 0x20) && (outpc.engine & 0x10))  {   
  	if(tpsaclk < (inpram.TpsAsync + inpram.TpsAsync2))goto END_TPS;
  }
  goto KILL_ACCEL;

TDE:		 // tpsdot < 0
  if(outpc.engine & 0x10)  {     // if tps accel bit set
    if((-tpsdot_ltch) > inpram.TpsThresh)
      goto KILL_ACCEL;
    if(tpsaclk < inpram.TpsAsync) 
  	   tpsaclk = inpram.TpsAsync;  // just tail off AE
    goto TAILOFF;
  }
  // decel
  if((-tpsdot_ltch) > inpram.TpsThresh)  {
    if(outpc.rpm < 1500)goto END_TPS;
    outpc.tpsfuelcut = inpram.TPSDQ;    // in %
    outpc.engine |= 0x20;		  // set tps decel bit
    goto END_TPS;
  }
  //  ** Jedrik bug fix
  if(outpc.engine & 0x30)  {     // if decel or just finished accel
KILL_ACCEL:
  	outpc.engine &= ~0x30;       // clear tps decel, accel bits
  	outpc.tpsfuelcut = 100;
  	*pPTMpin[4] &= ~0x10;        // clear accel led
  	tpsatmp = 0;
  }
END_TPS:
  // apply AE rpm-based scaler
  if(outpc.rpm <= inpram.ae_lorpm)
    ae_scaler =  100;
  else if(outpc.rpm >= inpram.ae_hirpm)
    ae_scaler = 0;
  else  {
    ae_scaler = (short)(((long)100 * (inpram.ae_hirpm - outpc.rpm)) / 
                            (inpram.ae_hirpm - inpram.ae_lorpm));
  }
  outpc.tpsaccel = (tpsatmp * ae_scaler) / 100;
  return; 	  
}

void ego_calc(void)  {
/**************************************************************************
**
**  Exhaust Gas Oxygen Sensor Measurement Section
**
**************************************************************************/
long ltmp;
unsigned long ultmp;
int ego1step,ego2step;
static unsigned long tegoclk,tegoXpt,tegoupdate;

  // calculate target AFR
  if(((inpram.EgoOption > 0) && (inpram.EgoOption <= 2)) || 
      (inpram.AFRMult == 1))  {
  	  // NBO2
  	  outpc.afrtgt1 = inpram.AFRTarget;
  	  outpc.afrtgt2 = inpram.AFRTarget;
  }
  else if((inpram.EgoOption >= 3) || (inpram.AFRMult == 2)) {
  	  // WBO2
		  outpc.afrtgt1 = (unsigned char)intrp_2dctable(outpc.rpm, kpaix, NO_FRPMS, 
		    NO_FMAPS, &inpram.frpm_table[0], &inpram.fmap_table[0], &inpram.afr_table[0][0][0]); 
		                                                                   // afr x 10
  	  if(inpram.dual_tble_optn)
  	    outpc.afrtgt2 = (unsigned char)intrp_2dctable(outpc.rpm, kpaix, NO_FRPMS, 
		    NO_FMAPS, &inpram.frpm_table[0], &inpram.fmap_table[0], &inpram.afr_table[1][0][0]); 
		                                                                   // afr x 10
  	  else
  	    outpc.afrtgt2 = outpc.afrtgt1;
  }
  if( (inpram.EgoOption == 0) ||
      	    (outpc.rpm < inpram.RPMOXLimit) ||
      	    (outpc.engine & 0x30) ||                // engine accel/ decel
      	    (outpc.clt < inpram.EgoTemp) ||
      	    (outpc.tps > inpram.TPSOXLimit) ||
      	    (outpc.map > inpram.MAPOXLimit) ||
        	    (outpc.seconds < 30))  {
		  outpc.egocor1 = 100;
		  outpc.egocor2 = outpc.egocor1;
		  // get ego1,2 from sensor
		  switch_page(1,5,6);    // get_adc
		  ego_cloop = 0;
		  egopstat = 0;              // out of ego closed PID loop
		  goto EGO_RETURN;
  }

  ego_cloop = 1;
  if(inpram.EgoAlg == 0)  {
  	// simple (non-pid) ego closed loop
  	if(egocount < inpram.EgoCountCmp)goto EGO_RETURN;
  	// check if rich/ lean after every EgoCountCmp ignition pulses
  	// get ego1,2 from sensor
  	switch_page(1,5,6);      // get_adc
  	egocount = 0;
  	if(inpram.EgoOption <= 2)  {
  		// NBO2
  		if(outpc.egoV1 < outpc.afrtgt1)  {
  	    // O2_IS_LEAN
  	    ego1step = (int)inpram.EgoStep;
  		}
  		else  {				
  	    // O2_IS_RICH
  	    ego1step = -(int)inpram.EgoStep;
  		}
  		ego2step = ego1step;
  	}
  	else if(inpram.EgoOption >= 3)  {
  		// WBO2
  		// calculate error
  		ego1step = (inpram.egoKP * (outpc.ego1 - outpc.afrtgt1)) / outpc.afrtgt1;
  		ego2step = (inpram.egoKP * (outpc.ego2 - outpc.afrtgt2)) / outpc.afrtgt2;
  	}    // end EgoOption >= 3
  }      // end EgoAlg = 0
  else  {         // Prop err corr with transport delay or
  	              //  PID algorithm with Smith predictor
  	if(egopstat == 0)  {	 // set up for ego closed P/PID loop
  	  egopstat = 1;            
  	  tegoclk = 0;
  	  ego1err = 0;
  	  sego1err = 0;
  	  ego1errm1 = ego1err;
  	  ego2err = 0;
  	  sego2err = 0;
  	  ego2errm1 = ego2err;
  	  outpc.egocor1 = 100;
  	  outpc.egocor2 = outpc.egocor1;
  	  afrtgt_skip = 0;
  	  afrdl1 = outpc.afrtgt1;
  	  afrdl2 = outpc.afrtgt2;
  	}
  	
    DISABLE_INTERRUPTS
    ultmp = lmms;
    ENABLE_INTERRUPTS
  	if(ultmp < tegoclk)goto EGO_RETURN;
  	// get ego1,2 from sensor
  	switch_page(1,5,6);      // get_adc
  	// update correction
  	// go through pid/Smith pred algorithm
    DISABLE_INTERRUPTS
    ultmp = lmms;
    ENABLE_INTERRUPTS
  	tegoclk = ultmp + EGODT;			 // .128 tics
  	if(outpc.rpm > 0)  {
  	  ltmp = inpram.egoKdly1 + (((long)(inpram.egoKdly2 * 120000) / 
  	    outpc.map) / outpc.rpm);   // sensor + transport delay (ms)
  	  tegoXpt = (ltmp * 1000) / 128;  // .128 tics
  	} 
  	else  {
  	  tegoXpt = 7812;       // = 1 sec in .128 tics
  	}
  	if(inpram.EgoAlg == 2)  {
  	  if(afrtgt_skip) 
  	    afrtgt_skip--;
  	  else  {
  	    if(tegoXpt > 0)  {
  	    	// Calculate slow lag coeff(%) for delayed afr target										
  	      afrSL = afrLF_calc(tegoXpt);   
  	      if(afrSL < 34)  {  
  	        // From eq for afrdly (below) any SL < 34 and (afrtgt - afrdly) 
  	        // < 3 (afrx10) means the update for afrdly will be 0. In this 
  	        // case keep afrSL =34, but skip (tegoXpt/txpt(SL=34)) updates.
  	        afrSL = 34;
  	        afrtgt_skip = (unsigned short)(tegoXpt / (unsigned short)468);
  	      } 
  	      else  {
  	        afrtgt_skip = 0;
  	        if(afrSL > 100)afrSL = 100;
  	      }
  	    } 
  	    else  {
  	      afrtgt_skip = 0;
  	      afrSL = 100;
  	    }
        // Smith Predictor delay free model outputs = outpc.afrtgt1,2
  	    //                 delayed model outputs = afrdl1,2
  	    afrdl1 += (int)((afrSL * (int)(outpc.afrtgt1 - afrdl1)) / 100);
  	    afrdl2 += (int)((afrSL * (int)(outpc.afrtgt2 - afrdl2)) / 100);
  	  }
  	}
  	// calc err from model o/ps: egoerr = ego + (afrtgt - afrdly) - afrtgt
  	if(inpram.EgoOption <= 2)  {   // NB
  	  ego1err = -(int)(outpc.egoV1 - afrdl1);
  	  ego2err = -(int)(outpc.egoV2 - afrdl2);
  	}
  	else  {                        // WB
  	  ego1err = outpc.ego1 - afrdl1;
  	  ego2err = outpc.ego2 - afrdl2;
  	}
  	if(egopstat == 1)  {
  	  //  1st time just correct and wait tegoXpt delay
  	  ego1step = ((int)(inpram.egoKP * ego1err) / (int)afrdl1);
  	  ego2step = ((int)(inpram.egoKP * ego2err) / (int)afrdl2);
  	  if(inpram.EgoAlg == 1)  {
  	    // for this alg. correct based on ego measurement (which is delayed)
  	    //  and the delayed afrtgt - so both at approx. same time.
  	    tegoupdate = tegoclk;
  	    afrdl1 = outpc.afrtgt1;
  	    afrdl2 = outpc.afrtgt2;
  	  }
  	  egopstat = 2;
  	}
  	else if(egopstat == 2)  {
  	  ego1step = 0;      // initially don't do anything til tegoXpt delay
  	  ego2step = 0;
  	  if(inpram.EgoAlg == 1)  {
  	    if(tegoclk - tegoupdate > tegoXpt)
  	      egopstat = 1; // make another correction next time thru
  	  } 
  	  else  {
  	    if(tegoclk > tegoXpt)
  	      egopstat = 3;  
  	  }
  	} 
  	else  {             // egopstat=3 = normal error correction for EgoAlg = 2 
  	  egoKPX = (int)(((int)EGODT * inpram.egoKP) / tegoXpt);
  	  if(egoKPX < 1)egoKPX = 1;
  	  if(ego1err * ego1errm1 <= 0)sego1err = 0;
  	  sego1err += ego1err;
  	  if(sego1err > 16000)sego1err = 16000;
  	  if(sego1err < -16000)sego1err = -16000;
  	  ego1step = (int)(egoKPX * (ego1err + (int)((inpram.egoKI* sego1err)/ 100)
  	         + (int)(inpram.egoKD * (ego1err - ego1errm1) / 100)) / (int)afrdl1);
  	  if(ego1step != 0)sego1err = 0;
  	  ego1errm1 = ego1err;
  	  if(ego2err * ego2errm1 <= 0)sego2err = 0;
  	  sego2err += ego2err;
  	  if(sego2err > 16000)sego2err = 16000;
  	  if(sego2err < -16000)sego2err = -16000;
  	  ego2step = (int)(egoKPX * (ego2err + (int)((inpram.egoKI* sego2err)/ 100)
  	         + (int)(inpram.egoKD * (ego2err - ego2errm1) / 100)) / (int)afrdl2);
  	  if(ego2step != 0)sego2err = 0;
  	  ego2errm1 = ego2err;
  	}
  }    // end p/pid loop - EgoAlg = 1,2
  
  // implement correction - update egocor1,2
  if(!outpc.wbo2_en1)  {
  	// no valid ego rdbk
  	outpc.egocor1 = 100;
  } 
  else  {
  	if(ego1step > 0)  {
  		// O2_IS_LEAN
  		if((outpc.egocor1 + ego1step) <= (100 + inpram.EgoLimit))
  			outpc.egocor1 += ego1step;      // egocor not railed
  		else
  			outpc.egocor1 = 100 + inpram.EgoLimit;    // railed
  	}
  	else  {				
  		// O2_IS_RICH
  		if((outpc.egocor1 + ego1step) >= (100 - inpram.EgoLimit))
  				outpc.egocor1 += ego1step;      // egocor not railed
  		else
  			  outpc.egocor1 = 100 - inpram.EgoLimit;    // railed
  	}
  }
  if(!outpc.wbo2_en2)  {
  	// no valid ego rdbk
  	outpc.egocor2 = 100;
  }
  else  {
  	if(ego2step > 0)  {						 
  		// O2_IS_LEAN
  		if((outpc.egocor2 + ego2step) <= (100 + inpram.EgoLimit))
  			outpc.egocor2 += ego2step;      // egocor not railed
  		else
  			outpc.egocor2 = 100 + inpram.EgoLimit;    // railed
  	}
  	else  {				
  		// O2_IS_RICH
  		if((outpc.egocor2 + ego2step) >= (100 - inpram.EgoLimit))
  			outpc.egocor2 += ego2step;      // egocor not railed
  		else
  			outpc.egocor2 = 100 - inpram.EgoLimit;    // railed
  	}
  }
EGO_RETURN:
  return;
}

/**************************************************************************
**
** Automatic Mixture Control - enter once a second
**
***************************************************************************/
  void amc(void)  {
    int ix,jx,tmp1;
    static char ve_mix,ve_rjx;
    static unsigned char ve_save[NO_INJ];
    static long sumegocor1,sumegocor2;
    
    if(outpc.seconds > AMCclk)  {
      AMCclk = outpc.seconds;
      // check criteria for being in AMC mode
      if(ego_cloop && (bad_ego_flag < 3) && (outpc.warmcor == 100) &&
         (outpc.tpsfuelcut == 100) && (outpc.tpsaccel == 0) &&
         (outpc.fuelcor == 100))  {
        if(!AMCmode)  {
          AMCmode =1;
          // initialize for entry to AMC mode
          AMCve_clk = 0;
          AMCNSum = 0;
          ve_mix = -1;
          ve_rjx = -1;
          sumegocor1 = 0;
          sumegocor2 = 0;
          AMCdve_change = 0;
          bad_ego_ltch = 0;
        }
        // latch occurence of bad ego sensor
        bad_ego_ltch |= bad_ego_flag;
        // check if near a map/rpm node
        for(ix = 0;ix < NO_FMAPS; ix++)  {
  	      if(kpaix < inpram.fmap_table[ix])
   		      break;
        }
        if(ix == NO_FMAPS)  {
          ix--;
        } 
        else if(ix > 0) {
          tmp1 = kpaix - inpram.fmap_table[ix-1];
          if(tmp1 < inpram.AMCve_dmap)  {
            if((inpram.fmap_table[ix] - kpaix) >= tmp1)ix--;
          } 
          else if((inpram.fmap_table[ix] - kpaix) >= inpram.AMCve_dmap)
            ix = -1;
        }
        if(ix == -1)
            goto NOT_VRTX;   // no need to evaluate rpm vertex
        for(jx = 0;jx < NO_FRPMS; jx++)  {
  	      if(outpc.rpm < inpram.frpm_table[jx])
   		      break;
        }
        if(jx == NO_FRPMS)  {
          jx--;
        } 
        else if(jx > 0) {
          tmp1 = outpc.rpm - inpram.frpm_table[jx-1];
          if(tmp1 < inpram.AMCve_drpm)  {
            if((inpram.frpm_table[jx] - outpc.rpm) >= tmp1)jx--;
          } 
          else if((inpram.frpm_table[jx] - outpc.rpm) >= inpram.AMCve_drpm)
            jx = -1;
        }
        if(jx == -1)  {
 NOT_VRTX:
          ve_mix = -1;
          ve_rjx = -1;
          AMCve_clk = outpc.seconds;
          AMCNSum = 0;
          sumegocor1 = 0;
          sumegocor2 = 0;
          goto CHK_BURN;
        }
        if((ix != ve_mix) || (jx != ve_rjx))  {
          // moved to a new vertex
          ve_mix = (char)ix;
          ve_rjx = (char)jx;
          ve_save[0] = in2ram.ve_table[0][ve_mix][ve_rjx];
          ve_save[1] = in2ram.ve_table[1][ve_mix][ve_rjx];
          AMCve_clk = outpc.seconds;
          AMCNSum = 0;
          sumegocor1 = 0;
          sumegocor2 = 0;
        } 
        sumegocor1 += outpc.egocor1;     // 100% = no correction
        sumegocor2 += outpc.egocor2;
        AMCNSum++;
        // check for how long at same vertex
        if(outpc.seconds - AMCve_clk >= inpram.AMCramve_dt)  {
          // update ram ve by a % of ave ego correction
          tmp1 = 100 - (int)(sumegocor1 / AMCNSum);
          tmp1 = 100 - ((inpram.AMCStep * tmp1) / 100);
          if(!(bad_ego_ltch & 0x01))
            in2ram.ve_table[0][ve_mix][ve_rjx] = (unsigned char)((tmp1 * 
                   		in2ram.ve_table[0][ve_mix][ve_rjx]) / 100);
          tmp1 = 100 - (int)(sumegocor2 / AMCNSum);
          tmp1 = 100 - ((inpram.AMCStep * tmp1) / 100);
          if(!(bad_ego_ltch & 0x02))
            in2ram.ve_table[1][ve_mix][ve_rjx] = (unsigned char)((tmp1 * 
                   		in2ram.ve_table[1][ve_mix][ve_rjx]) / 100);
          if((bad_ego_ltch & 0x03) != 0x03)	 {
            AMCram_updates++;
            // send back to MT so knows when amc update and can fetch data
            outpc.amc_updates++;
            tmp1 = in2ram.ve_table[0][ve_mix][ve_rjx] - ve_save[0];
            if((tmp1 > inpram.AMCdve) || (-tmp1 > inpram.AMCdve))
              AMCdve_change = 1;
            tmp1 = in2ram.ve_table[1][ve_mix][ve_rjx] - ve_save[1];
            if((tmp1 > inpram.AMCdve) || (-tmp1 > inpram.AMCdve))
              AMCdve_change = 1;
          }
          AMCve_clk = outpc.seconds;
          AMCNSum = 0;
          sumegocor1 = 0;
          sumegocor2 = 0;
        }
      }  // in AMC mode
      else  {		// leave AMC mode 
        AMCmode = 0;
      }
      
CHK_BURN:  
      // Check if should burn ve tables in flash
      if((inpram.AMCOption > 1) && 
            ((outpc.seconds - AMCburn_clk) > inpram.AMCT_thresh) &&
            (AMCram_updates > inpram.AMCupdate_thresh) && AMCdve_change)  {
        AMCburn_clk = outpc.seconds;
        AMCram_updates = 0;
        AMCdve_change = 0;
        burn_idx = 5;
        amc_burn_flag = 1;
      }
        
    }   // seconds clock tic
    return;
  }

unsigned char afrLF_calc(long t)  {
  // afrtgt slow lag filter for Smith Prediction
  const int txpt[10] = { // xpt time, .128 ms tics
    78,156,234,390,468,858,1716,3510,8892,17940};
  const unsigned char SL[10] = {   // Lag filter coeff to produce txpt delay
    90,80,60,40,34,20,10,5,2,1};   // %
  int ix,interp;
  long interp3;
  
  if(t <= txpt[0])
    return(SL[0]);
  if(t >= txpt[9])
    return(SL[9]);
  for(ix = 1;ix <= 9; ix++)  {
    if(t < txpt[ix])break; 
  }
  
  interp = txpt[ix] - txpt[ix - 1];
  interp3 = t - txpt[ix - 1];
  return((char)(SL[ix-1] + (interp3*(SL[ix] - SL[ix-1]))/interp));
}

#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT

#pragma CODE_SEG  NON_BANKED
#endif
INTERRUPT void ISR_Timer_Clock(void)  {
unsigned char coils,fsensdat;
static unsigned char last_fsensdat;
unsigned int utmp;
int tmp,tmp1;
static unsigned short FPdcounter;
  // .128 ms clock interrupt - clear flag immediately to resume count
  CRGFLG = 0x80;  // clear RTI interrupt flag
  // also generate 1.024 ms, .10035 sec and 1.0035 sec clocks
  lmms++;   // free running clock(.128 ms tics) good for ~ 110 hrs
  mms++;    // in .128 ms tics - reset every 8 tics = 1.0024 ms
  
  // check for re-enabling IC interrupt
  if(lmms > t_enable_IC[0])  {
    TFLG1 = ICintmask[0];       // clear Ignition IC interrupt flag
    TIE |= ICintmask[0];        // enable Ignition IC interrupt
    t_enable_IC[0] = 0xFFFFFFFF;
  }
  if(lmms > t_enable_IC[1])  {
    TFLG1 = ICintmask[1];       // clear Ignition IC2 interrupt flag
    TIE |= ICintmask[1];        // enable Ignition IC2 interrupt
    t_enable_IC[1] = 0xFFFFFFFF;
  }
  if(lmms > t_chgoff)  {
	  // cease charging ignition coil after fuel has dispersed
	  TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
	  CFORC |= 0x20;           // force output in OL5 onto OC pin
	  if(inpram.DualSpkOptn)  {
	    TCTL1 = (TCTL1 & 0xFE) | SPK;  // set OC o/p in OL4
	    CFORC |= 0x10;           // force output in OL4 onto OC pin
	  }
	  IgnOCpinstate = SPK;
	  t_chgoff = 0xFFFFFFFF;
  }
  // check for turning on pwm duty cycle
  if(!(outpc.engine & 0x02))  {   // if engine not cranking
  	if(!pwm1_on)  {               // and haven't started pwm  
  		if(lmms > pwm1_time)  {
  			pwm1_on = 1;
  			PWMPER2 = inpram.InjPWMPd;   // set PWM period (us)
  			// set PWM duty (on time)
  			PWMDTY2 = (unsigned char)((inpram.InjPWMDty * PWMPER2) / 100);
  		}
  	}
  	if(!pwm2_on)  {               // and haven't started pwm  
  		if(lmms > pwm2_time)  {
  			pwm2_on = 1;
  			PWMPER4 = inpram.InjPWMPd;   // set PWM period (us)
  			// set PWM duty (on time)
  			PWMDTY4 = (unsigned char)((inpram.InjPWMDty * PWMPER4) / 100);
  		}
  	}
  }
  
  // check for IAC step pulses
  if(IdleCtl == 4) {
  	iacpwmctr += inpram.IACpwm_step;
  	if (iacpwmctr >= 100) iacpwmctr=0;
  	// 100 - IACmotor_pos = duty cycle
  	if (IACmotor_pos > iacpwmctr) *pPTMpin[2] &= ~0x04; // turn off fast idle solenoid
  	else *pPTMpin[2] |= 0x04;                         // turn on fast idle solenoid
  	outpc.iacstep = IACmotor_pos;    // Dual purpose as idle DC - E.Fahlgren fix
  }
  else {
  	if(IAC_moving > 1)  {
  		if(lmms > motor_time)  {
  			motor_time = lmms + ((inpram.IACtstep*8)/10);  // .128 ms
  			motor_step++;
  			if(motor_step > 7)motor_step = 0;
  			if(IACmotor_pos > outpc.iacstep)  {   // cw
  				outpc.iacstep++;
  				coils = (IACCoilA[7-motor_step] << 6) | 
  					(IACCoilB[7-motor_step] << 7);
  			}
  			else if(IACmotor_pos < outpc.iacstep) {    //ccw
  				outpc.iacstep--;
  				coils = (IACCoilA[motor_step] << 6) | 
  					(IACCoilB[motor_step] << 7);
  			}
  			*pPTTpin[6] = (PTT & ~0xC0) | coils;
  			if(outpc.iacstep == IACmotor_pos)	 {
  				if(!IACmotor_reset)	 {
  				  IACmotor_reset = 1;
  				  last_iacclt = -3200;	 // this will cause to go to 
  				        // temperature based position after initial reset
  				  IAC_moving = 0;
  				}
  				else  {
  				  motor_time += 32000; // delay next move for ~4 secs
  				  IAC_moving = 1;
  				}
  				if(IdleCtl == 2)
  				  PORTB |= 0x10;  // disable current to stepper motor(bit=1)
  			}
  		}
  	}
  	else if(IAC_moving)  {
  		if(lmms > motor_time)  {
  			IAC_moving = 0;
  		}
  	}
  }
  
  // read fuel sensor data, determine freq(proportional to %alcohol)
  if(inpram.FlexFuel)  {
    fsensdat = PORTE & 0x01; 
    switch(FSensStat)  {
      case 0:
        // first time - save pin value
        last_fsensdat = fsensdat;
        FPdcounter = 0;
        FSens_Pd = 0;
        FSensStat = 1;
        break;
      case 1:
        // wait for pin hi -> lo transition to start period(freq) count
        if(last_fsensdat && !fsensdat)  {
          FPdcounter++;                 // .128 ms tics
          FSensStat = 2;
        }
        break;
      case 2:
        // continue period count
        if(last_fsensdat && !fsensdat)  {    // transition hi -> lo
          // save present count = period
          FSens_Pd = FPdcounter;        // .128 ms tics
          FPdcounter = 0;               // start new period count
        }
        FPdcounter++;
        break;
      default:
        break;
    }        // end of switch
    last_fsensdat = fsensdat;
  }
  
  // check mms to generate other clocks
  if(mms < 8)goto CLK_DONE;
  mms = 0;
  millisec++;     // actually 1.024 ms
  
 if((millisec == 49) || (millisec >= 98))  {		// 50 ms calculations  
 // get map/ maf, tps
 // map/ maf, tps lag(IIR) filters
  if((inpram.MAFOption & 0x0F) != 1)  {         // map
    tmp = inpram.mapmax - inpram.map0;
    tmp1 = ATD0DR0;
    asm {
 			    ldy  tmp
 			    ldd  tmp1
 			    EMULS
 			    ldx  #1023
 			    EDIVS
 			    sty tmp
    }
    tmp += inpram.map0;										// adcval in kPa x 10
    // apply lag filter
    tmp = tmp - outpc.map;
    tmp1 = (short)inpram.mapLF;
    asm {
 			    ldy  tmp
 			    ldd  tmp1
 			    EMULS
 			    ldx  #100
 			    EDIVS
 			    sty tmp
    }																			
    outpc.map += tmp;											// kpa x 10
  } 
  if((inpram.MAFOption & 0x0F) != 0)  {         // maf
    if(MAFPin == 0)
      utmp = ATD0DR0;
    else if(MAFPin == 1)
      utmp = ATD0DR6;
    else
      utmp = ATD0DR7;
    utmp = maffactor_table[utmp];
    // apply lag filter
    tmp = utmp - mafraw;
    tmp1 = (short)inpram.mapLF;
    asm {
 			    ldy  tmp
 			    ldd  tmp1
 			    EMULS
 			    ldx  #100
 			    EDIVS
 			    sty tmp
    }
    mafraw += tmp;                      //	 mg/ sec x 10
  }
  tmp = ATD0DR3 - inpram.tps0;
  tmp1 = inpram.tpsmax - inpram.tps0;
  asm {
 			    ldy  tmp
 			    ldd  #1000
 			    EMULS
 			    ldx  tmp1
 			    EDIVS
 			    sty tmp
  }
  // tmp is raw tps (% x 10) - apply lag filter
  tmp = tmp - outpc.tps;
  tmp1 = (short)inpram.tpsLF;
  asm {
 			    ldy  tmp
 			    ldd  tmp1
 			    EMULS
 			    ldx  #100
 			    EDIVS
 			    sty tmp
  }
  outpc.tps += tmp;																			
  // save last tps, map for calculation of tpsdot, mapdot
  outpc.tpsdot = 2 * (outpc.tps - last_tps); // % x 10 change per .1 sec
  last_tps = outpc.tps;
  if((inpram.MAFOption & 0x0F) != 1)  {           // mapdot (always use map if available)
    outpc.mapdot = 2 * (outpc.map - last_map);    // kPa x 10 change per .1 sec
    last_map = outpc.map;
  }
  else  {																					// mafmapdot (map not available)
    outpc.mapdot = 2 * (mafmap - last_map);       // kPa x 10 change per .1 sec
    last_map = mafmap;
  }  
  if(millisec > 97) {
    millisec = 0;
    tpsaclk++;                      // .10035 sec clocks
    knk_clk++;
  }
 }                                        // end 50 ms calculations
   
CLK_DONE:
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT

#pragma CODE_SEG NON_BANKED                
#endif
INTERRUPT void ISR_Ign_TimerIn(void)  {

static unsigned long t_in3[2],dt2[2],dt3[2],t3t,dt3t;
static unsigned long dts;
static long tddts,tddtpred,t2dddts;
unsigned long t_in2,t2t,dt2t;
static unsigned short st3t,sdt3t,sdtprdt;
unsigned short st2t,sdt2t;
long errt,tcnt;
short stcnt;
static unsigned long dtprdt;
static unsigned char missed_pulse[2],xtra_pulse[2],no_chk_pulses,
  no_wheel_teeth,nsynch,savovflo;
unsigned int rpm_pred=0,rpm=0;
unsigned long ltmp1,ltmp2,dtpredx;
unsigned short stmp1,stmp2,TCICus,tcIC;
char sched_both,save_page,sync_flg;
unsigned char ctmp1,mask_save,ix;
long old_charge;
union ultyp {
  unsigned long ltmp;
  unsigned int pltmp[2];
} ul1, ul2;

  save_page = PPAGE;
  PPAGE = 0x3C;       // if call any subs on another page have to swtch pge
  // Just received new input pulse - 1/ cyl / 2 crank revs (1 for 2 stroke).
  //  In wheel decode mode will throw out pulses to achieve this.
  if(TFLG1 & 0x01)  {
   // interrupt from 1st IC sensor
   TFLG1 = 0x01;       // clear IC interrupt flag
   ICint = 0;
   if(inpram.DualSpkOptn == 1)
     OCint = 0;
   tcIC = *pTIC[0];
   TCICus = (tcIC/3)<<1;
   if(pulse_no == 0)  {        // 1st input pulse
	  pulse_no++;
	  OCint = 0;
	  ICt_overflow[0] = 0;
	  ICtt_ovflo = 0;
	  t_in3[0] = TCICus;
	  dt2[0] = 0;
	  dt3[0] = 0;       // time difference between pulses, us
	  outpc.dt3 = dt3[0];    // for HP calculations
	  dtpred = dt3[0];  // predicted time difference for next pulse
	  tddtpred = 0;
	  t2dddts = 0;
	  ign_state[0] = 0;
	  missed_pulse[0] = 0;
	  xtra_pulse[0] = 0;
	  mask_teeth = 0;
	  nsynch = 0;
	  cam_sync = 0;
	  if(inpram.no_skip_pulses > 2)
	    no_chk_pulses = inpram.no_skip_pulses;
	  else
	    no_chk_pulses = 2;
	  if(inpram.DualSpkOptn == 1)
	    no_chk_pulses++;   // else have to use pulse_no[ICint]
	  goto IC_RET;
   }
  }
  else if(inpram.DualSpkOptn == 1)  {
    // interrupt from 2nd IC sensor (Dual Spark mode)
    TFLG1 = ICintmask[1];	       // clear IC2 interrupt flag
    ICint = 1;
    OCint = 1;
    tcIC = *pTIC[1];
    TCICus = (tcIC/3)<<1;
    // wait til synch on 1st IC
    if(!synch)  {
      ICt_overflow[1] = 0;
      t_in3[1] = TCICus;
      dt2[1] = 0;
      dt3[1] = dt3[0];
      ign_state[1] = 0;
      missed_pulse[1] = 0;
      xtra_pulse[1] = 0;
      goto IC_RET;
    }
  } 
  else  {
    // interrupt from 2nd IC sensor (cam_sync)
    ICint = 1;
    cam_sync = 1;
    goto IC_RET;
  }
  t_chgoff = 0xFFFFFFFF;
  if(synch != END_SYNCH)  {
    ICtt_ovflo = 0;
  }
  else  {			// synched
   // if synched in wheel decode - process pulses
   if(inpram.No_Teeth)  {
    // incr absol tooth no (1 = 1st tooth after missing teeth)
    ctmp1 = 1 + mask_teeth;
    tooth_no = tooth_no + ctmp1;
    if(tooth_no > last_tooth)
      tooth_no = tooth_no - last_tooth;     // reset tooth number
    // no. teeth passed since last tach IC
    no_wheel_teeth = no_wheel_teeth + ctmp1;
    if(tooth_no == 1)
      // tooth 1 = 1st tooth after missing teeth
      ctmp1 += inpram.No_Miss_Teeth;
    // calculate time since last entered here
    savovflo = ICtt_ovflo;
    if(inpram.No_Teeth > 40)  {
      // use shorts to speed cpu time for many-teeth wheels
      st2t = st3t;
      st3t = TCICus;
      sdt2t = sdt3t;
      if(!(TFLG2 & 0x80) || (TCNT < tcIC))  {  // check for t overflow isr pending
        // no pending overflow isr or IC occurred < overflow
        if(!ICtt_ovflo)
          sdt3t = st3t - st2t;
        else if(ICtt_ovflo > 1)  {
          ign_reset();
          goto IC_RET;
        } 
        else  {
          sdt3t = st3t + (21845 + (int)(21845 - st2t));
          ICtt_ovflo = 0;
        }
      }
      else  {
        // pending overflow isr and IC after overflow
        if(ICtt_ovflo > 0)  {
          ign_reset();
          goto IC_RET;
        } 
        sdt3t = st3t + (21845 + (int)(21845 - st2t));
        ICtt_ovflo = -1;
      }
    } 
    else  {
      // use longs to hold large times for few-tooth wheels at lo rpm
      t2t = t3t;
      t3t = TCICus;
      // Need to test for overflow because overflow ISR < priority than
      //  IC ISR and might be pending, so ICtt_ovflo not up to date.
      dt2t = dt3t;
      if(!(TFLG2 & 0x80) || (TCNT < tcIC))  {  // check for t overflow isr pending
        // no pending overflow isr or IC occurred < overflow
        if(!ICtt_ovflo)
          dt3t = t3t - t2t;
        else  {
          asm {
            ldab ICtt_ovflo 
            clra
            ldy  #43690
            EMUL
            sty  ul1.pltmp[0]
            std  ul1.pltmp[1]
          };
          dt3t = t3t + ul1.ltmp - t2t;
          ICtt_ovflo = 0;
        }
      }
      else  {
        // pending overflow isr and IC after overflow
        ICtt_ovflo++;
        asm {
          ldab ICtt_ovflo 
          clra
          ldy  #43690
          EMUL
          sty  ul1.pltmp[0]
          std  ul1.pltmp[1]
        };
        dt3t = t3t + ul1.ltmp - t2t;
        ICtt_ovflo = -1;
      }
    } 
    mask_save = mask_teeth;
    mask_teeth = 0;
    if(no_wheel_teeth < inpram.No_Skip_Teeth)  {      // non-tach tooth
      if(inpram.No_Teeth > 40)  {
        // check non-tach tooth error
        stmp2 = sdt2t * ctmp1;
        if(sdt3t > stmp2)	 {
          stmp1 = sdt3t - stmp2;
          ix = 1;
        }
        else  {
          stmp1 = stmp2 - sdt3t;
          ix = 2;
        }
        //ltmp1 = 100 * stmp1;
        asm {
          ldd  #100
          clra
          ldy  stmp1
          EMUL
          sty  ul1.pltmp[0]
          std  ul1.pltmp[1]
        };
        //ltmp2 = PulseTol * stmp2;
        asm {
          ldab  PulseTol
          clra
          ldy  stmp2
          EMUL
          sty  ul2.pltmp[0]
          std  ul2.pltmp[1]
        };
        if(ul1.ltmp > ul2.ltmp)  {
          // teeth out of synch
          if(ix == 1)  {
            xtra_pulse[0] = 0;
            // missing pulse
            missed_pulse[0]++;
            tooth_no++;
            no_wheel_teeth++;
            if((missed_pulse[0] > 1) || (tooth_no > last_tooth) ||
               (stmp1 > (3*sdt2t)))  {
              ign_reset();
              goto IC_RET;
            }
            // add missing pulse - set dt3t to last dt2t
            sdt3t = sdt2t;
            ctmp1 = 1;
            if(no_wheel_teeth >= inpram.No_Skip_Teeth)goto TACH_T;
          } 
          else  {
            missed_pulse[0] = 0;
            // false trigger
            xtra_pulse[0]++;
            if(xtra_pulse[0] > 1)  {
              ign_reset();
              goto IC_RET;
            }
            // reject false trigger
            tooth_no--;
            if(tooth_no < 1)tooth_no = last_tooth;
            mask_teeth = mask_save;
            no_wheel_teeth--;
            st3t = st2t;
            sdt3t = sdt2t;
            ICtt_ovflo = savovflo;
            goto IC_RET;         // wait for next (true) pulse
          }
        } 
        else  {
            missed_pulse[0] = 0;
            xtra_pulse[0] = 0;
        }
        // compute prediction error
        stcnt = (sdt3t - (sdtprdt * ctmp1));		// = error in us
        // refine prediction for only/ oldest output based on new tooth datum
        tcnt = IgnTimerComp[OCint] + (3*(stcnt>>1));		// = 2/3 us tics
        // only update if not very close (within 40 us) to OC
        if((IgnOCt_overflow[OCint] > 0) || (tcnt > TCNT + 60))  {
          if(tcnt < 0)  {
            IgnOCt_overflow[OCint]--;
            tcnt += 65535; 
          } 
          else if(tcnt > (long)65535)  {
            IgnOCt_overflow[OCint]++;
            tcnt -= 65535;
          }
          IgnTimerComp[OCint] = tcnt;
          *pTOC[OCint] = (unsigned short)IgnTimerComp[OCint];
        }
        if(ctmp1 > 1)
          sdt3t = sdt3t / ctmp1;
      }
      else  {
        // check non-tach tooth error
        if(ctmp1 > 1)
          ltmp2 = dt2t * ctmp1;
        else
          ltmp2 = dt2t;
        if(dt3t > ltmp2)  {
          ltmp1 = dt3t - ltmp2;
          ix = 1;
        }
        else  {
          ltmp1 = ltmp2 - dt3t;
          ix = 2;
        }
        ltmp1 = 100 * ltmp1;
        if(ltmp1 > PulseTol * ltmp2)  {
          // teeth out of synch
          if(ix == 1)  {
            xtra_pulse[0] = 0;
            // missing pulse
            missed_pulse[0]++;
            tooth_no++;
            no_wheel_teeth++;
            if((missed_pulse[0] > 1) || (tooth_no > last_tooth) ||
               (ltmp1 > (3*dt2t)))  {
              ign_reset();
              goto IC_RET;
            }
            // add missing pulse - set dt3t to last dt2t
            dt3t = dt2t;
            ctmp1 = 1;
            if(no_wheel_teeth >= inpram.No_Skip_Teeth)goto TACH_T;
          } 
          else  {
            missed_pulse[0] = 0;
            // false trigger
            xtra_pulse[0]++;
            if(xtra_pulse[0] > 1)  {
              ign_reset();
              goto IC_RET;
            }
            // reject false trigger
            tooth_no--;
            if(tooth_no < 1)tooth_no = last_tooth;
            mask_teeth = mask_save;
            no_wheel_teeth--;
            t3t = t2t;
            dt3t = dt2t;
            ICtt_ovflo = savovflo;
            goto IC_RET;         // wait for next (true) pulse
          }
        } 
        else  {
            missed_pulse[0] = 0;
            xtra_pulse[0] = 0;
        }
        // compute prediction error
        if(ctmp1 > 1)
          tcnt = (dt3t - (dtprdt * ctmp1));		// = error in us
        else
          tcnt = (dt3t - dtprdt);	          	// = error in us
        // refine prediction based on new tooth datum
        tcnt = IgnTimerComp[OCint] + ((3*tcnt)>>1);		// = 2/3 us tics
        // only update if not very close (within 40 us) to OC
        if((IgnOCt_overflow[OCint] > 0) || (tcnt > TCNT + 60))  {
          if(tcnt < 0)  {
            IgnOCt_overflow[OCint]--;
            tcnt += 65535; 
          } 
          else if(tcnt > (long)65535)  {
            IgnOCt_overflow[OCint]++;
            tcnt -= 65535;
          }
          IgnTimerComp[OCint] = tcnt;
          *pTOC[OCint] = (unsigned short)IgnTimerComp[OCint];
        }
        if(ctmp1 > 1)
          dt3t = dt3t / ctmp1;
      }
      //  Check if any teeth have come in during isr processing
      if(TFLG1 & 0x01)  {
        TFLG1 = 0x01;
        mask_teeth++;
      }
      goto IC_RET;
    } 
TACH_T:
    // at a tach pulse - normalize dlt_t to 1 tooth for next time
    if(ctmp1 > 1)	 {
      dt3t = dt3t / ctmp1;
      sdt3t = sdt3t / ctmp1;
    }
    no_wheel_teeth = 0;                     // reset counter
    // check if missing teeth will occur in next interval
    if((tooth_no + inpram.No_Skip_Teeth) > inpram.No_Teeth)
      no_wheel_teeth = inpram.No_Miss_Teeth;
   }              // end wheel decode synched processing
   
   if(inpram.DualSpkOptn > 1)  {
    // wasted spark/ COP synched tach pulse processing
    // alternate outputs (note: this avoids rpm limit due to coil dwell)
    OCint = 1 - OCint;
    if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3))  {
      // check for cam still in sync
      cyl_no++;
      if(cyl_no < inpram.no_cyl)  {
        if(cam_sync)  {
          ign_reset();   // had cam sync, now out of synch
          goto IC_RET;
        } 
      }
      else  {
        cyl_no = 0;								 
        if(cam_sync)  {
          cam_sync = 0;
        } 
        else  {           
          ign_reset();   // had cam sync, now out of synch
          goto IC_RET;
        }
      }
    }
   }     // end wasted spark/ COP synched processing
  }			 // end synched processing
  
  ltch_lmms = lmms;     // latch RTI .128 ms clk at each tach pulse
  // calculate new dt between tach pulses. All times in us.
  t_in2 = t_in3[ICint];
  dt2[ICint] = dt3[ICint];
  t_in3[ICint] = TCICus;
  if(trig_ret_mode == 1)  {
    // transitioning out of trigger return mode
    if(!(TFLG2 & 0x80) || (TCNT < tcIC))  // check for t overflow isr pending
      ICt_overflow[ICint] = 0;
    else
      ICt_overflow[ICint] = -1;  // pending, so -1 will cause to clear in isr.
    // use last dt3 as prediction for next IC for this one time transition;
    //  also leave rpm as is.
    dtpred = dt3[ICint];
    trig_ret_mode = 0;	 // done with trig return til ign_reset
    goto AVE;
  }
  // Need to test for overflow because overflow ISR < priority than
  //  IC ISR and might be pending, so ICt_overflow not up to date.
  if(!(TFLG2 & 0x80) || (TCNT < tcIC))  {  // check for t overflow isr pending
    // no pending overflow isr or IC occurred < overflow
    if(!ICt_overflow[ICint])
      dt3[ICint] = t_in3[ICint] - t_in2;
    else  {
      dt3[ICint] = t_in3[ICint] + ((long)ICt_overflow[ICint] * 43690) - t_in2;
      ICt_overflow[ICint] = 0;
    }
  }
  else  {
    // pending overflow isr and IC after overflow
    dt3[ICint] = t_in3[ICint] + ((long)(ICt_overflow[ICint] + 1) * 43690) - t_in2;
    ICt_overflow[ICint] = -1;      // pending, so -1 will cause to clear in isr
  }
  
if(inpram.No_Teeth)  {
    sync_flg = 0;
    switch(synch)  {

      // Establish synch in wheel decode mode
      case 0:
        if(dt3[0] > dt2[0])
          ltmp1 = dt3[0] - dt2[0];
        else
          ltmp1 = dt2[0] - dt3[0];
        ltmp1 = 100 * ltmp1;
        if(ltmp1 < PulseTol * dt2[0])  {
          nsynch++;
          if(nsynch >= 1)	 {  // have consecutive normal teeth
            if(((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3))
                && (inpram.No_Miss_Teeth == 0))  {
              // look for cam sync - in between last tooth and tooth 1
              if(cam_sync)  {
                cam_sync = 0;
                tooth_no = 1;         // 1st tooth after cam_sync
                no_wheel_teeth = 0;
                OCint = 0;						// fire on 1st output
                if(!inpram.Delay_Teeth)  {
                  sync_flg = 1;		    // at adv_offset degx10 btdc
                  break;
                } 
                else
                  synch = 2;          // wait delay teeth
              }
              goto IC_RET;            // keep looking for cam_sync
            }
            else  {
              synch = 1;        // look for missing teeth
            }
          }
        }
        else                    // not normal tooth
          nsynch = 0;
        cam_sync = 0;
        goto IC_RET;

      // Look for missing teeth
      case 1:
        if(dt3[0] > dt2[0])
          ltmp1 = dt3[0] - dt2[0];
        else
          ltmp1 = dt2[0] - dt3[0];
        ltmp1 = 100 * ltmp1;
        if(ltmp1 > PulseTol * dt2[0])  {
          // not normal tooth - check if missing tooth
          ltmp2 = (inpram.No_Miss_Teeth + 1) * dt2[0];
          if(dt3[0] > ltmp2)
            ltmp1 = dt3[0] - ltmp2;
          else
            ltmp1 = ltmp2 - dt3[0];
          if(ltmp1 < PulseTol * ltmp2)  {
        	  // passed exact no. missing teeth and got pulse from next normal tooth
        	  tooth_no = 1;       // 1st tooth after missing teeth
        	  no_wheel_teeth = 0;
        	  // dt for normal tooth
        	  dt3[0] = dt3[0] / (inpram.No_Miss_Teeth + 1); 
        	  if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3))  {
        	    // cam sync must be set so as to occur during missing teeth
        	    if(!cam_sync && (inpram.no_cyl == 2))  {   // 2 cyl COP
        	        // no cam - fire 2nd output first
        	        OCint = 1;
        	    }
        	    else  {
        	      // wasted spark - fire 1st output first
        	      OCint = 0;
        	    }
        	    cam_sync = 0;  
        	  } 
        	  else		 // no cam signal
        	    OCint = 0;
        	  if(!inpram.Delay_Teeth)  {
        	    sync_flg = 1;		    // at adv_offset degx10 btdc
        	    break;
        	  } 
        	  else
        	    synch = 2;          // wait delay teeth
        	}
          else  {          // not normal or missing tooth
            ign_reset();   // something out of synch - start over
            goto IC_RET;
          }
        }
        // if get here => normal tooth or synched but want Delay_Teeth
        cam_sync = 0;
        goto IC_RET;
      
      // Wait for Delay_Teeth normal teeth to start 1st tach
      case 2:
        if(dt3[0] > dt2[0])
          ltmp1 = dt3[0] - dt2[0];
        else
          ltmp1 = dt2[0] - dt3[0];
        ltmp1 = 100 * ltmp1;
        if(ltmp1 < PulseTol * dt2[0])  {
          tooth_no++;
          if(tooth_no > inpram.Delay_Teeth)  {
            sync_flg = 1;
            break;
          }
        }
        else  {
          ign_reset();	 // something out of synch - start over
          goto IC_RET;
        }
        // have not passed Delay_Teeth yet
        goto IC_RET;
      
      // Have synch 
      case END_SYNCH:
        break;
      default:
        break;
    }		// end synch switch
    if(sync_flg)  {
      synch = END_SYNCH;
      dt3t = dt3[0];
      dtprdt = dt3t;
      sdt3t = (unsigned short)dt3t;
      t3t = t_in3[0];
      st3t = (unsigned short)t3t;
      if(inpram.DualSpkOptn > 1)  {
        cyl_no = 0;
      }
      // calculate dt3 for tach pulse and set up for predictor
      dt3[0] = inpram.No_Skip_Teeth * dt3[0];
      dt2[0] = dt3[0];
      dtpred = dt3[0];
    }
    //  Check if any teeth have come in during isr processing
    if(TFLG1 & 0x01)  {
      TFLG1 = 0x01;    // clear IC interrupt flag
      mask_teeth++;
    }
}   			 // End synch check for wheel decode
else  {    // Do synch check for Non wheel decode mode
  if(synch != END_SYNCH)  {
    if((inpram.DualSpkOptn == 2) || (inpram.DualSpkOptn == 3))  {
      // wait for cam sync (between last and 1st tach pulse
      if(cam_sync)  {
        // first tach pulse after cam synch
        synch = END_SYNCH;
        dtpred = dt3[0];
        cam_sync = 0;
        cyl_no = 0;
        OCint = 0;
      }
    } 
  }
  if(pulse_no >= no_chk_pulses)  {
    // after 1st few pulses, start checking for missed/ extra pulses
    if((outpc.tpsdot < (inpram.TpsThresh >> 2)) && (PulseTol < 100))  {
      // require not strongly accelerating for unambiguous false trigger
      if(100 * dt3[ICint] < ((100 - PulseTol) * dt2[ICint]))  {
        xtra_pulse[ICint]++;
        if(xtra_pulse[ICint] > 2)  {
  	      ign_reset();
  	      goto IC_RET;
        }
	      // reject false trigger
	      t_in3[ICint] = t_in2;
	      dt3[ICint] = dt2[ICint];
	      outpc.dt3 = dt3[ICint];
	      goto IC_RET;                     // wait for next (true) pulse
      }
      else  {                   // no extra pulses
        xtra_pulse[ICint] = 0;
      }
    }
    if(outpc.tpsdot > -(int)(inpram.TpsThresh >> 2))  {
      // require not strongly decelerating for unambiguous missing pulse
      if(100 * dt3[ICint] > (100 + PulseTol) * dt2[ICint])  {
        missed_pulse[ICint]++;
        if(missed_pulse[ICint] > 2)  {
  	      ign_reset();
  	      goto IC_RET;
        }
  	    // make up for missing pulse - set dt3 to last dt2
  	    dt3[ICint] = dt2[ICint];
      }
      else  {                   // no missed pulses
        missed_pulse[ICint] = 0;
      }
    }
  }               // end pulse_no >= no_chk_pulse
}                 // end synch check for non-wheel decode
  outpc.dt3 = dt3[ICint];
  if(synch != END_SYNCH)  {
      dtpred = dt3[ICint];
  }
  
  // Predict next tach delta_t 
  errt = dt3[ICint] - dtpred;
  outpc.spare[0] = (int)((outpc.rpm * errt) / 16667); // degx10
  if(!inpram.PredOpt)  {
      // last interval pred.
      dtpred = dt3[ICint];
  }
  else  {
  		// alpha-beta-gamma filter prediction
  		dts = dtpred + ((inpram.alpha * errt) / 100);
  		tddts = tddtpred + ((inpram.beta * errt) / 100);
  		t2dddts = t2dddts + ((inpram.gamma * errt) / 100);
  		dtpred = dts + tddts + (t2dddts >> 1);
  		tddtpred = tddts + t2dddts;
  }
  if(inpram.No_Teeth)  {
    // calculate predicted time per tooth for comparison with measured  
    //  delta_t between teeth to correct OC register 
    dtprdt = dtpred / inpram.No_Skip_Teeth;
    sdtprdt = (unsigned short)dtprdt;
  }
  if(pulse_no < inpram.no_skip_pulses)  {  // skip 1st few (>=1) pulses
	  pulse_no++;
	  goto IC_RET;
  }
  else  {
    if(pulse_no < 3)
      pulse_no++;
    if((inpram.DualSpkOptn != 2) && (inpram.DualSpkOptn != 3))
      synch = END_SYNCH;  
  }
  if(synch != END_SYNCH)goto IC_RET;
  
  // calculate rpm halfway between now and next ign pulse
  if(dtpred > 100)
    rpm_pred = (unsigned int)(Rpm_Coeff / dtpred);
  else  {
  	// Noise or beyond rev limit, clear all in engine
  	ign_reset();
  	goto IC_RET;
  }
  rpm = (unsigned int)(Rpm_Coeff / dt3[ICint]);

AVE:
// lag (iir) filter
  if(synch < END_SYNCH)
    outpc.rpm = rpm;
  else
    outpc.rpm += (int)((inpram.rpmLF * ((long)rpm - outpc.rpm)) / 100);
  if(inpram.No_Teeth)  {
    if(TFLG1 & 0x01)  { //  Check any teeth have come in during isr processing
      TFLG1 = 0x01;     // clear IC interrupt flag
      mask_teeth++;
    }
  } 

// Calculate ignition parameters
if(trig_ret_mode == 2)  {
/* Example tach signal used in trigger return mode                         
//  n-2         n-1          n          n+1         n+2
//     lead edge(70 btdc)    TDC      trail edge(5 btdc)
//      \/                    |         \/
//---    --------    --------    --------    --------
//   |  |        |  |        |  |        |  |        |
//   |__|        |__|        |__|        |__|        |_
//  /\          /\          /\  /\     /\   /\			 
// IC & Spk   IC&Spk     IC&Spk IC     Spk  IC
//               |----dt-----|  |----dt-----|
//      Cranking------->|<--------Not Cranking              
//                            /\
//                     transition(trig_ret_mode=1)
// NOTE: This mode NOT used with Dual Spark or wheel decode
*/
  // Force spark NOW
  TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
  CFORC |= 0x20;           // force output in OL5 onto OC pin
  IgnOCpinstate = SPK;
  if(outpc.rpm > inpram.crank_rpm)  {    // finished cranking
    // transition to other edge for future ICs and out of trig ret mode
    if((TCTL4 & 0x03) == 0x01)
      TCTL4 = (TCTL4 & 0xFC) | 0x02;
    else if((TCTL4 & 0x03) == 0x02)
      TCTL4 = (TCTL4 & 0xFC) | 0x01;
    trig_ret_mode = 1;
    adv_us = 0;
    // don't want to do anything else til next IC on opposite edge,
    // then will set up to charge & spark in normal way, so disable
    // Ign OC interrupt.
    TIE &= ~0x20;
    goto IC_EXIT;
  }
  else  {
    adv_us = 0;
    coil_dur_set = (coil_dur * 100) << 1;  // us,  double to be sure coil 
            // sufficiently charged before next, possibly erratic IC
    charge_time = dtpred - adv_us - coil_dur_set;   // us
    // want to start charging coil_dur_set usec before next predicted IC,
    //   but don't want to discharge til next IC, so will disable OC
    //   when start charging.
  }
  tcnt = TCNT - tcIC;   // *pTIC is latched time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = tcIC + tcnt + 90;  // add some extra time for final reg setting
  IgnTimerComp[0] = tcIC + ((3*charge_time)>>1);     // 2/3 us
  if(IgnTimerComp[0] < tcnt)
    IgnTimerComp[0] = tcnt;  
  ign_state[0] = 0;
  ign_setpin[0] = CHG;  // use to set OC o/p in OL5
  goto CHECK_OVFLO;
}																	 // end trig return mode
else if(crank_trig_mode)  {        // trigger rise cranking
  // Force spark NOW
  if(OCint == 0)  {	 // Ign O/P 1
    TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
  } 
  else  {						 // Ign O/P 2
    TCTL1 = (TCTL1 & 0xFE) | SPK;         // set OC o/p in OL4
  }
  CFORC |= OCintmask[OCint];   // force output in OLn onto OC pin
  IgnOCpinstate = SPK;
  if(outpc.rpm > inpram.crank_rpm)  {    // finished cranking
    crank_trig_mode = 0;
    goto NORM_IGN;
  }
  else  {
    adv_us = 0;
    coil_dur_set = coil_dur * 100;  // us
    ltmp1 = coil_dur_set + (inpram.max_spk_dur * 100);
    if(ltmp1 > dtpred)  {
	    // scale down chg/ dischg time to fit time available
	    coil_dur_set = (coil_dur_set * dtpred) / ltmp1;
    }
    else  {
      // double dwell to be sure coil sufficiently charged before
      //  next, possibly erratic, IC
      coil_dur_set = coil_dur_set << 1;
      ltmp1 = coil_dur_set + (inpram.max_spk_dur * 100);
      if(ltmp1 > dtpred)  {
	      // reduce charge time to fit time available
	      coil_dur_set = dtpred - (inpram.max_spk_dur * 100);
      }
    } 	
    charge_time = dtpred - adv_us - coil_dur_set;   // us
    // want to start charging coil_dur_set usec before next predicted IC,
    //   but don't want to discharge til next IC, so will disable OC
    //   when start charging.
  }
  tcnt = TCNT - tcIC;   // *pTIC is latched time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = tcIC + tcnt + 90;  // add some extra time for final reg setting
  IgnTimerComp[OCint] = tcIC + ((3*charge_time)>>1);     // 2/3 us
  if(IgnTimerComp[OCint] < tcnt)
    IgnTimerComp[OCint] = tcnt;  
  ign_state[OCint] = 0;
  ign_setpin[OCint] = CHG;  // use to set OC o/p in OL5 or OL4
  goto CHECK_OVFLO;
}                  // end trigger rise cranking

NORM_IGN:
if((inpram.ICIgnOption >> 4) < 1)  {
// Normal Ignition Output
// Note: tdc is really input capture (IC)
// cyl n                                   cyl n+1
// tdc                                        tdc
//  |--------------- dtpred -------------------| 
//  |                __________                |
//  |               |          |               |
//  |               |-coil_dur-|----adv_us-----|
//  |--charge_time--|          |               |
//  |_______________|          |_______________|
// *pTIC                      Spk
//
 adv_us = ((long)16667 * ic_adv_deg) / rpm_pred;
 if(inpram.DualSpkOptn > 1)
   dtpredx = dtpred << 1;   // wasted spk/ COP use 2 cycles for dwell, spk
               // so when cyl 1 tach arrives, set up spk for cyl3, etc.
 else
   dtpredx = dtpred;
 // The following limits max adv to 60 deg at 10krpm for 8 cyl and
 //  much more for < rpm, < no. cyl. It provides enough calculation
 //  time to do a prediction before the spark event.
  if(adv_us > (long)(dtpredx - 500))
	  adv_us = (long)(dtpredx - 500);
  // calculate time to start charging coil
  coil_dur_set = coil_dur * 100;   // us
  if(!inpram.DualSpkOptn)
    ltmp1 = coil_dur_set + (inpram.max_spk_dur * 100);
  else  {
    ltmp1 = coil_dur_set + adv_us;
    if(adv_us < 10)adv_us = 10;	 // don't want -adv - set sensor to avoid
  }
  if(ltmp1 > dtpredx)  {
	  // scale down chg/ dischg time to fit time available
	  coil_dur_set = (coil_dur_set * dtpredx) / ltmp1;
  }	
  if(inpram.No_Teeth)  {
    if(TFLG1 & 0x01)  { //  Check any teeth have come in during isr processing
      TFLG1 = 0x01;     // clear IC interrupt flag
      mask_teeth++;
    }
  }
  charge_time = (long)(dtpredx - adv_us - coil_dur_set);   // us
  if(!inpram.DualSpkOptn)  {
   old_charge = 0;
   if(OCint == 0)
     IgnOCpinstate = (PTIT & 0x20) >> 5;   // present pin state
   else
     IgnOCpinstate = (PTIT & 0x10) >> 4;   // present pin state
   ltmp1 = dtpred - (dtpred >> 4);
   if(charge_time > (long)ltmp1)  {
     // charge time is > 6% of next IC; due to normal fluctuation
     // this could result in starting charge after IC.
     if(charge_time > dtpred)  {
       // In this case switch to 'this cylinder' mode (spark on 
       // this cylinder)
       adv_us += dtpred;
       if(IgnOCpinstate == SPK)  {
         charge_time -= dtpred;
       }
       else  {
         ign_state[OCint] = 2;
       }
     }
     else  {
       if(IgnOCpinstate == CHG)  {
         // extend coil dur to ensure we start charging before dtpred
         coil_dur_set += charge_time - ltmp1;
         charge_time = ltmp1;
         ign_state[OCint] = 0;
       }
       else  {
         old_charge = charge_time;
         charge_time = 0;
         adv_us = dtpred + adv_us;
       }
     }
   }
  }
}
else  {
// EDIS Ignition Output
// cyl n                                   cyl n+1
// tdc                                        tdc
//  |-------- dtpred ---------------------------| 
//  |-delay- __________                         |
//  |       |          |                        |
//  |       |---SAW----|                        |
//  |       |          |                        |
//  |_______|          |________________________|
// *pTIC  
// NOTE: This mode NOT used with Dual Spark or wheel decode
//
  adv_us = 0;
  // send SAW pulse after delay, so we don't send
  // SAW while still sparking (send at 64us atdc)
  charge_time = 64;   //us
  // SAW pulse calculation
  ltmp1 = 1536 - ((long)256 * ic_adv_deg) /  100;
  // multispk EDIS
  if((inpram.ICIgnOption >> 4) == 2)  {
    if(first_edis)  {		 // 1st SAW for multispk is 2048 us
      first_edis = 0;
      ltmp1 = 2048;      // 1024 if ever have 10 cyl
    }
    else if(outpc.rpm < 1200)  {      
      ltmp1 += 2048;     // 1024 if ever have 10 cyl
    }
  }
  coil_dur_set = ltmp1;
  IgnOCpinstate = SPK;
  tcnt = TCNT - tcIC;   // *pTIC is latched time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = tcIC + tcnt + 90;  // add some extra time for final reg setting
  IgnTimerComp[0] = tcIC + ((3*charge_time)>>1);     // 2/3 us
  if(IgnTimerComp[0] < tcnt)
    IgnTimerComp[0] = tcnt;  
  ign_state[0] = 0;
  ign_setpin[0] = CHG;  // use to set OC o/p in OL5
  goto CHECK_OVFLO;
}
 
 //  Only get here for normal ignition output
  if(inpram.No_Teeth)  {
    if(TFLG1 & 0x01)  { //  Check any teeth have come in during isr processing
      TFLG1 = 0x01;    // clear IC interrupt flag
      mask_teeth++;
    }
  } 
 if(!inpram.DualSpkOptn)	 {
  if(IgnOCpinstate == SPK)  {    // OC output is sparking
	 tcnt = TCNT - tcIC;   // *pTIC is latched time at entry to ISR
	 if(tcnt < 0)tcnt += 65535;
	 tcnt = tcIC + tcnt + 20; // add extra time for final reg setting
	 IgnTimerComp[OCint] = tcIC + ((3*charge_time)>>1);     // 2/3 us
	 if(IgnTimerComp[OCint] < tcnt)  {
		// transitioning to early chge (large adv) state
		ign_state[OCint] = 2;
		// Charge NOW
		if(OCint == 0)  {
		  TCTL1 = (TCTL1 & 0xFB) | (CHG << 2);  // set OC o/p in OL5
		} 
		else  {
		  TCTL1 = (TCTL1 & 0xFE) | CHG;         // set OC o/p in OL4
		}
		CFORC |= OCintmask[OCint];  // force output in OLn onto OC pin
		IgnOCpinstate = CHG;
		// Set time to spark
		ltmp1 = (dtpred - adv_us);
		IgnTimerComp[OCint] = tcIC + ((3*ltmp1)>>1);   // 2/3 us
		ign_setpin[OCint] = SPK;  // use to set OC o/p in OL5 or OL4
		if(old_charge > 0)  {
		  charge_time = old_charge - ltmp1;
		}
		else  {
		  // next chge time - predicted to be prior to next pulse input                                                                                 
		  charge_time += adv_us;     // rel to spk event
		}
	 }
	 else  {  // normal state - wait til time to charge
		 ign_state[OCint] = 0;
		 ign_setpin[OCint] = CHG;  // use to set OC o/p in OL5 or OL4
	 }
  }        // end OC output is sparking
  else  {  // OC output is charging
	 if(charge_time <= 0)  {		// => early chge, OC interrupt enabled
		// in early chge (large adv) state - stay there
		ign_state[OCint] = 2;
		// Reset OC reg to update to latest spk time
		IgnTimerComp[OCint] = tcIC + ((3*(dtpred - adv_us))>>1);   // 2/3 us
		// next chge time
		charge_time += adv_us;   // rel to spk event
		ign_setpin[OCint] = SPK;
	 }
	 else  {
		if(ign_state[OCint] == 2)  {
		  // transitioning to normal charge state
		  ign_state[OCint] = 0;
		  // Reset OC reg to update to latest spk time
		  IgnTimerComp[OCint] = tcIC + ((3*(dtpred - adv_us))>>1);   // 2/3 us
		  ign_setpin[OCint] = SPK;
		}
		else  {
		  if(ic_adv_deg >= 0)  {
		    // IC came earlier than predicted and haven't sparked yet.
	 FORCE_SPK:	    
		    // Force spark NOW
		    if(OCint == 0)  {
		      TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
		    } 
		    else  {
		      TCTL1 = (TCTL1 & 0xFE) | SPK;         // set OC o/p in OL4
		    }
		    CFORC |= OCintmask[OCint];  // force output in OLn onto OC pin
		    IgnOCpinstate = SPK;
		    IgnTimerComp[OCint] = tcIC + ((3*charge_time)>>1);  // 2/3 us
		    ign_setpin[OCint] = CHG;
		    ign_state[OCint] = 0;
		  }
		  else  {
		    //  desired advance is atdc - update register to latest spk time
		    IgnTimerComp[OCint] = tcIC - ((3 * adv_us)>>1);   // 2/3 us
		    tcnt = TCNT - tcIC;   // *pTIC is latched time at entry to ISR
		    if(tcnt < 0)tcnt += 65535;
		    tcnt = tcIC + tcnt + 90;	 // add some extra time to exit isr
		    if(IgnTimerComp[OCint] < tcnt) goto FORCE_SPK;
		    charge_time += adv_us;   // rel to spk event
		    ign_setpin[OCint] = SPK;
		    ign_state[OCint] = 1;
		  }
		}
	 }
  }        // end OC output is charging
 } 
 else  {	 // dual ign outputs (dual/wasted/COP) simplifies since double the
      //  period means entire dwell/spark sequence over by next IC for this o/p
   IgnTimerComp[OCint] = tcIC + ((3*charge_time)>>1);     // 2/3 us
   ign_state[OCint] = 0;
   ign_setpin[OCint] = CHG;  // use to set OC o/p in OL5 or OL4
 }
  // check if v. short OC reg occurs after TCNT (=free running, common
  //   timer).
  tcnt = TCNT - tcIC;   // tcIC is latched time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = tcIC + tcnt + 90;	 // add some extra time to get out of isr
  if(IgnTimerComp[OCint] < tcnt)  {
    if(ign_setpin[OCint] == CHG)  {
      // make spk time in OC come out exact
      ltmp1 = ((tcnt - IgnTimerComp[OCint])<<1)/3;         // us
      coil_dur_set -= ltmp1;
      if(coil_dur_set < 2)coil_dur_set = 2;
    }
    IgnTimerComp[OCint] = tcnt;  
  }
  if(inpram.No_Teeth)  {
    if(TFLG1 & 0x01)  { //  Check any teeth have come in during isr processing
      TFLG1 = 0x01;    // clear IC interrupt flag
      mask_teeth++;
    }
  } 

CHECK_OVFLO:
   coil_dur_out[OCint] = coil_dur_set;
   if(IgnTimerComp[OCint] > (long)65535)  {
    IgnOCt_overflow[OCint] = IgnTimerComp[OCint] >> 16;
    IgnTimerComp[OCint] &= 0xFFFF;
   }
   else
    IgnOCt_overflow[OCint] = 0;
   if(!IgnOCt_overflow[OCint])  {
    // Set Ign OC pin & enable interrupt
    if(OCint == 0)  {
      TCTL1 = (TCTL1 & 0xFB) | (ign_setpin[OCint] << 2);
    } 
    else  {
      TCTL1 = (TCTL1 & 0xFE) | ign_setpin[OCint];
    }
    *pTOC[OCint] = (unsigned short)IgnTimerComp[OCint];  // load OC compare register
    TIE |= OCintmask[OCint];
    TFLG1 = OCintmask[OCint];	 // clear ign OC interrupt flag
   }
   else  {  // Disable Ign OC interrupt until ovflow done
    TIE &= ~OCintmask[OCint];
   }
  if(inpram.No_Teeth)  {
    if(TFLG1 & 0x01)  { //  Check any teeth have come in during isr processing
      TFLG1 = 0x01;    // clear IC interrupt flag
      mask_teeth++;
    }
  } 
  // Set up for Injector squirt(s)
  XTcalc = 2;		  // just got tach pulse - set flag to do X,tau calc in main loop.
  if(igncount == 0)
  	asecount++;
  egocount++;

  // Turn on fuel pump
  PORTE |= 0x10;
  outpc.engine |= 0x01;   // set engine running

  if(outpc.engine & 0x02)goto SCHED_SQUIRT;  // if engine cranking
  igncount++;
  if(igncount < inpram.Divider)
    goto IC_EXIT;				  // skip Divider tach pulses 

SCHED_SQUIRT:
  igncount = 0;
  if(inpram.RevLimOption == 2)  {
    if(outpc.rpm > inpram.RevLimRpm2)
      // Cut fuel for Over Rev
	    cut_fuel = 1;
    else if(outpc.rpm < inpram.RevLimRpm1)
      // restore fuel
      cut_fuel = 0;
    if(cut_fuel)  {
      outpc.pw1 = 0;   // Jedrik fix - let world know injectors are off
      outpc.pw2 = 0;
	    goto IC_EXIT;
    }
  }
  if(((outpc.engine & 0x02) && (inpram.no_cyl <= 8))|| !inpram.Alternate)  { 
    // if cranking or no alternate option
  	sched_both = 1;
  	goto SCHED1;
  }
  sched_both = 0;
  altcount = 1 - altcount;
  if(altcount)
  	goto SCHED2;

SCHED1:
  if(pwcalc1 == 0)  {
    outpc.pw1 = 0;
    goto IC_EXIT;
  }
  // Turn On Inj1
  TCTL2 |= 0x04;   // set output hi in OL1
  CFORC |= 0x02;   // force high
  if(!inpram.DualSpkOptn) {
    PWMPER2 = inpram.InjPWMPd;   // set PWM period (us)
    PWMDTY2 = PWMPER2;           // set PWM duty to 100 %
    pwm1_on = 0;
    pwm1_time = lmms + ((inpram.InjPWMTim*8)/10); // .128 ms
    PWMCNT2 = 0x00;  // clear counter
    PWME |= 0x04;    // enable PWM2
  }
  // Set up to turn Off Inj1 when get to pw us
  outpc.pw1 = pwcalc1;
  Fl1TimerComp = TCNT + ((3*(long)outpc.pw1)>>1);  // 2/3 us
  Fl1OCt_overflow = (unsigned short)(Fl1TimerComp >> 16); 
  TC1 = (unsigned short)(Fl1TimerComp & 0xFFFF); // load OC compare reg
  if(Fl1OCt_overflow <= 1)  {
    // don't need to handle <= 1 overflow because TCNT will not
    // = TimerComp until after the overflow as long as pw < ~43 ms
		Fl1OCt_overflow = 0;
    TCTL2 = TCTL2 & 0xFB;    // set output lo in OL1
    TIE |= 0x02;             // Enable Inj1 OC interrupt
    TFLG1 = 0x02;	           // clear OC interrupt flag
  }
  else  {  // Disable Inj1 OC interrupt until ovflows done
		// do below to allow enough time to set OC pin in overflow isr
		if(TC1 < 30)TC1 = 30;   
  	TIE &= ~0x02;
  }
  *pPTMpin[3] |= 0x08;   // turn on inj led
  outpc.squirt |= 0x01;  // inj1 squirting
  
  if(!sched_both)
  	goto IC_EXIT;
  if(inpram.No_Teeth)  {
    if(TFLG1 & 0x01)  { //  Check any teeth have come in during isr processing
      TFLG1 = 0x01;    // clear IC interrupt flag
      mask_teeth++;
    }
  } 

SCHED2:
  if(pwcalc2 == 0)  {
    outpc.pw2 = 0;
    goto IC_EXIT;
  }
  // Turn On Inj2
  TCTL2 |= 0x40;   // set output hi in OL3
  CFORC |= 0x08;   // force high
  if(!inpram.DualSpkOptn) {
    PWMPER4 = inpram.InjPWMPd;   // set PWM period (us)
    PWMDTY4 = PWMPER4;           // set PWM duty to 100 %  
    pwm2_on = 0;
    pwm2_time = lmms + ((inpram.InjPWMTim*8)/10); // .128 ms
    PWMCNT4 = 0x00;  // clear counter
    PWME |= 0x10;    // enable PWM4
  }
  // Set up to turn Off Inj2 when get to pw us
  outpc.pw2 = pwcalc2;
  Fl2TimerComp = TCNT + ((3*(long)outpc.pw2)>>1);  // 2/3 us
  Fl2OCt_overflow = (unsigned short)(Fl2TimerComp >> 16); 
  TC3 = (unsigned short)(Fl2TimerComp  & 0xFFFF); // load OC compare reg
  if(Fl2OCt_overflow <= 1)  {
    // don't need to handle <= 1 overflow because TCNT will not
    // = TimerComp until after the overflow as long as pw < ~43 ms
    Fl2OCt_overflow = 0;
    TCTL2 = TCTL2 & 0xBF;   // set output lo in OL3
    TIE |= 0x08;            // Enable Inj2 OC interrupt
    TFLG1 = 0x08;	          // clear OC interrupt flag
  }
  else  {  // Disable Inj2 OC interrupt until ovflows done
		// do below to allow enough time to set OC pin in overflow isr
		if(TC3 < 30)TC3 = 30;   
    TIE &= ~0x08;
  }
  *pPTMpin[3] |= 0x08;   // turn on inj led
  outpc.squirt |= 0x02;  // inj2 squirting

IC_EXIT:
  if(inpram.No_Teeth)  {
    //  Check if any teeth have come in during isr processing
    PPAGE = save_page;
    if(TFLG1 & 0x01)  {
      TFLG1 = 0x01;		// clear IC interrupt flag
      mask_teeth++;
    }
    if(TFLG1 & 0x01)  {
      TFLG1 = 0x01;		// clear IC interrupt flag
      mask_teeth++;
    }
    if(TFLG1 & 0x01)  {
      TFLG1 = 0x01;		// clear IC interrupt flag
      mask_teeth++;
    }
    return;
  } 
  else  {
    // Set up to renable IC interrupt in Timer ISR after a part of the time
    //  to next IC has elapsed to avoid coil ringing noise false interrupts
    ltmp1 = ((inpram.ICISR_pmask * dtpred) / 100) + 
        ((unsigned long)inpram.ICISR_tmask * 100); 
    t_enable_IC[ICint] = ltch_lmms + ((ltmp1 * 8) / 1000);	// .128 ms units
    TIE &= ~ICintmask[ICint];   // disable interrupt
  }
IC_RET:
  TFLG1 = ICintmask[ICint];		// clear IC interrupt flag
  PPAGE = save_page;
  return;		
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_TimerOverflow(void)  {
  // Get display seconds from continuously running TCNT
  TC_ovflow++;
  if(TC_ovflow >= TC_ovfla[TC_ov_ix])  {
    // update seconds to send back to PC
    outpc.seconds++;
    if(TC_ov_ix >= 59)  {
      TC_ov_ix = 0;
      TC_ovflow = 0;
    }
    else
      TC_ov_ix++;
  }

  // Handle Ign IC stuff
  if(pulse_no == 0)  {
  	// clear Timer Overflow interrupt flag (TOF)
  	TFLG2 = 0x80;
  	return;
  }
  else  {
  	ICt_overflow[0]++;
  	ICt_overflow[1]++;
  	ICtt_ovflo++;
  }

  // Handle Ign OC stuff
  // Example:  *pTIC = 30K us,  dt = 80K, *pTIC + dt = 110k
  //    OC reg = 110K - 65K = 45K  => overflow = 1
  //    Timer counter goes from 30K to 65K/ 0 to 45K,
  //    elapsed time = (65 - 30 = 35K) + 45K = 80K = dt
  if(IgnOCt_overflow[0] > 0)  {
    IgnOCt_overflow[0]--;
    if(IgnOCt_overflow[0] == 0)  {
		  // check if v. short OC reg occurs after TCNT (=free running,
		  //   common timer). Enter this isr with TCNT=0, so if needed,
		  //   add some extra time to get out of isr before OC time reached
		  if(IgnTimerComp[0] < TCNT + 18)
		    IgnTimerComp[0] = TCNT + 18;  
		  TC5 = (unsigned short)IgnTimerComp[0];  // load OC compare register
		  // set/ enable ign OC pin and interrupt
		  TCTL1 = (TCTL1 & 0xFB) | (ign_setpin[0] << 2);
		  TIE |= 0x20;
		  TFLG1 = 0x20;	 // clear ign OC interrupt flag
    }
  }
  if(inpram.DualSpkOptn && (IgnOCt_overflow[1] > 0))  {
    IgnOCt_overflow[1]--;
    if(IgnOCt_overflow[1] == 0)  {
		  // check if v. short OC reg occurs after TCNT (=free running,
		  //   common timer). Enter this isr with TCNT=0, so if needed,
		  //   add some extra time to get out of isr before OC time reached
		  if(IgnTimerComp[1] < TCNT + 18)
		    IgnTimerComp[1] = TCNT + 18;  
		  TC4 = (unsigned short)IgnTimerComp[1];  // load OC compare register
		  // set/ enable ign OC pin and interrupt
		  TCTL1 = (TCTL1 & 0xFE) | ign_setpin[1];
		  TIE |= 0x10;
		  TFLG1 = 0x10;	 // clear ign OC interrupt flag
    }
  }

  // Handle Inj OC stuff
  if(Fl1OCt_overflow > 0)  {
    Fl1OCt_overflow--;
    if(Fl1OCt_overflow == 0)  {
  	  // set/ enable inj1 OC pin and interrupt
      TCTL2 = TCTL2 & 0xFB;      // set output lo in OL1
  	  TIE |= 0x02;
      TFLG1 = 0x02;	 // clear OC interrupt flag
    }
  }
  if(Fl2OCt_overflow > 0)  {
    Fl2OCt_overflow--;
    if(Fl2OCt_overflow == 0)  {
  	  // set/ enable inj2 OC pin and interrupt
      TCTL2 = TCTL2 & 0xBF;      // set output lo in OL3
  	  TIE |= 0x08;
      TFLG1 = 0x08;	 // clear OC interrupt flag
    }
  }

  // clear timer overflow interrupt flag (TOF)
  TFLG2 = 0x80;
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_Ign_TimerOut(void)  {
long tcnt;
char save_page,OCchan;

// Enter here when ign OC reg match
  if(TFLG1 & 0x20)  {
    OCchan = 0;
    IgnOCpinstate = (TCTL1 & 0x04) >> 2;
  }
  else  {
    OCchan = 1;
    IgnOCpinstate = TCTL1 & 0x01;
  }
  save_page = PPAGE;
  PPAGE = 0x3C;       // if call any subs on another page have to swtch pge
  if(IgnOCpinstate == CHG)  {
  	// Coil has just started charging
  	if(trig_ret_mode || crank_trig_mode)  {
  		// disable ign OC interrupt - will spk on next IC
  		TIE &= ~OCintmask[OCchan];
  		goto OC_RET;
  	}
  	IgnTimerComp[OCchan] = *pTOC[OCchan] + ((3*coil_dur_out[OCchan])>>1); // 2/3 us
  	ign_setpin[OCchan] = SPK;
  }
  else  {
  	// Coil has just started discharging (sparking)
	  if(ign_state[OCchan] == 0)  {
  		// disable ign OC interrupt - chge after next IC pulse
  		TIE &= ~OCintmask[OCchan];
  		goto OC_RET;
	  }
	  else  {  // want to charge after done with spark
  		IgnTimerComp[OCchan] = *pTOC[OCchan] + ((3*charge_time)>>1);  // 2/3 us
	  }
	  ign_setpin[OCchan] = CHG;  // use to set OC o/p in OLn
  }
  // check if v. short OC reg occurs after TCNT
  tcnt = TCNT - *pTOC[OCchan];   // pTOC is OC time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = *pTOC[OCchan] + tcnt + 12;
  if(IgnTimerComp[OCchan] < tcnt)IgnTimerComp[OCchan] = tcnt;
  if(IgnTimerComp[OCchan] > (long)65535)  {
    IgnOCt_overflow[OCchan] = IgnTimerComp[OCchan] >> 16;
    IgnTimerComp[OCchan] &= 0xFFFF;
  }
  else
    IgnOCt_overflow[OCchan] = 0;
  if(!IgnOCt_overflow[OCchan])  {     
    *pTOC[OCchan] = (unsigned short)IgnTimerComp[OCchan]; // load ign OC register
    // enable Ign OC pin
    if(OCchan == 0)
      TCTL1 = (TCTL1 & 0xFB) | (ign_setpin[OCchan] << 2);
    else
      TCTL1 = (TCTL1 & 0xFE) | ign_setpin[OCchan];
  }
  else  {     // Disable Ign OC intrpt til ovflow done
  	TIE &= ~OCintmask[OCchan];
  }

OC_RET:
  // clear ign OC interrupt flag
  TFLG1 = OCintmask[OCchan];
  PPAGE = save_page;
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_Inj1_TimerOut(void)  {

// Enter here when OC match to cease firing Inj1.
// At entry here the pin will automatically turn off injector;
  // also turn off pwm1 (this not necessary since nanded)
  PWME &= ~0x04;
  outpc.squirt &= ~0x01; // inj1 stopped squirting
  if(!(outpc.squirt & 0x03))	 // neither inj squirting
    *pPTMpin[3] &= ~0x08;      // turn off inj led
  // Disable Inj1 OC interrupt - will re-enable on next tach pulse
  TIE &= ~0x02;
  // clear OC interrupt flag
  TFLG1 = 0x02;
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_Inj2_TimerOut(void)  {

// Enter here when OC match to cease firing Inj2.
// At entry here the pin will automatically turn off injector;
  // also turn off pwm2 (this not necessary since nanded)
  PWME &= ~0x10;
  outpc.squirt &= ~0x02; // inj2 stopped squirting
  if(!(outpc.squirt & 0x03))	 // neither inj squirting
    *pPTMpin[3] &= ~0x08;      // turn off inj led
  // Disable Inj2 OC interrupt - will re-enable on next tach pulse
  TIE &= ~0x08;
  // clear OC interrupt flag
  TFLG1 = 0x08;
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        
#endif

int move_IACmotor(void)  {
unsigned char coils;
unsigned long ultmp;
  if(IAC_moving || (outpc.iacstep == IACmotor_pos))return 0;
  // set up the motor move
  DISABLE_INTERRUPTS
  ultmp = lmms;
  ENABLE_INTERRUPTS
  motor_time = ultmp + ((inpram.IACtstep*8)/10);  // .128 ms
  motor_step++;
  if(motor_step > 7)motor_step = 0;
  if(IACmotor_pos > outpc.iacstep)  {    // cw
  	outpc.iacstep++;
  	coils = (IACCoilA[7-motor_step] << 6) |
  	  (IACCoilB[7-motor_step] << 7);
  }
  else  {                                // ccw
  	outpc.iacstep--;
  	coils = (IACCoilA[motor_step] << 6) | 
  	  (IACCoilB[motor_step] << 7);
  }
  PORTB &= ~0x10;  // enable current to motor(bit=0)
  DISABLE_INTERRUPTS
  *pPTTpin[6] = (PTT & ~0xC0) | coils;
  ENABLE_INTERRUPTS
  IAC_moving = 2;
  return 1;
}


/**************************************************************************
**
** SCI Communications
**
** Communications is established when the PC communications program sends
** a command character - the particular character sets the mode:
**
** "a" = send all of the realtime display variables (outpc structure) via txport.
** "w"+<offset lsb>+<offset msb>+<nobytes>+<newbytes> = 
**    receive updated data parameter(s) and write into offset location
**    relative to start of data block
** "e" = same as "w" above, followed by "r" below to echo back value
** "r"+<offset lsb>+<offset msb>+<nobytes>+<newbytes> = read and
**    send back value of a data parameter or block in offset location
** "y" = verify inpram data block = inpflash data block, return no. bytes different.
** "b" = jump to flash burner routine and burn a ram data block into a flash 
**    data block.
** "t" = receive new data for clt/mat/ego/maf tables
** "c" = Test communications - echo back Seconds
** "Q" = Send over Embedded Code Revision Number
** "S" = Send program title.
**
**************************************************************************/
#ifndef GCC_BUILD
#pragma CODE_SEG  NON_BANKED
#endif
INTERRUPT void ISR_SCI_Comm(void)  {
  char dummy,save_page;
  int ix;
  static int vfy_fail,rd_wr,xcntr;
  static unsigned char CANid,ibuf,sendCANdat=0,next_txmode,cksum;
  unsigned char sect,nsect;
  static unsigned int txptr,tble_word,ntword;
  
#define getCANid             40
#define getTableId           41
#define setBurningParameters 99
  
  // if RDRF register not set, => transmit interrupt
  if(!(SCI0SR1 & 0x20))goto XMT_INT;

// Receive Interrupt
  // Clear the RDRF bit by reading SCISR1 register (done above), then read data
  //  (in SCIDRL reg).
  // Check if we are receiving new input parameter update
txgoal = 0;
rcv_timeout = 0xFFFFFFFF;
if(SCI0SR1 & 0x08)  {	 // check for rcv Overrun error
   txmode = 0;
   dummy = SCI0DRL;
   SCI0CR2 &= ~0xAC;   // rcv, xmt disable, interrupt disable
   return;
}

switch(txmode)  {

case 0:

  switch(SCI0DRL)  {
    case 'a':				 // send back all real time ram output variables
  next_txmode = 1;
  txmode = getCANid;
  cksum = 0;
  break;

    case 'w':		  // receive new ram input data and write into offset location;
                  //  also used for forwarding CAN msgs bet. MT & auxilliary boards.
                  //  In this CAN mode, no_bytes must be >0 and <=8.
  next_txmode = 5;
  txmode = getCANid;
  rd_wr = 1;
  break;

    case 'e':		  // same as 'w', but verify by echoing back values
  next_txmode = 5;
  txmode = getCANid;
  rd_wr = 2;
  break;

    case 'r':		  // read and send back ram input data from offset location;
                  //  also used for forwarding CAN msgs bet. MT & auxilliary boards
  next_txmode = 5;
  txmode = getCANid;
  rd_wr = 0;
  cksum = 0;
  break;

    case 'y':      // Verify that a flash data block matches a
  next_txmode = 2; //  corresponding ram data block
  txmode = getCANid;
  break;
   	
    case 'b':        // burn a block of ram input values into flash;
                  //  also used for forwarding CAN msgs bet. MT & auxilliary boards
  next_txmode = setBurningParameters;
  txmode      = getCANid;
  break;

    case 't':        // update a flash table with following serial data
  txmode = 20;
  cksum = 0;
  break;

    case '!':        // start receiving reinit/reboot command
  txmode = 30;
  break;

    case 'c':        // send back seconds to test comms
  txcnt = 0;         
  txmode = 1;
  txgoal = 2;        // seconds is 1st 2 bytes of outpc structure
  txbuf.seconds = outpc.seconds;
  SCI0DRL = *(char *)&txbuf;
  SCI0CR2 &= ~0x24;   // rcv, rcvint disable
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    case 'Q':         // send code rev no.
  txcnt = 0;
  txmode = 4;
  txgoal = 20;
  SCI0DRL = RevNum[0]; 
  SCI0CR2 &= ~0x24;   // rcv, rcvint disable
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    case 'S':         // send program title
  txcnt = 0;
  txmode = 5;
  txgoal = 32;
  SCI0DRL = Signature[0]; 
  SCI0CR2 &= ~0x24;   // rcv, rcvint disable
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;
  
    case 'k':         // request checksum.
  txcnt = 0;
  txmode = 0;
  txgoal = 1;
  SCI0DRL = cksum; 
  SCI0CR2 &= ~0x24;   // rcv, rcvint disable
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    default:
  break;
  }     // End of switch for received command
    break;

case getCANid: // Get CAN id for current command.
   CANid = SCI0DRL;
   if (CANid < NO_CANBOARDS && inpram.board_id_type[CANid] != 0)
     txmode = getTableId;
   else	 {
  	 // CANid wrong - kill comms since don't know where to send data
     txmode = 0;
     SCI0CR2 &= ~0xAC;   // rcv, xmt disable, interrupt disable
     kill_ser = 1;
     kill_ser_t = outpc.seconds + 10;
     return;
   }
   break;
case getTableId: // Get table id for current command.
   tble_idx = SCI0DRL;
   if(CANid != CANID_THIS_BOARD)  {
     txmode = next_txmode;
   } 
   else if(tble_idx >= NO_TBLES)  {
  	 // tble index wrong
     txmode = 0;
     //  kill comms since don't know which/ how much data
     SCI0CR2 &= ~0xAC;   // rcv, xmt disable, interrupt disable
     kill_ser = 1;
     kill_ser_t = outpc.seconds + 10;
     return;
   }
   else
     txmode = next_txmode;
   next_txmode = 0;
   if (txmode == 1) { 
     txcnt = 0;
     txgoal = tableBytes(tble_idx);
     // load all output variables into txbuf to avoid incoherent word data.
     // To work all words in structure must be word aligned, all longs
     // aligned on long boundaries.
     xcntr = 0;
     txptr = 0;
     *tableWordRam(tble_idx,0) = *((unsigned int *)(&outpc) + 0);
     *tableWordRam(tble_idx,1) = *((unsigned int *)(&outpc) + 1);
     SCI0DRL = *tableByteRam(tble_idx, 0);
     cksum += SCI0DRL; 
     SCI0CR2 &= ~0x24;   // rcv, rcvint disable
     SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
   }
   else if (txmode == setBurningParameters) { // Burn command
     if(CANid != CANID_THIS_BOARD)  {
  	   // set up single CAN message & forward to aux board
  	   can[0].cx_msg_type[can[1].cxno_in] = MSG_BURN; 
  	   can[0].cx_varblk[can[0].cxno_in] = tble_idx;
  	   can[0].cx_varoff[can[0].cxno_in] = 0;
  	   can[0].cx_dest[can[1].cxno_in] = CANid;
  	   can[0].cx_varbyt[can[0].cxno_in] = 0;		 // no data bytes
  	   // This is where (in xmt ring buffer) to put next message
  	   if(can[0].cxno_in < (NO_CANMSG - 1))
  	     can[0].cxno_in++;
  	   else
  	     can[0].cxno_in = 0;	 // overwrite oldest msg in queue
  	   // increment counter
  	   if(can[0].cxno < NO_CANMSG)
  	     can[0].cxno++;
  	   else
  	     can[0].cxno = NO_CANMSG;
  	   if(!(CANTIER & 0x07))  {
  	     // Following will cause entry to TxIsr without sending msg
  	     // since when CANTIER = 0, CANTFLG left as buff empty(>0).
  	     // If CANTIER has at least 1 int buf enabled, will enter
  	     // TxIsr automatically.
  	     CANTBSEL = CANTFLG;
           CANTIER = CANTBSEL;
  	   }  
     }
     else if(flocker == 0) { 
       flocker     = 0xCC;    // set semaphore to prevent burning flash thru runaway code
       burn_flag   = 1;
       burn_idx = tble_idx;
     }
     txmode      = 0;			 // handle burning in main loop
   } 
   else if(txmode == 2)  {
     vfy_fail = 0;
     save_page = PPAGE;
     PPAGE = 0x3C;
     for(ix = 0; ix < tables[tble_idx].n_bytes; ix++)  {
       if(*tableByteFlash(tble_idx,ix) != *tableByteRam(tble_idx,ix))
       vfy_fail++;
     }
     PPAGE = save_page;     
     txcnt = 0;
     txgoal = 2;
     SCI0DRL = *(char *)&vfy_fail;   
     SCI0CR2 &= ~0x24;   // rcv, rcvint disable
     SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
   }
   break;

case 5:
  	if(CANid != CANID_THIS_BOARD)  {
  	  if(rd_wr == 1)  {
  	    sendCANdat = 1;
  	  } 
  	  else if(rd_wr == 0)  {
  	    getCANdat = 1;
  	    ltch_CAN = lmms + 3906;   // 0.5 sec timeout to get CAN data
  	  } 
  	  else  {
  	    sendCANdat = 0;
  	    getCANdat = 0;
  	  }
  	}
  	rxoffset = (SCI0DRL << 8); // byte offset(msb) from start of inpram
  	txmode++;
  	break;

case 6:
  	rxoffset |= SCI0DRL;    // byte offset(lsb) from start of inpram
  	txmode++;
  	break;
  	
case 7:
  	rxnbytes = (SCI0DRL << 8); 	// no. bytes (msb)
  	txmode++;
  	break;

case 8:
  	rxnbytes |= SCI0DRL;		// no. bytes (lsb)
  	rxcnt = 0;
  	// check won't blow table arrays
  	if(rxoffset + rxnbytes > tableBytes(tble_idx))  {
  	  //  kill comms since don't know how much data being sent
  	  txmode = 0;
  	  SCI0CR2 &= ~0xAC;   // rcv, xmt disable, interrupt disable
  	  kill_ser = 1;
  	  kill_ser_t = outpc.seconds + 10;
  	  return;
  	}
  	if(rd_wr == 0)  {		    // read & send back input data
  	  if(getCANdat)  {
  	    txmode = 0;         // done receiving/writing all the data
  	    // set up single CAN message & forward to aux board
  	    can[0].cx_msg_type[can[1].cxno_in] = MSG_REQ; 
  	    can[0].cx_varblk[can[0].cxno_in] = tble_idx;
  	    can[0].cx_varoff[can[0].cxno_in] = rxoffset;
  	    can[0].cx_dest[can[1].cxno_in] = CANid;
  	    can[0].cx_varbyt[can[0].cxno_in] = (unsigned char)rxnbytes;
  	    // This is where (in xmt ring buffer) to put next message
  	    if(can[0].cxno_in < (NO_CANMSG - 1))
  	      can[0].cxno_in++;
  	    else
  	      can[0].cxno_in = 0;	 // overwrite oldest msg in queue
  	    // increment counter
  	    if(can[0].cxno < NO_CANMSG)
  	      can[0].cxno++;
  	    else
  	      can[0].cxno = NO_CANMSG;
  	    if(!(CANTIER & 0x07))  {
  	      // Following will cause entry to TxIsr without sending msg
  	      // since when CANTIER = 0, CANTFLG left as buff empty(>0).
  	      // If CANTIER has at least 1 int buf enabled, will enter
  	      // TxIsr automatically.
  	      CANTBSEL = CANTFLG;
  	      CANTIER = CANTBSEL;
  	    }
  	  } 
  	  else  {
  	    txcnt = 0;
  	    txmode = 3;
  	    txgoal = rxnbytes;
  	    SCI0DRL = *tableByteRam(tble_idx, rxoffset);
  	    cksum += SCI0DRL;
  	    SCI0CR2 &= ~0x24;   // rcv, rcvint disable
  	    SCI0CR2 |= 0x88;        // xmit enable & xmit interrupt enable
  	  }
  	}
  	else  {                   // write data to input data block
  	  // buffer to maintain coherence of inputs while awaiting serial bytes
  	  if((rxnbytes > 1) && (rxnbytes < sizeof(txbuf)))
  	    ibuf = 1;
  	  else
  	    ibuf = 0; 
  	  txmode++;
  	}
  	break;

case 9:
  	// Check for data overrun.  Note: this
  	// still not bullet proof because could write half the bytes
  	// then have overrun; need to store and write all at once if
  	// all is valid.
  	if((SCI0SR1 & 0x08) == 0)  {   // none yet- write data.
  	  if(sendCANdat)
  	    *((char *)&txbuf + rxcnt) = SCI0DRL;
  	  else if(!ibuf)
  	    *tableByteRam(tble_idx, rxoffset + rxcnt) = SCI0DRL;
  	  else
  	    *((char *)&txbuf + rxcnt) = SCI0DRL;
  	}
  	rxcnt++;
  	if(rxcnt >= rxnbytes)  {
  	  if(sendCANdat)  {
  	    txmode = 0;         // done receiving/writing all the data
  	    sendCANdat = 0;
  	    // set up single CAN message & forward to aux board
  	    for(ix = 0; ix < rxnbytes; ix++)  {
  	      *(&can[0].cx_datbuf[can[0].cxno_in][ix]) = *((char *)&txbuf + ix);
  	    }
  	    can[0].cx_msg_type[can[1].cxno_in] = MSG_CMD; 
  	    can[0].cx_varblk[can[0].cxno_in] = tble_idx;
  	    can[0].cx_varoff[can[0].cxno_in] = rxoffset;
  	    can[0].cx_dest[can[1].cxno_in] = CANid;
  	    can[0].cx_varbyt[can[0].cxno_in] = (unsigned char)rxnbytes;
  	    // This is where (in xmt ring buffer) to put next message
  	    if(can[0].cxno_in < (NO_CANMSG - 1))
  	      can[0].cxno_in++;
  	    else
  	      can[0].cxno_in = 0;	 // overwrite oldest msg in queue
  	    // increment counter
  	    if(can[0].cxno < NO_CANMSG)
  	      can[0].cxno++;
  	    else
  	      can[0].cxno = NO_CANMSG;
  	    if(!(CANTIER & 0x07))  {
  	      // Following will cause entry to TxIsr without sending msg
  	      // since when CANTIER = 0, CANTFLG left as buff empty(>0).
  	      // If CANTIER has at least 1 int buf enabled, will enter
  	      // TxIsr automatically.
  	      CANTBSEL = CANTFLG;
  	      CANTIER = CANTBSEL;
  	    }  
  	  } 
  	  else if(ibuf)  {
  	    for(ix = 0; ix < rxnbytes; ix++)  {
  	      *tableByteRam(tble_idx,rxoffset + ix) = *((char *)&txbuf + ix);
  	    }
  	  }
  	  dummy = SCI0DRL;      // clear OR bit (read sci0sr1(done), sci0drl)
  	  if(rd_wr == 1)
  	    txmode = 0;         // done receiving/writing all the data
  	  else  {
  	    txcnt = 0;				// send back data just written
  	    txmode = 3;				//	 for verification
  	    txgoal = rxnbytes;
  	    SCI0DRL = *tableByteRam(tble_idx, rxoffset);
  	    SCI0CR2 &= ~0x24;   // rcv, rcvint disable
  	    SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  	  }
  	}       // finished all rxnbytes
  	break;

case 20:	 // d/load & burn tables - *** Do while engine NOT turning ***
  	// Note: this type table has no ram copy - its not intended for tuning
  	tble_idx = SCI0DRL;    // type of table
  	if(tble_idx > 3)	 {
  	  // tble index wrong - kill comms since don't know how much data being sent
  	  txmode = 0;
  	  SCI0CR2 &= ~0xAC;   // rcv, xmt disable, interrupt disable
  	  kill_ser = 1;
  	  kill_ser_t = outpc.seconds + 10;
  	  return;
  	}
  	ntword = 0;
  	flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code
  	save_page = PPAGE;
  	PPAGE = 0x3C;
	  // erase table
  	nsect = (unsigned char)(tableWords(tble_idx)/SECTOR_WORDS);
  	if(tableWords(tble_idx) > (nsect*SECTOR_WORDS))nsect++;
  	for (sect = 0; sect < nsect; sect++)  {
  	  Flash_Erase_Sector(tableWordFlash(tble_idx, 0) + sect * SECTOR_WORDS);
  	}
  	PPAGE = save_page;
  	flocker = 0;
  	txmode++;
  	break;

case 21:
  	cksum += SCI0DRL;
  	tble_word = (SCI0DRL << 8);    // msb
  	txmode++;
  	break;

case 22:
  cksum += SCI0DRL;
  tble_word |= SCI0DRL;            // lsb
  flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code
  save_page = PPAGE;
  PPAGE = 0x3C;
  // write table word
  Flash_Write_Word(tableWordFlash(tble_idx, ntword), tble_word);
  flocker = 0;
  ntword++;
  if(ntword < tableWords(tble_idx))
    txmode = 21;
  else  {
    txmode = 0;
    ign_reset();
  }
  PPAGE = save_page;
  break;

case 30:
  	if(SCI0DRL == '!')    // receive 2nd ! for reboot signal
  	  txmode++;
  	else if(SCI0DRL == 'x')  {  // received reinit signal (!x)
  	  reinit_flag = 1;
  	  txmode = 0;
  	}
  	else
  	  txmode = 0;
  	break;

case 31:
  	if(SCI0DRL == 'x')  {   // received complete reboot signal(!!x)
  	  if(outpc.rpm < inpram.crank_rpm)  {  // check not rebooting at hi rpm
  	    // go to start of bootload pgm
  	    //asm (jmp $F800);
  	    (void)reboot();
  	  }
  	}
  	else if(SCI0DRL == '!')  {   // received complete reload signal(!!!)
  	  if(outpc.rpm < inpram.crank_rpm)  { // check not reloading code at hi rpm
  	    SCI0CR1 = 0x00;
  	    SCI0CR2 = 0x00;      // disable Tx, Rx
  	    // go to start of monitor code
  	    //asm (jmp $F842);
  	    (void)monitor();
  	  }
  	}
  	txmode = 0;
  	break;

default:
  dummy = SCI0DRL;   // dummy read of reg to clear int flag
  break;

  }     // End of case switch for received data
    
  if((txgoal == 0) && (txmode > 0))
    rcv_timeout = lmms + 3906;   // 0.5 sec timeout
  return;

// Transmit Interrupt
XMT_INT:
  // Clear the TDRE bit by reading SCISR1 register (done), then write data
  txcnt++;
  if(txcnt < txgoal)  {
    if(txmode == 3)  {
  	  SCI0DRL = *tableByteRam(tble_idx, rxoffset + txcnt);
  	  if(rd_wr == 0)
  	    cksum += SCI0DRL;
    }
    else if(txmode == 4)  {
  	  SCI0DRL = RevNum[txcnt];
    }
    else if(txmode == 5)  {
  	  SCI0DRL = Signature[txcnt];
    }
    else if(txmode == 2)  {
  	  SCI0DRL = *((char *)&vfy_fail + txcnt);
    } 
    else if(txmode == 6)  {
  	  // pass on CAN msg to MT
  	  SCI0DRL = *((char *)&txbuf + txcnt);
    } 
    else  {
      xcntr++;
      SCI0DRL = *tableByteRam(tble_idx, xcntr);
      cksum += SCI0DRL;
      if(xcntr >= 3)  {	 // transmitted 4 bytes, load 2 new words
        xcntr = -1;
        txptr += 2;
        *tableWordRam(tble_idx,0) = *((unsigned int *)(&outpc) + txptr);
        *tableWordRam(tble_idx,1) = *((unsigned int *)(&outpc) + txptr+1);
      }
    }
  } 
  else  {   // done transmitting
    if(txmode == 1)
      outpc.cksum = cksum;
    txcnt = 0;
    txgoal = 0;
    txmode = 0;
    SCI0CR2 &= ~0x88;    // xmit disable & xmit interrupt disable
    SCI0CR2 |= 0x24;     // rcv, rcvint re-enable
  } 
  return;
}														 
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        
#endif

void CanInit(void)
{
unsigned char ix;
	/* Set up CAN communications */
	/* Enable CAN, set Init mode so can change registers */
	CANCTL1 |= 0x80;
	CANCTL0 |= 0x01;
	
	/* clear ring buffers */
	for(ix = 0;ix < 2;ix++)  {
	  can[ix].cxno = 0;
	  can[ix].cxno_in = 0;
	  can[ix].cxno_out = 0;
	}
	
	while(!(CANCTL1 & 0x01));  // make sure in init mode
	
	/* Set Can enable, use IPBusclk (24 MHz),clear rest */
	CANCTL1 = 0xC0;  
	/* Set timing for .5Mbits/ sec */
	CANBTR0 = 0xC2;  /* SJW=4,BR Prescaler= 3(24MHz CAN clk) */
	CANBTR1 = 0x1C;  /* Set time quanta: tseg2 =2,tseg1=13 
	              (16 Tq total including sync seg (=1)) */
	CANIDAC = 0x00;   /* 2 32-bit acceptance filters */
	/* CAN message format:
	 Reg Bits: 7 <-------------------- 0
	  IDR0:    |---var_off(11 bits)----|  (Header bits 28 <-- 21)
	  IDR1:    |cont'd 1 1 --msg type--|  (Header bits 20 <-- 15)
	  IDR2:    |---From ID--|--To ID---|  (Header bits 14 <--  7)
	  IDR3:    |--var_blk-|--spare--rtr|  (Header bits  6 <-- 0,rtr)
	*/  
	/* Set identifier acceptance and mask registers to accept 
	     messages only for can_id or device #15 (=> all devices) */
	/* 1st 32-bit filter bank-to mask filtering, set bit=1 */
	CANIDMR0 = 0xFF;           // anything ok in IDR0(var offset)
	CANIDAR1 = 0x18;           // 0,0,0,SRR=IDE=1
	CANIDMR1 = 0xE7;		   // anything ok for var_off cont'd, msgtype
	CANIDAR2 = can_id;     // rcv msg must be to can_id, but
	CANIDMR2 = 0xF0;			 // can be from any other device
	CANIDMR3 = 0xFF;           // any var_blk, spare, rtr
	/* 2nd 32-bit filter bank */
	CANIDMR4 = 0xFF;           // anything ok in IDR0(var offset)
	CANIDAR5 = 0x18;           // 0,0,0,SRR=IDE=1
	CANIDMR5 = 0xE7;		   // anything ok for var_off cont'd, msgtype
	CANIDAR6 = 0x0F;			 // rcv msg can be to everyone (id=15), and
	CANIDMR6 = 0xF0;			 // can be from any other device
	CANIDMR7 = 0xFF;           // any var_blk, spare, rtr
	
	/* clear init mode */
	CANCTL0 &= 0xFE;  
	/* wait for synch to bus */
	while(!(CANCTL0 & 0x10));
	
	/* no xmit yet */
	CANTIER = 0x00;
	/* clear RX flag to ready for CAN recv interrupt */
	CANRFLG = 0xC3;
	/* set CAN rcv full interrupt bit */
	CANRIER = 0x01;
	return;
}

#ifndef GCC_BUILD
#pragma CODE_SEG  NON_BANKED
#endif
INTERRUPT void CanTxIsr(void)
{
unsigned char ix,jx;
  
/* CAN Xmit Interrupt */
CANTBSEL = CANTFLG;    // select MSCAN xmit buffer
// Check ring buffers and xfer to MSCAN buffer
for(ix = 0;ix < 2;ix++)  {
 if(can[ix].cxno)  {
 	/* Set up identifier registers */
	CAN_TB0_IDR0 = (unsigned char)(can[ix].cx_varoff[can[ix].cxno_out] >> 3);
	                      // 8 high bits in IDR0, 3 low bits in IDR1
	CAN_TB0_IDR1 = 
	  (unsigned char)((can[ix].cx_varoff[can[ix].cxno_out] & 0x0007) << 5) | 
	               0x18 |           // SRR=IDE=1
                 can[ix].cx_msg_type[can[ix].cxno_out];			// 3 bits
	CAN_TB0_IDR2 = (can_id << 4) | can[ix].cx_dest[can[ix].cxno_out];
	CAN_TB0_IDR3 = (can[ix].cx_varblk[can[ix].cxno_out] << 4);
	CAN_TB0_DLR = can[ix].cx_varbyt[can[ix].cxno_out];
	/* Set xmt buffer priorities (lower is > priority) */
	CAN_TB0_TBPR = 0x02;
	
	/* set data in buffer */
	switch(can[ix].cx_msg_type[can[ix].cxno_out])  {
		case MSG_CMD:  // msg for dest ecu to set a variable to val in msg
		case MSG_RSP:  // msg in reply to dest ecu's request for a var val
		  for(jx = 0;jx < CAN_TB0_DLR;jx++)  {
		    *(&CAN_TB0_DSR0 + jx) = can[ix].cx_datbuf[can[ix].cxno_out][jx];
		  }
		  break;
		
		case MSG_REQ:  // msg to send back current value of variable(s)
		  CAN_TB0_IDR3 |= 0x01;  //rtr=1 (remote frame,no data-ignores DLR)
		  break;
		  
		case MSG_XSUB:
		  CAN_TB0_DLR = 0;
		  break;
		  
	}
	// This is where (in xmt buffer) to get next outgoing message
	if(can[ix].cxno_out < (NO_CANMSG - 1))
		can[ix].cxno_out++;
	else
		can[ix].cxno_out = 0;
	if(can[ix].cxno > 0)
		can[ix].cxno--;    // TB buf loaded for xmit - decrement ring buf count
	can_status &= CLR_XMT_ERR;
	/* set CAN xmt interrupt bit and clear flag to initiate transmit */
	CANTIER |= CANTBSEL;  
	CANTFLG = CANTBSEL;  // 1 clears(buffer full), 0 is ignored
	break;               // only handle 1 buffer entry at time
 }
 if(ix == 1)  {    // nothing left in either ring buffer
  CANTIER = 0x00;  // leave CANTFLG as buffer empty, but disable interrupt 
  break;  
 }
}		        // end for loop
if((CANRFLG & ~0x0C) != 0)  {
        // Xmt error count
        can_status |= XMT_ERR;
        //reset = 1;
}
return;
}

INTERRUPT void CanRxIsr(void)
{
unsigned char rcv_id,msg_type,var_blk,var_byt,jx;
unsigned short var_off;

    /* CAN Recv Interrupt */
    if(CANRFLG & 0x01)  {
    
        var_off = ((unsigned short)CAN_RB_IDR0 << 3) |
                       ((CAN_RB_IDR1 & 0xE0) >> 5);
        msg_type = (CAN_RB_IDR1 & 0x07);
        rcv_id = (CAN_RB_IDR2 & 0xF0) >> 4;		// message from device rcv_id
        var_blk = (CAN_RB_IDR3 & 0xF0) >> 4;
        var_byt = (CAN_RB_DLR & 0x0F);

        switch(msg_type)  {
        
          case MSG_CMD:  // msg for this ecu to set a variable to val in msg
          case MSG_RSP:  // msg in reply to this ecu's request for a var val
        	               // value in the data buffer in recvd msg
            if(getCANdat)  {
              // set up for serial xmit of the recvd CAN bytes (<= 8)
              txcnt = 0;
              txmode = 6;
              txgoal = var_byt;
              if((txgoal < 1) || (txgoal > 8))  {
                for(jx = 0;jx < var_byt;jx++)  {
                  *((char *)&txbuf + jx) = *(&CAN_RB_DSR0 + jx);
                }
                SCI0DRL = *((char *)&txbuf);
                SCI0CR2 &= ~0x24;   // rcv, rcvint disable
                SCI0CR2 |= 0x88;        // xmit enable & xmit interrupt enable
              }
              getCANdat = 0;
              ltch_CAN = 0xFFFFFFFF;
              break;
            } 
            else  {
              switch(var_blk)  {
                case 0:	 // inpram
                case 1:	 // outpc
                  // update variable value with received data
                  for(jx = 0;jx < var_byt;jx++)  {
                    *(canvar_blkptr[var_blk] + var_off + jx) = 
                           *(&CAN_RB_DSR0 + jx);
                  }
                  break;
              }
            }
            break;
        		
          case MSG_REQ:  // msg to send back current value of variable(s)
            switch(var_blk)  {
              case 0:	 // inpram
              case 1:	 // outpc
                // put variable value(s) in xmit ring buffer
                for(jx = 0;jx < var_byt;jx++)  {
                  *(&can[0].cx_datbuf[can[0].cxno_in][jx]) = 
                       *(canvar_blkptr[var_blk] + var_off + jx);
                }
                break;
            }

            // Update related parameters for xmt ring buffer
            can[0].cx_msg_type[can[0].cxno_in] = MSG_RSP;
            can[0].cx_varblk[can[0].cxno_in] = var_blk;
            can[0].cx_varoff[can[0].cxno_in] = var_off;
            can[0].cx_dest[can[0].cxno_in] = rcv_id;
            can[0].cx_varbyt[can[0].cxno_in] = var_byt;
            // This is where (in xmt buffer) to put next messge
            if(can[0].cxno_in < (NO_CANMSG - 1))
              can[0].cxno_in++;
            else
              can[0].cxno_in = 0;
            // increment counter
            if(can[0].cxno < NO_CANMSG)
              can[0].cxno++;
            else
              can[0].cxno = NO_CANMSG;
            if(!(CANTIER & 0x07))  {
              // Following will cause entry to TxIsr without sending msg
              // since when CANTIER = 0, CANTFLG left as buff empty.
              // If CANTIER has at least 1 int buf enabled, will enter
              // TxIsr automatically. 
              CANTBSEL = CANTFLG;
              CANTIER = CANTBSEL;
            }
            break;
        		
          case MSG_XSUB:  // msg to execute a subroutine
            switch(rcv_id)  {
              case 1:           // message to this ecu from device 1
                can_xsub01();		// execute sub immediately here (set
                                // flag if can execute in main loop) 
                break;
            }
        	  break;
        }					 // end msg_type switch
        can_status &= CLR_RCV_ERR;
    }
    if((CANRFLG & ~0x72) != 0)  {
        // Rcv error or overrun on receive
        can_status |= RCV_ERR;
        //reset = 1;
    }
    /* clear RX buf full, err flags to ready for next rcv int */
    /*  (Note: can't clear err count bits) */
    CANRFLG = 0xC3;
	
    return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        
#endif

void can_xsub01(void)  {
  return;
}

int barocor_eq(int baro)  {
  // returns baro correction in % (100 is no correction)
   // baro in kPa x 10
   return((int)(inpram.bcor0 + (((long)inpram.bcormult * baro) / 1000)));
}

int aircor_eq(int mat)  {
  // returns air density correction from equation in % (100 is no correction)
  //   mat in deg F x10 or deg Cx10
  /* Following equations used:

       For Farenheit, air density (pounds/cubic feet) = .0391568 x map (kPa x 10)
                                                        -------------------------
                                                       ((mat(degFx10)/10) + 459.7)
       If we take 70 degF as standard temperature (no density correction) and ignore
       map, which is already accounted for elsewhere,then
         air density correction(%) = (70 + 459.7) x 100
                                     ------------------
                                     ((mat/10) + 459.7)
       For Celsius, with a 20 deg C reference and mat in deg C x 10
         air density correction(%) = (20 + 273.2) x 100
                                     ------------------
                                     ((mat/10) + 273.2)
  */ 
  if(inpram.Temp_Units == 0)  {    // deg F
  	return((int)(529700 / (mat + 4597)));
  }
  else  {                          // deg C
  	return((int)(293200 / (mat + 2732)));
  }
}

int coil_dur_table(int delta_volt)  {
  int ix;
  long interp, interp3;
  // returns correction for coil duration vs battery voltage deviation
  //  from 12 V from table lookup in .1 ms units
  // bound input arguments
  if(delta_volt > inpram.deltV_table[NO_COILCHG_PTS-1])  {
    return((int)inpram.deltDur_table[NO_COILCHG_PTS-1]);
  }
  if(delta_volt < inpram.deltV_table[0])  {
    return((int)inpram.deltDur_table[0]);
  }
  // delta_volt in V x 10
  for(ix = NO_COILCHG_PTS - 2; ix > -1; ix--)  { 
  	if(delta_volt > inpram.deltV_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp = inpram.deltV_table[ix + 1] - inpram.deltV_table[ix];
  if(interp != 0)  {
    interp3 = (delta_volt - inpram.deltV_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
    if(interp < 0)interp = 0;
  }
  return((int)(inpram.deltDur_table[ix] +
	  interp * (inpram.deltDur_table[ix+1] - inpram.deltDur_table[ix])/ 100));
}

int intrp_2ditable(unsigned int x, int y, unsigned char nx, unsigned char ny,
  unsigned int * x_table, int * y_table, int * z_table)  {
  int ix,jx;
  long interp1, interp2, interp3;
  // bound input arguments
  if(x > x_table[nx-1])x = x_table[nx-1];
  else if(x < x_table[0])x = x_table[0];
  if(y > y_table[ny-1])y = y_table[ny-1];
  else if(y < y_table[0])y = y_table[0];
  // Find bounding indices in table
  for(ix = ny - 2; ix > -1; ix--)  {  // Start w highest index
	//  because will generally have least time for calculations at hi y
  	if(y > y_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
  for(jx = nx - 2; jx > -1; jx--)  {  // Start w highest index
	// because will generally have least time for calculations at hi x
	  if(x > x_table[jx])  {
	    break;
    }
  }
  if(jx < 0)jx = 0;
  // do 2D interpolate
  interp1 = y_table[ix + 1] - y_table[ix];
  if(interp1 != 0)  {
    interp3 = (y - y_table[ix]); 
    interp3 = (100 * interp3); 
    interp1 = interp3 / interp1; 
  }
  interp2 =	x_table[jx + 1] - x_table[jx];
  if(interp2 != 0)  {
    interp3 = (x - x_table[jx]); 
    interp3 = (100 * interp3); 
    interp2 = interp3 / interp2; 
  }
  return((int)(((100 - interp1) * (100 - interp2) * z_table[ix*nx+jx]
	  + interp1 * (100 - interp2) * z_table[(ix+1)*nx+jx]
	  + interp2 * (100 - interp1) * z_table[ix*nx+jx+1]
	  + interp1 * interp2 * z_table[(ix+1)*nx+jx+1]) / 10000));
}

int knk_thresh_calc(unsigned int rpm)  {
  int ix;
  long interp, interp3;
  // returns values for knock signal threshhold vs rpm.
  // bound input arguments
  if(rpm > inpram.knk_rpm[NO_KNKRPMS-1])  {
    return(inpram.knk_thresh[NO_KNKRPMS -1]);
  }
  if(rpm < inpram.knk_rpm[0])  {
    return(inpram.knk_thresh[0]);
  }
  for(ix = NO_KNKRPMS - 2; ix > -1; ix--)  { 
  	if(rpm > inpram.knk_rpm[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
  interp = inpram.knk_thresh[ix + 1] - inpram.knk_thresh[ix];
  if(interp != 0)  {
    interp3 = (rpm - inpram.knk_rpm[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
  }
  return((int)(inpram.knk_thresh[ix] + interp * (inpram.knk_thresh[ix+1] - 
                    inpram.knk_thresh[ix])/ 100));
}

void set_spr_port(char port, char val)  {
    unsigned char pval;

    if (val) {
       outpc.port_status |= (0x01 << port);
    }
    else {
       outpc.port_status &= ~(0x01 << port);    // JL fix
    }

    if(port < 4)  {
        pval = (0x01 << (port + 2));
        if(val)
          PTM |= pval;
        else
          PTM &= ~pval;
    } 
    else if(port < 6)  {
        pval = (0x01 << (port + 2));
        if(val)  {
          DISABLE_INTERRUPTS
          PTT |= pval;
          ENABLE_INTERRUPTS
        }
        else  {
          DISABLE_INTERRUPTS
          PTT &= ~pval;
          ENABLE_INTERRUPTS
        }
    } 
    else  {
        if(val)
          PORTA |= 0x01;
        else
          PORTA &= ~0x01;
    } 
}

#ifndef GCC_BUILD
#pragma CODE_SEG  NON_BANKED
#endif
INTERRUPT void UnimplementedISR(void) {
   /* Unimplemented ISRs trap.*/
   return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        
#endif


/******************************************************************************
Arguments:	

      progAdr		Pointer to the start of the destination 
			  Flash location to be programmed
      bufferPtr	Pointer to the start of the source data 
      size      Number of WORDS to be programmed
                  
    This function will program non-paged flash only 
******************************************************************************/
void fburner(unsigned int* progAdr, unsigned int* bufferPtr, 
                                          unsigned int no_words)
{
unsigned char sect,no_sectors;
																
  	if(flocker != 0xCC)return; // if not set, we got here unintentionally
  	// sector = 1024 bytes (512 words)
  	if(burn_flag == 1)  {
  	  no_sectors = (unsigned char)(no_words / SECTOR_WORDS);
  	  if(no_words > (no_sectors * SECTOR_WORDS))no_sectors++;
  	  for (sect = 0; sect < no_sectors; sect++)  {  	  
  	    Flash_Erase_Sector(progAdr + sect * SECTOR_WORDS);
  	  }
  	}
  	Flash_Write_Word(progAdr + burn_flag-1, *(bufferPtr + burn_flag-1));
    	
	return;
}

//*****************************************************************************
//* Function Name: Flash_Init
//* Description : Initialize Flash NVM for HCS12 by programming
//* FCLKDIV based on passed oscillator frequency, then
//* uprotect the array, and finally ensure PVIOL and
//* ACCERR are cleared by writing to them.
//*
//*****************************************************************************
void Flash_Init(unsigned long oscclk)  {
unsigned char fclk_val;
unsigned long temp;
  /* Next, initialize FCLKDIV register to ensure we can program/erase */
  temp = oscclk;
  if (oscclk >= 12000) {
    fclk_val = oscclk/8/200 - 1; /* FDIV8 set since above 12MHz clock */
    FCLKDIV = FCLKDIV | fclk_val | FDIV8;
  }
  else
  {
    fclk_val = oscclk/8/200 - 1;
    FCLKDIV = FCLKDIV | fclk_val;
  }
  FPROT = 0xFF; /* Disable all protection (only in special modes)*/
  FSTAT = FSTAT | (PVIOL|ACCERR);/* Clear any errors */
  return;
}
void Flash_Erase_Sector(unsigned int *address)  {
  FSTAT = (ACCERR | PVIOL); // clear errors
  (*address) = 0xFFFF;/* Dummy store to page to be erased */
  FCMD = ERASE;
/*********************************************************************
;* Allow final steps in a flash prog/erase command to execute out
;* of RAM while flash is out of the memory map
;* This routine can be used for flash word-program or erase commands
;*
;********************************************************************/
/* Execute the sub out of ram  */
   asm {
     ldaa  #CBEIF
     jsr  RamBurnPgm
   };
   return;
}

void Flash_Write_Word(unsigned int *address, unsigned int data)  {
  FSTAT = (ACCERR | PVIOL); // clear errors
  (*address) = data; // Store desired data to address being programmed
  FCMD = PROG; // Store programming command in FCMD
/* Execute the sub out of ram  */
   asm {
     ldaa  #CBEIF
     jsr  RamBurnPgm
   };
  return;
}

