/*	SW-COMPUTER Factory Control Program
	WWW.EXTREME-FIRE.COM
	Target controller = ATTINY85-20PU microcontroller from ATMEL.
	Clock speed = 8 MHz internal.
	Development platform = AVR Studio 4  Version 4.13 Service Pack 2  Build 571.
	C compiler = WinAVR 20070525
	Programer = AVR Dragon
	Hardware Schematic = SW-COMPUTER all versions.
	Hardware Layout = SW-COMPUTER all versions.

This program is fully released to the Public Domain by Terry Fritz 2007 / 2008.
*/
/*
History
	001 - Initial release  Nov. 23, 2007  Terry Fritz
	002 - Next generation  Nov. 25, 2007  Terry Fritz
	002.40 - First real beta release.
	002.41 - Fixed Vibration timing problems, set peak current to 250 amps.	Dec. 26, 2007  9:24AM
	002.42 - Added Temperature sensing.  Dec. 26, 2007  1:10PM
	002.43 - Removed high battery voltage speed reduction.  High battery voltage to 17.0V.  Dec. 27, 2007 11:16PM
				Timing rechecked Dec 28, 2007 2:19PM
	002.44 - Added event logging, LiPO monitor support, PWM and BurstTimeFactor low limits.  Dec. 30, 2007 10:00AM
	002.45 - Added LiPO Monitoring. Redid Port control. Jan 1, 2008  8:51AM
	002.46 - Speeded up programing vibrations and Burst to Full auto time.  Check LiPO alert 3 times. Jan. 7, 2008 4:30PM
	002.50 - Released 
	002.60 - Tweaked LiPO Function.  Lowered trip points from 3000mV to 1000mV.  Changed installed detection from
				2000mV to 750mV.  Feb. 15, 2008  11:10PM
			Added FiringCounter (was unused MotorRunningLoopCounter).
			Added battery voltage reset after 300 trigger cycles and DeadTime.
			Fixed self test 6 limits.
	002.70 - Fixed LiPO limits per WolfDragon.  Mar. 14, 2008  4:28AM
	002.80 - Set temp limit to 55C.  Set Max battery voltage to 16V.
			Added maximum Current and Temperature logging.  
			Added Semi-only, Auto Roll, Soft Start, and hard battery settings to definitions to make special programming easy.  
			Changed RDSon to 2818 uOhms.  
			Rollover counter adjusted to 162 from 167 counts.  
			Fixed high to low battery voltage lockout problem - Thanks Jawz :)  
			Break time changed to 75mS.
			April 5, 2008  4:00PM
	003.00 - Rolling to 3.00 rev now :)
			Adding over temp lockout!  - Thanks Jawz!!
			Limited event log to 250 events.
			Recalibrated temperature.
			March 8, 2008  8:57PM
	003.10 - Added "Secret Mode" programming for custom chip functions through trigger pulls:
				SemiOnly Soft and hard locks.
				3-Round to full auto rollover enable / disable.
				Soft start enable / disable.
				Hard battery voltage limits.  April 11, 2008 6:46AM
	003.20 - Added low battery voltage failure warning.
			Fixed low voltage program mode problem.  April 30, 2008 10:52PM


Known issues:
	To use pin 1 (AUX1) the Reset Disable Fuse has to be set and the Debug Wire Enable Fuse has to be clear.
		( RSTDISBL = 1  DWEN = 0 )  They use pin 1 by defalt and will keep the pin high if not fixed.

	Set BrownOut voltage to 4.3V in fuses.

	Version 3.10 runs it low on memeory.  Secret Mode programming is sacrificial.

Future changes:
	None
*/


/* -- Trigger Master II specific programming --------------------------------------------



Done
* Defaults to Burst Mode
* Wider timing adj so 2 round burst possible
* Replaced eventlog() ... "improved diagnostics"
* Vibe confirmation in trigger pulls for confirmation (limited to actual real values for gunmode)
* Burst complete even if trigger early release
* Disable of AB (6 vibes: 0-no change 1-AB on 2-AB Off)
*   side effect: Reset now at 7 vibes
* Semi Lock (enter this mode, no exit.)  Pull trigger 10 times in mode selection = Semi LOCK
* Burst only (no rollover to auto) - Mode '4'
* Sniper/DM mode (Mode '5') added - semi-only mode with a 1 second shot delay

20080904
	- Changed gun default if mode = 0xFF in eeprom to "ThreeRound" not "Normal"
	- Implemented wider range of "decrease burst time" and faster decrease (-2 not --)
		to allow for 2 round bursts if desired.  Lower is by 4% at a time, higher is by 2% at a time.
		Max 20% slower and max 50% longer
	- Added ErrorLog() function and supporting DEFs to replace EventLog().
	- Replaced EventLog() with a new function

20080904
	- Sacrificed SecretMode() for program space
	- Altering firing loop so that bursts always complete (if more than 1 shot is made by time of letoff)
	- Added disable/enable of Active Braking in config mode
	- Implemented semi-lock mode (10 pulls in gun mode config = semi LOCK)
	- Burstonly mode done (Mode 4)
	- Sniper/DM mode with 1 second shot delay (but no delay for first 5 learning shots)
	 	** This mode is COMPATIBLE with semi lock!  (While in semi-lock, can select mode 5 to turn on 
		** delay.  To turn off, just select any other mode.  Lock will keep in semi but delay is now off.)
	- Hijacked the version eeprom byte to be Version 1.0
	- Fixed bug in trigger pull detection for active braking enable/disable
	- Version 1.0

20080911
	- Added ability for manual adjustment (via Burst time inc/dec in programming mode) of single shot 
		time (i.e. 1-round bursts) for semi-locked units on fullauto-only guns.  These guns have no means
		of auto-detecting shot timing due to lack of semi-auto mechanism.
	- Therefore added code which uses BurstTimeFactor to apply anywhere between -13 to +17 directly
		to (runtime) SingleShotTime.  This is only done if:
		1) Unit is semi-locked, and 2) BurstTimeFactor has been changed via user programming.  (#2 is
		to ensure no functionality change for semi-locked units on semi-capable AEGs -- which have no
		reason to ever modify the burst time.  And if they do, a factory reset will fix it.)
	- Fixed bug:  A SEMI-LOCKED unit plugged directly into a fullauto-only gun will now "just work".
		This was not the case with previous versions.  If a semi-only unit was installed into a
		gun that only fired fullauto, it would use the default BurstTime and never would override it
		for semi-only operation.  Fixed in ReadEEPROM().
	- Version 1.1



-- Trigger Master III changes, etc ----------------------------------------------------

20081211
	- New RDSon (value from Terry) for IRF1324 FET of 1280 (was 2818 uOhms)
	- DrainCurrentPeakLimit from 250(Amps) to 500(Amps)
	- Version 2.0
	* This is the version used on the Trigger Master Mark III first production run

20090127
	- Version C.1
		Custom code for Anthony Surkon: Mode for SAFE-BURST-FULL (Mode 6)
		First 5 shots after powerup will be in semi only for timing sensing
		Cannot update timing after powerup
		Prototyping with a MkII, final on a MkIII

20090202
	- Version C.2
		Improved SafeBurstFull so that first 5 shots are still in semi (always ceasefire at disconnect 
		of trigger in semi) but added code to re-introduce continual improvement/autosensing of bursttime
		based on the ShotCounter number detected at trigger disconnect time during bursts.
		(Added SavedShotCounter)
		It works by:
			1. At the completion of a burst, call SetBurstTime() with the saved ShotCounter value before
				going to CeaseFire.
			2. In CeaseFire - the normal location of SetBurstTime() - do not SetBurstTime if the gun 
				is in SafeSemiFull mode.
		KNOWN ISSUE/LIMITATION:
			* Burst length is on a "Best Effort" basis.  Since the piston is never stopped right
			at trigger disconnect time (in semi) after shot #5, it is not possible to guarantee the 
			piston position	and therefore firing cycle completion.  This is exacerbated by the fact 
			that the piston	ceasefire position is indeterminate when firing in fullauto.
	
		SUMMARY (overrides any conflicting comments, not that there should be any):
			1. SafeBurstFull (mode 6) works
			2. Assuming the best practise of first 5 shots in semi-auto after powerup is done:
				The first 5 shots in this mode are always a single, semi shot.  (This is to set the 
				piston position to a known position.)
			3. By the fifth trigger pull in semi, autosensing is complete and bursts should be 3 long.
			4. A limitation of this mode is that burst timing is best-effort only.  The piston is not
				stopped at trigger-disconnect time in semi, so shooting in semi doesn't 'reset' the
				piston.  Also the piston will be in an indefinable position after shooting in full
				since it depends on when the shooter lets go of the trigger.  It is possible and
				unavoidable that some minor drift in burst timing occurs in this mode.  If it occurs, 
				it can be reset if needed with a power cycle.
	
	- Text for label (Known Issues/Limitations):
	"It is possible for the duration of bursts to experience minor "drift" in a prolonged firing session, 
	especially if fullauto is used frequently.  
	
	When fullauto Is used the end-position of the piston depends on when the shooter releases the 
	trigger.  It could be half-way through a firing cycle when you stop.  If you then fire a burst, 
	that half-done firing cycle can lead to a minor timing error (e.g. a 3-and-a-half shot burst)

	This condition is just a mechanical gearbox issue.  It does not affect the Trigger Master 
	electronics, and any drift issue will be cleared at next powerup (when piston position is reset 
	by the first shots in semi.)"

	- This is a candidate for production if it tests OK.
	- Code is getting a little... awkward.  Might be time for a complete rewrite based on my other work.
	- 99.9% program full, by the way.


20090217
	- Working on version 2.1 (Release Candidate)
	- Mode 6 (SAFE-BURST-FULL) above is considered a "bonus" mode only with no guaranteed timing accuracy.
	- ThreshHold for burst completion changed from 15% to 25%.  15% is too close to the "normal" 
		variance for SingleShotTime of +/-10%

	Problem report:
	With much "quick triggering" the value of SingleShotTime (which is constantly updated as long as it
	is +/-10% of previous value) can become too small (successive -10%) and therefore cause burst fire 
	on semi-auto, due to the "Complete burst if trigger released early" functionality.  Another thing that
	can exacerbate (or be an alternate cause of) the problem is the firing cycle being left in a semi-fired
	state due to early trigger release or firing on auto.  
	I.E. 	Firing cycle 50% done + next shot is on semi = detected SingleShotTime is SingleShotTime + 50%

	Fix attempt by cleaning up Burst Completion code and making more robust:
		1) Altered threshold for burst completion from 15% to 25% (minor issue, does not solve problem)
		2) make it static (set 'BurstCompletionThreshold' at startup and not updated, i.e. it is not a
			function of SingleShotTime and its later updates!)  Cannot lead to burst complete on semi as
			long as the first timing shots are done properly.
	
	- Turns out that is not enough.  Burst completion is more trouble than it is worth due to being
		fundamentally unable to cope with a lot of early-trigger-releasing which is an inevitable result
		of quick semi-auto trigger action.  Therefore, disabling Burst Completion code.
	- Code is intact but voided by 'DisableBurstCompletion = 1'.
	- Sacrified FactoryDiagnostics() which I never use (honestly I thought I had toasted it before.)
	- Added EventLog(6) for Factory Reset Initiated
	- Added a vibe signal to end of delay for Sniper/DM mode
	- Advanced config option 6 is now 'Shot delay end vibe signal on/off' (default on)
		It used to be Active Braking enable/disable.  1 Pull = ON, 2 Pulls = OFF, 0 = No change
	- Fixed a bug where gunfunctions should only be processed if the trigger was pulled at all for
		option 1 (Firing Mode Selection) in programming mode.  Skip that entirely if the trigger was
		not touched (otherwise it [re]sets some things it should not.)
	- V2.1, 96.1%

20090223
	- Burst Completion has been successfully made more robust, but it still should be default off.
	- Added "Burst Completion On/Off" Default OFF.  Programming option 7
	- FACTORY RESET moved to "8"
	- Version 2.2, 99.2%




18 Hints to reduce code size

1. Compile with full size optimization.
2. Use local variables whenever possible.
3. Use the smallest applicable data type. Use unsigned if applicable.
4. If a non-local variable is only referenced within one function, it should be
declared static.
5. Collect non-local data in structures whenever natural. This increases the possibility
of indirect addressing without pointer reload.
6. Use pointers with offset or declare structures to access memory mapped I/O.
7. Use for(;;) { } for eternal loops.
8. Use do { } while(expression) if applicable.
9. Use descending loop counters and pre-decrement if applicable.
10. Access I/O memory directly (i.e., do not use pointers).
11. Declare main as C_task if not called from anywhere in the program.
12. Use macros instead of functions for tasks that generates less than 2-3 lines
assembly code.
13. Reduce the size of the Interrupt Vector segment (INTVEC) to what is actually
needed by the application. Alternatively, concatenate all the CODE segments
into one declaration and it will be done automatically.
14. Code reuse is intra-modular. Collect several functions in one module (i.e., in one
file) to increase code reuse factor.
15. In some cases, full speed optimization results in lower code size than full size
optimization. Compile on a module by module basis to investigate what gives the
best result.
16. Optimize C_startup to not initialize unused segments (i.e., IDATA0 or IDATA1 if all
variables are tiny or small).
17. If possible, avoid calling functions from inside the interrupt routine.
18. Use the smallest possible memory model.


--------------------------------------------------------------------------------------- */



/*	Include libraries */
	#include <avr/io.h> /* For all the odd ATTINY stuff */

/*	Define ATTINY85 ports (PBx) to the gun controls */
/*	Note that these port numbers or NOT the physical pin numbers! */
	#define Drain 2			/* FET drain voltage port */
	#define Aux0 3			/* AUX 0 I/O port */
	#define Trigger 4		/* Trigger voltage port */
	#define Aux1 5			/* AUX 0 I/O port */
	#define Ground 10  		/* Internal A/D ground used for calibration */
	#define Temperature 11	/* Internal temperature probe.  Only use in mode 2 */
			
/*	Variable definitions */

	// HiJacked by DonP, MkIII version is below
	#define VersionNumber 0x21						/* Software version number programed to EEPROM */

	#define HighBatteryVoltage 16000				/* In mV */
	#define LowBatteryVoltage 7000					/* In mV */
	#define TriggerFiringVoltagePercent 80			/* Perent */
	#define BatteryVoltageDroopWarningPercent 85	/* Percent */
	#define TriggerHoldVoltage 3000					/* In mV */
	#define TrigTrip 500							/* In mV */

	#define DrainCurrentPeakLimit 500				/* In Amps */
	#define RDSon 1280								/* In uOhms */ 
	#define TemperatureLimit 60						/* In Celcius */ 
	
	#define BreakTime 75							/* mS */
			
	#define Normal 1								/* These are GunModes made easy to read */
	#define ThreeRound 2
	#define ThreeRoundAuto 2
	#define SemiOnly 3								/* Semi Only gun function */
	// DonP Added 20080904
	#define BurstOnly 4			// ThreeRound but with AutoRoll (roll over to auto) off
	#define SniperDM 5			// Semi only, with shot delay
	// DonP Added 20090127
	#define SafeBurstFull	6	// SAFE-BURST-FULL, first 5 shots in semi exempt for timing.

	#define Neutral 0x29							/* Port control values Modified for LiPO */
	#define Break 0x28
	#define Motor 0x2B

// DonP Defines
	// For EventLog
	#define	ERRLOG_MEMSTART 	0x41			// Beginning of cyclic error log
	#define ERRLOG_MEMEND		0xAF			// End of cyclic error log
	#define ERRLOG_BOOKMARK_LOCN 0x40			// Always contains last location written to
	// For shot delay (non-zero shot delay for Sniper/DM mode)
	#define	SNIPER_DM_MODE_SHOT_DELAY 100		// 1 second (This number * 10 = Delay in milliseconds)


/* Function prototypes */
	void MotorOFF(void);						/* Turns the motor off */
	void MotorON(void);							/* Turns the motor on */
	void BreakOFF(void);						/* Turns the break off */
	void BreakON(void);							/* Turns the break on */
	void EnergizeFETs(void);					/* Enables the FETs in a controled way */
	void Timer(long Time);						/* A mS timer  Timer(123) = 123mS */
	void FastTimer(long Time);					/* An unclaibrated ~15uS fast timer */
	long GetVoltage(unsigned char Sensor, unsigned char ADCRange);	/* Gets the Voltage (in mV) from the chosen source (Drain, Trigger, Aux) */
	void Vibrate(void);							/* Vibrates the gun's motor for signaling the user */
	void CalibrateAtoD(void);					/* Calibrates A/D convertor */
	void EEPROM_write(unsigned char ucAdress, unsigned char ucData);	/* Write to EEPROM */
	unsigned char EEPROM_read(unsigned char ucAddress);					/* Read from EEPROM */
	void ProgramEEPROM(void);					/*  User programming of the EEPROM */
	void ReadEEPROM(void);						/*  Get stored EEPROM data */
	void SetBurstTime(long ShotCounter);		/*	Set burst time function */
	void MotorPWM(char PWMduty);				/*	Motor PWM function */
	void SetAUXPorts(void);						/*  Setup AUX ports */
// Sacrificed FactoryDiagnostics (unused)
//	void FactoryDiagnostics(void);				/*  Factory test */
	unsigned char LiPOStatus(void);				/*  LiPO battery check */
//	void Diagnostic(long DiagTime);				/*  Diagnostic test routein */
// Sacrificed SecretMode()
// 	void SecretMode (void);						/*  Secret Programming Mode */
	void Vibrate1 (unsigned char);				/*  Multiple Vibrations */

//	void EventLog(char);						/*  Event logging */
	void EventLog(char eventcode);				//  DonP Replacement function

/*	Initialize variables */

	/* long is a 4 byte integer */

	unsigned char SemiOnlyLock = 0;				/* Semi-Only lock flag 0=off (default) 1=on 2=permant on*/
	unsigned char AutoRoll = 1;					/* 3-Round auto rollover flag 0=off 1=on (default) */
	unsigned char SoftStartEnable = 1;			/* Soft Start Enable 1=default */
	long HardBatteryVoltage = 0;				/* Hard Battery Voltage Setting in mV.  0=off*/


	long  BatteryVoltageDroopWarning = 5500;/* mV */
	long  TriggerFiringVoltage = 6000;		/* mV */
	long  InitialBatteryVoltage = 0;		/* mV */
	long  BurstTimeFactor = 80;				/* The single to three round burst ratio DEFAULT if EEPROM = FF*/
											/* 100 = 3.00 X the single shot time. 2X+100% */
	long  BurstTimeFactorPercent = 0;		/* Real BurstTimeFactorPercent */
	unsigned char  MotorSpeed = 254;		/* 0 to 254 PWM value */
	
	long  SingleShotTime = 70;				/* Default loops for single shot */
	long  BurstTime = 205;					/* Default motor on time for a burst */

	long  TimeFactor = 100;					/* Adjust for 1mS step.  Magically, the calibration factor = 100 */
	long  SingleShotTimeOld = 0;			/* Old value of single shot time */
	long  TimeFactorFast = 1;				/* A sub mS timer counter */
	unsigned char  ErrorCode = 0;			/* The Errors are each given a unique number */
	long  TriggerVoltage = 0;				/* The voltage on the trigger input in mV */
	long  DrainCurrent = 0;					/* The current through the drive FET in Amps */
	long  DrainVoltage = 0;					/* The voltage on the MOSFET drains mV */
	long  counter2 = 0;						/* Counter for Vibrate function */
	long  Time = 0;							/* Variable for Timer function */
	long  counter = 0;						/* Counter for Timer function */
	unsigned char  Sensor = 0;				/* Input pin number for ADC */
	long  Voltage = 0;						/* A/D convertor returned voltage in mV */
	long  count = 0;						/* local counter */
	long  VoltageOffset0 = 0;				/* A/D 5.0V REF offest error */
	long  VoltageOffset1 = 0;				/* A/D 2.56V REF offest error */
	long  VoltageOffset2 = 0;				/* A/D 1.1V REF offest error */
	unsigned long  FiringCounter = 0;		/* Counts firing cycles*/
	unsigned char  GunMode = Normal;		/* 1=trigger control (Normal) 2=burst (ThreeRoundAuto) */
	long  ShotCounter = 0;					/* Shot count inside a burst */
	long  SavedShotCounter = 0;				// DonP 20090202 - for SafeBurstFull
	unsigned char  TriggerHold = 0;			/* Hold further shotting until trigger is released */
	long  ModeCounter = 0;					/* A main loop counter for the threeround auto fuction */
	// DonP 20080904 - Default is now Burst mode
	//unsigned char  GunFunction = Normal;	/* Operating mode DEFAULT if EEPROM = FF */
	unsigned char  GunFunction = ThreeRound;	/* Operating mode DEFAULT if EEPROM = FF */
											/* 1 = Normal  2 = ThreeRound  3 = Semi Only*/
											// 4 = BurstOnly, 5 = Sniper/DM, 6 = SafeBurstFull
	unsigned char ucAddress = 0x00;			/* For EEPROM functions */
	unsigned char ucData = 0x00;			/* For EEPROM functions */
	unsigned char  ADCRange = 0;			/* 0 = 5.0V REF   1 = 2.56V REF  2 = 1.1V REF */
	long  count1 = 0;						/* Error loop counter */
	long  counter3 = 0;						/* Timer counter */
	long  counter4 = 0;						/* FastTimer counter */
	long  SBTCounter = 0;					/* Burst time function counter */
	unsigned char  PWMduty = 0x00;			/* Motor PWM control variable */
	long  counter5 = 0;						/* Master Reset time counter */
	long  GunFunctionFlag = 0;				/* Counter used in mode programing */
	unsigned char BatteryVoltageEEPROM = 0;	/* Stored initial battery voltage */
	unsigned char DroopWarningFlag = 0;		/* Droop warning counter */
	unsigned char DroopWarningFlag1 = 0;	/* Hard Droop warning counter */
	long counter6 = 0;						/* Motor softstart counter */
	long RampSpeed = 0;						/* MotorRampSpeed variable */
	long TestValue = 0;						/* Factory test variable */
	unsigned char TestCode = 0;				/* Facotry test variable */
	unsigned char TestCounter = 0;			/* Factory test counter */
	// DonP 20080904 - These no longer needed
	//unsigned char Event = 0;				/* Event logging variable */
	//unsigned char EventOld = 0;				/* Event logging variable */
	// Replaced with below for new function
	unsigned char ErrorLogAddress = 0xff;	// Current error log address
	unsigned char LiPO = 0;					/* LiPO battery status */
	long Aux0Data = 0;						/* Aux port 0 data variable */
	long Aux1Data = 0;						/* Aux port 1 data variable */
	unsigned char LiPOInstalled = 0;		/* LiPO Installed flag */
	unsigned char BatteryLimitReset = 0;	/* LiPO Installed flag */
	long DeadTimeCounter = 0;				/* Counter used to find battery rest time */
	unsigned char TemperatureMAX = 0;		/* Maximum temperature seen */
	long RunningTemp = 0;					/* CPU temperature */
	unsigned char CurrentMAX = 0;			/* Maxiumum current seen / 10 */
	unsigned char OverTempLockout = 0;		/* Over temperature lockout flag */
	long DiagTime = 0;						/* Diganostic variable */
	//unsigned char count6 = 0;				/* Secret mode Loop counter */
	unsigned char VibCount = 0;				/* Multi vibration counter */
	unsigned char counter8 = 0;				/* Multi vibration counter */

	// DonP 20080904 - For Active Braking (default on, 0x01 in EEPROM=on 0x00=off)
	//unsigned char ActiveBrakingEnable = 1;	//0x24  EEPROM address
	// DonP 20090217 - Turned it into this instead
	unsigned char ShotDelayEndVibeEnable = 0;	//0x24 EEPROM address
	// DonP 20080904 - For Sniper/DM mode (semi-only mode with shot delay)
	unsigned char ShotDelay = 0;	// 0x25 EEPROM address (0 or FF in eeprom = disabled)
	// DonP 20080911 - For general use
	unsigned char flag = 0;
	// DonP 20090217 - For burst completion threshold.  Calculated at SetBurstTime().
	unsigned long BurstCompletionThreshold = 88;		// Default value is default SingleShotTime (70)
														// + 25% = 88
	unsigned int DisableBurstCompletion = 1;	// 0x26 EEPROM address (FF in eeprom = disabled)

int main(void)
{
/*	MAIN POWER UP */

	/*	Wait 2 seconds for hardware to stabilize */
	Timer(2000);  
	ErrorCode = 1;
	GunMode = Normal;

	/*	Setup A/D parameters */
	CalibrateAtoD();

	/*	Check trigger voltage */
	if (GetVoltage(Trigger,0) > TrigTrip) {ErrorCode = 4;}

		/*  Get EEPROM data */
	ReadEEPROM();


	// DonP 20080911 - Code to allow for manual adjustment of single shots in semi-locked mode
	// (See log for details)
	//	
	if (EEPROM_read(0x20) == 0x02) 			// If we are SEMI-LOCKED
	{
		if( EEPROM_read(0x02) != 0xFF) 		// If unit has modified BurstTimeFactor, modify SingleShotTime
		{
			// This doesn't work well:  Took it out
			// Apply BurstTimeFactor% + 20% to [the runtime value of] SingleShotTime
			// (BurstTimeFactor default is 80%)  Actual value could be from 20-250 before we +20.
			/* SingleShotTime = (((BurstTimeFactor+20)*SingleShotTime)/100);	*/

			// Replaced it with this below.  (Mis)use bursttimefactor to modify runtime singleshottime.
			if( BurstTimeFactor >= 80)
			{
				flag = (BurstTimeFactor - 79); 					// 1-171
				SingleShotTime = SingleShotTime + (flag/10);	// +1 to +17
			}

			if( BurstTimeFactor <= 79)
			{
				flag = (BurstTimeFactor-19);					// 1-60
				SingleShotTime = (SingleShotTime-(66-flag))/5;	// -13 to -1
			}
		}
		BurstTime = SingleShotTime-2;	// This is the default semi-locking operation from SetBurstTime()
		//
		// This needs to be done even if we didn't mess with BurstTimeFactor above, because default 
		// bursttime is about 3 shots worth.  If we never do a SetBurstTime() then a fullauto-only gun 
		// will never set BurstTime to be SingleShotTime; so we have to ensure it here if we're 
		// semi-locked.
	}
	//
	// Adjusting BurstTime in programming mode therefore modifies the shot time for Semi-Locked 
	// Trigger Masters in fullauto only guns (which are not capable of self-adjustment which requires 
	// semi-auto gearbox operation.)  We only do this if we're semi-locked AND the BurstTimeFactor has 
	// been altered via user programming.
	//
	// End DonP for time adjusting of single shots work for semi-locked units with modified bursttimefactors.



	/*	Energize drive circuits */
	EnergizeFETs();

	/*  Setup AUX ports */
	SetAUXPorts();

	/*	Check drain voltage */
	DrainVoltage = (GetVoltage(Drain,0));
	if (DrainVoltage < LowBatteryVoltage) {ErrorCode = 2;}
	if (DrainVoltage > HighBatteryVoltage) {EventLog(1); ErrorCode = 3;}

	/*  Set Battery Limits */
	if (InitialBatteryVoltage == 0) {InitialBatteryVoltage = (GetVoltage(Drain,0));}
	if (HardBatteryVoltage != 0) {InitialBatteryVoltage = HardBatteryVoltage;}
	TriggerFiringVoltage = (InitialBatteryVoltage * TriggerFiringVoltagePercent) / 100;
	BatteryVoltageDroopWarning = (InitialBatteryVoltage * BatteryVoltageDroopWarningPercent) / 100;

	LiPO = LiPOStatus();
		
	/*	Vibrate status to motor */
	for (count1 = 0; count1 < ErrorCode; count1++)
	{
	Timer(500); Vibrate();
	}

	/*Factory Test */
	//FactoryDiagnostics();		// DonP 20090217 - Removed (save program space)

	/*  If the battery voltage is not safe, force a shutdown */
Halt:
	if (ErrorCode > 1) {goto Halt;}
		/*	Error Codes
		1	Normal operation
		2	Battery voltage too low
		3	Battery voltage too high
		4   Trigger voltage too high 
		16	Low battery
		17  High motor peak current 
		18  Overheating
		*/

	/*	Wait for trigger pull to enter programming mode (2 seconds) */
	for (count=0; count < 1185; count++)
	{
		if (GetVoltage(Trigger,0) > LowBatteryVoltage) 
		{
			// DonP added 
			EventLog(5);	// Event code for 'programming mode entered'
			ProgramEEPROM();
		}
	}

	/* Semi-Only Locked here From SemiOnlyLock definition */
	if (SemiOnlyLock != 0) {GunFunction = 3;}

	
/*	MAIN SCANNING LOOP */
	Vibrate();Vibrate();Vibrate();Vibrate();
	if (LiPOInstalled != 0) {Vibrate();Vibrate();Vibrate();Vibrate();}

	
MainLoop:
	ModeCounter++; DeadTimeCounter++;
	/*	Set motor to off no matter what is going on */
	MotorOFF(); 

	/*	Check trigger voltage */
	TriggerVoltage = GetVoltage(Trigger,0);

	if ((TriggerVoltage < TriggerFiringVoltage) && (TriggerVoltage > LowBatteryVoltage)) 
		{DroopWarningFlag1++;
		if (DroopWarningFlag1 > 5) {Vibrate(); Timer(1000); DroopWarningFlag1 = 0;}
		} 

	if ((TriggerVoltage >= TriggerFiringVoltage) && (TriggerHold == 0)) 
	{
		
		if (LiPOInstalled == 0)
		{
			/*	Check trigger voltage incase the battery is dying. Check three times to stop false warnings */
			if (TriggerVoltage < BatteryVoltageDroopWarning) {DroopWarningFlag++;} else {DroopWarningFlag = 0; goto Fire;}
			if (DroopWarningFlag > 2) {ErrorCode = 16;}
			goto Fire;
		}
		else
		{
		LiPO = LiPOStatus();
		if (LiPO == 1) {DroopWarningFlag = 0; goto Fire;}
		DroopWarningFlag++;
		if ((LiPO == 3) && (DroopWarningFlag > 2)) {(ErrorCode = 16); goto CeaseFire;}
		if ((LiPO == 2) && (DroopWarningFlag > 2)) {ErrorCode = 16; goto Fire;}
		goto Fire;
		}
	}
	if (TriggerVoltage < TrigTrip) {TriggerHold = 0; GunMode = Normal; ModeCounter = 0;}

	/* Goto full auto in three round burst mode if trigger is held down 1/2 second longer */
	if ((ModeCounter > 162) && (TriggerHold == 1) && (GunFunction != 3) && (AutoRoll == 1)) {GunMode = ThreeRoundAuto; goto Fire;}


	
/*  Reset Battery Limits after 300 triggers*/
// DonP 20090217 - And DeadTimeCounter > 1000, which is empirically determined to be about 2 seconds.
	if ((BatteryLimitReset == 0) && (FiringCounter > 300) && (DeadTimeCounter > 1000) && (HardBatteryVoltage != 0))
		{InitialBatteryVoltage = (GetVoltage(Drain,0));
		TriggerFiringVoltage = (InitialBatteryVoltage * TriggerFiringVoltagePercent) / 100;
		BatteryVoltageDroopWarning = (InitialBatteryVoltage * BatteryVoltageDroopWarningPercent) / 100;
		EventLog(4);
		BatteryLimitReset = 1;
		}
	
	/*	Return to top and loop again */
goto MainLoop;


/*	FIRING LOOP */
Fire:

if (OverTempLockout == 1) {goto CeaseFire;}
/* MOTOR IS ON!!!!! */
	MotorON();
	Timer(1);	/* A delay to avoid the motor start up electrical noise burst */
	ShotCounter = 0;
	SavedShotCounter = 0;	// DonP 20090202 for SafeSemiFull
		
	FiringCounter++;  /* increment firing loop counter */

ShotDetect:  /* Main firing loop. */
	ShotCounter++;
	TriggerVoltage = GetVoltage(Trigger,1);	/* Get trigger voltage */
	if (TriggerVoltage < TriggerHoldVoltage)  /* Is trigger off now? */
	{
		// DonP 20090217 - Updated Burst fire completion
		// Keep firing despite trigger release if the following applies:
		// 1. Trigger is off (we are here, so it is)
		// 2. Current time spent firing (i.e. ShotCounter) is more than SingleShotTime + 25%
		// 3. But current time spent firing (i.e. ShotCounter) is also LESS THAN BurstTime
		//		(In other words, trigger is down 25%+ longer than 1 shot, but less than a full burst)
		// 4. And we are in a qualifying fire mode (N.B. SafeBurstFull mode bursts are handled separately)
		// THEN complete the burst by going to ShotDetect instead of CeaseFire.
		//
		// In practice we don't dynamically determine SingleShotTime + 25% since that opens the door
		// to "completing" bursts when we're in semi-auto if the SingleShotTime gets eventually munged up
		// as a result of repeated quick-triggering (causing improper stacked -10% updates to 
		// SingleShotTime) or something.
		//
		// So instead we calculate 'BurstCompletionThreshold' (default value = 'BurstTime' at powerup)
		// once inside SetBurstTime() [at 'Golden Shot'] and never touch it again.  That way, worst 
		// case scenario for screwed-up too-small SingleShotTime is no different than usual... BurstTime
		// is erroneous (and not 'fires[completes] burst in semi').  Burst completion will always require
		// more time than can be delivered while in semi-auto mode.
		//
		// NEWS:  Burst completion is more trouble than it's worth.  Gets screwed up too easily with
		//		  lots of quick semi-auto action which causes 2 things: 
		//			1) Early trigger releases, could lead to -10% SingleShotTime drift
		//			2) Early releases leave piston in semi-fired state, therefore next trigger pull
		//				is in fact 100% + ??% of SingleShotTime causing the next cycle to be off (which 
		//				can screw up SingleShotTime sensing as well as triggering of Burst Completion.
		//
		if( DisableBurstCompletion == 0 )					// So far disabling bursts is always the case
		{
			if( BurstCompletionThreshold < BurstTime )		// Enable check and Sanity check 
			{
				// Complete bursts in SAFE-SEMI-BURST/FULL as well as SAFE-SEMI-BURST
				if( (ShotCounter>BurstCompletionThreshold) && (ShotCounter<BurstTime) && ((GunFunction==ThreeRoundAuto) || (GunFunction==BurstOnly)) )
						goto ShotDetect;
			}
		}
		// End DonP Burst Completion


		// DonP 20090127 - For SafeBurstFull
		// If Trigger is now released (could be cutoff due to semi-auto, or due to manual release)
		// If gunfunction is SafeBurstFull but FiringCounter is <6, ceasefire like for normal semiauto.
		//   (i.e. normal semi shots for first 5 shots after powerup)
		// If gunfunction is SafeBurstFull, keep firing and do not consider ceasing until BurstTime is up
		// (*)If BurstTime is up but trigger is still down, keep firing (fullauto) until trigger released.
		//  	Otherwise, ceasefire right away.
		if (GunFunction == SafeBurstFull )
		{
			// * If this is the very first shot AND we are in semi-auto, ceasefire normally
			// so we start off in a known piston position. (Piston forward)  See KNOWN ISSUES for why.
			if( FiringCounter<=5 )
			{
				SetBurstTime(ShotCounter);
				goto CeaseFire;		// If burst time is up and trigger was previously opened, cease fire.
			}

			// If Trigger is OFF but we have not yet reached or exceeded BurstTime, we must be in SEMI
			// and the disconnector has kicked in.  Finish the burst and SetBurstTime() as applicable.
			if( ShotCounter<=BurstTime )	// Selector on semi but not finished burst?
			{
				// Save the value to use in SetBurstTime() after burst - only if not already saved
				if(SavedShotCounter == 0)	// Always is reset to 0 at start of a firing sequence.
					SavedShotCounter = ShotCounter;  
				goto ShotDetect;	// Continue firing if not in first 5 shots, until bursttime is up.
			}
			else							// Selector on semi but burst is done?
			{
				SetBurstTime(SavedShotCounter);	// Do this here at end of burst instead of in CeaseFire code.
				goto CeaseFire;		// If burst time is up and trigger was previously opened, cease fire.
			}
		}
		// End DonP


		if ((GunFunction == Normal) || (GunMode == ThreeRoundAuto)) 
		{
			GunMode = Normal; goto CeaseFire;	  /* normally just CeaseFire */
		}
		

		// DonP 20090217 - Replaced the code below with the following line:
		goto CeaseFire;				// Cease Fire if none of the other conditions apply and trigger is off.
		//if (TriggerHold == 0) 
		//{
		//	goto CeaseFire;
		//}  
	}

	// Con't DonP 20090127 - For SafeBurstFull
	// (*) For last item in previous list: if bursttime is up but trigger is still down...
	if( GunFunction == SafeBurstFull )
	{
		goto ShotDetect;		// Keep firing, we are in fullauto.
	}
	// End con't DonP


	if ((GunMode == ThreeRoundAuto) || (GunFunction == Normal)) 
	{
		goto ShotDetect;	 /* Keep cycling in auto modes */
	}
	if (ShotCounter > BurstTime) 
	{
		TriggerHold = 1; goto CeaseFire;	  /* Three round time delay wait */
	}
	goto ShotDetect;

// ORIGINAL BELOW
//ShotDetect:  /* Main firing loop. */
//	ShotCounter++;
//	TriggerVoltage = GetVoltage(Trigger,1);	/* Get trigger voltage */
//	if (TriggerVoltage < TriggerHoldVoltage)  /* Is trigger off now? */
//	{
//		if ((GunFunction == Normal) || (GunMode == ThreeRoundAuto)) 
//		{
//			GunMode = Normal; goto CeaseFire;	  /* normally just CeaseFire */
//		}
//		if (TriggerHold == 0) 
//		{
//			goto CeaseFire;
//		}  
//	}
//	if ((GunMode == ThreeRoundAuto) || (GunFunction == Normal)) 
//	{
//		goto ShotDetect;	 /* Keep cycling in auto modes */
//	}
//	if (ShotCounter > BurstTime) 
//	{
//		TriggerHold = 1; goto CeaseFire;	  /* Three round time delay wait */
//	}
//	goto ShotDetect;
// END ORIGINAL

	

/*	FIRING HALT */

CeaseFire:

	/*	The motor ON and OFF functions have a 1mS anti cross conduction safety delay built in */
	/*	Turn motor to off */
	MotorOFF();

	/*	Turn break to on */
// 20090217 - Took this out and renamed to 'ShotDelayEndVibeEnable'
//	if( ActiveBrakingEnable == 1 )	// DonP 20080904 - Skip Active Braking if disabled
//	{
		BreakON();
//	}

	/*	Leave the break on for some time in mS */
	Timer(BreakTime);

	/*	Turn break to off */
	BreakOFF();
	

	// DonP 20080904 - To enable Sniper/DM mode	
	// Delay an amount of time equal to (ShotDelay x 10) milliseconds.
	if((ShotDelay > 0) && (FiringCounter>=5))	// Only delay if past the 'sync' shots
	{
		Timer(ShotDelay*10);
		// DonP 20090217 - Added a quick vibe to signal end of delay, if enabled
		if( ShotDelayEndVibeEnable == 1 )
		{
			for (counter2 = 5; counter2 > 0; --counter2)
			{
				PORTB = Motor;
				FastTimer(20);		/* Wait 500uS for motor on pulse */
				PORTB = Neutral;
				Timer(25);
			}
		}
	}

	
	/*  If in semi mode, set burst time */
	// DonP 20090202 - Added "And not in SafeBurstFull mode"
	// This way we never set burst time with a 'ShotCounter' that might be invalid due to 
	// overriding in semi mode.  We instead have entered SetBurstTime() within the override of semi
	// in the actual shot loop right before CeaseFire with the proper value.  Not the cleanest code.
	if ((TriggerHold == 0) && (GunFunction!=SafeBurstFull)) {SetBurstTime(ShotCounter);}

	/*  Check unit temperature */
	RunningTemp = GetVoltage(Temperature,2);
	
	RunningTemp = (RunningTemp - 298) * 100 / 109;
	if (RunningTemp >= TemperatureLimit) {EventLog(3); OverTempLockout = 1;}
	if (RunningTemp <= (TemperatureLimit - 5)) {OverTempLockout = 0;}
	if (OverTempLockout == 1) {ErrorCode = 18;}


	ModeCounter = 0; DeadTimeCounter = 0;

	/*Maximum Temperature */
		if (RunningTemp > TemperatureMAX)
			{
			TemperatureMAX = RunningTemp;
			EEPROM_write(0x10, (char)TemperatureMAX);
			}

	/*Maximum Current */
		if (DrainCurrent / 10 > CurrentMAX)
			{
			CurrentMAX = DrainCurrent / 10;
			EEPROM_write(0x11, (char)CurrentMAX);
			}

	/*  If an error occured report it */
	if (ErrorCode == 0) {goto MainLoop;}
		/*	16 - Low battery - Vibrate motor once after firing
			17 - High motor peak current - Vibrate motor twice
			18 - Overheating - Vibrate motor three times
		*/
		Timer(100);
		if (ErrorCode == 16) {Vibrate1(1);}
		if (ErrorCode == 17) {Vibrate1(2);}
		if (ErrorCode == 18) {Vibrate1(3);}

		ErrorCode = 0;

goto MainLoop;

}  /* End of main */



/* FUNCTIONS */

/* For safety in that "nothing can go wrong", the motor drive ports are controlled directly */
void MotorOFF(void)
	{
	MotorPWM(0);
	}


void MotorON(void)
	{
	PORTB = Neutral; 	/* Be REALLY(!!) sure break is off */
	Timer(1);		/* Wait 1mS for anti-cross conduction */
	PORTB = Motor;	/* motor is on for peak current test */
		Timer(1);	/* Wait to stabalize */
		/*	Check drain current for over peak current limit */
		DrainCurrent = GetVoltage(Drain,1) * 1000 / RDSon;
		PORTB = Neutral;	/* motor is off after test */
		if (DrainCurrent > DrainCurrentPeakLimit) {PORTB = Neutral; EventLog(2); ErrorCode = 17; return;} /* Shutdown if overcurrent */

		MotorPWM(1);
	/* Soft Start */
	if (SoftStartEnable == 1)
		{
		for (counter6 = 0; counter6 < 51; counter6++)
			{
				RampSpeed = MotorSpeed / 2 + (MotorSpeed * counter6) / 100;
				OCR0B = (char)RampSpeed;
			}
		}
		else
		{
				RampSpeed = MotorSpeed / 2 + (MotorSpeed * counter6) / 100;
				OCR0B = (char)MotorSpeed;		
		} 
	}


void BreakOFF(void)
	{PORTB = Neutral;}	/* The break signal is negative logic */


void BreakON(void)
	{
	PORTB = Neutral;	/* Be REALLY(!!) sure motor is off */
	Timer(1);		/* Wait 1mS for anti-cross conduction */
	PORTB = Break;	/* The break signal is negative logic */
	} 


void Timer(long Time)  /* Timer(100) = 100mS */
	{Time = Time * TimeFactor; for (counter3 = 0; counter3 <= Time; counter3++) {}}


void FastTimer(long Time) /* Timer(50) = 672uS */
	{Time = Time * TimeFactorFast; for (counter4 = 0; counter4 <= Time; counter4++) {}}


void EnergizeFETs(void)
	{
	/*	Energize break circuit */
	/*	Break is on PB0 */
	PORTB = 0x00;	/* Set pullups off */ 
	DDRB = 0x01;	/* Set port B pin 0 for output */
	PORTB = Neutral;	/* Set port B pin 0 to 1 */
	
	/*	Wait 1000mS for capacitive break circuit to stabilize (very important!!) */
	Timer(1000);  

	/*	Energize motor circuit */
	/*	Motor is on PB1 */
	DDRB = 0x03;	/* Set port B pins 0 and 1 for output */
	PORTB = Neutral;	/* Set port B pin 0 to 1 and pin 1 to 0 */
	/*	Wait 1mS */
	Timer(1);
	}


void SetAUXPorts(void)
	{
	/*  Set AUX ports */
	/* Turn on pullups for Aux0 and Aux1. Set motor off and break off */
	/* Pullups are set in the Nuetral, Break, and Motor definitions. */
	DDRB = 0x03;
	PORTB = Neutral;
	}


long GetVoltage(unsigned char Sensor, unsigned char ADCRange)
	{
	/* Select input Source */
	if (ADCRange == 0)								/* 0-20V range */
		{
		if (Sensor == Aux1) {ADMUX = 0x00;}
		if (Sensor == Drain) {ADMUX = 0x01;}
		if (Sensor == Trigger) {ADMUX = 0x02;}
		if (Sensor == Aux0) {ADMUX = 0x03;}
		if (Sensor == Ground) {ADMUX = 0x0D;}		/* Internal ground for calibration */
		}
	if (ADCRange == 1)								/* 0-10.23V range */
		{
		if (Sensor == Aux1) {ADMUX = 0x90;}
		if (Sensor == Drain) {ADMUX = 0x91;}
		if (Sensor == Trigger) {ADMUX = 0x92;}
		if (Sensor == Aux0) {ADMUX = 0x93;}
		if (Sensor == Ground) {ADMUX = 0x9D;}		/* Internal ground for calibration */
		}
	if (ADCRange == 2)								/* 0-1.1V range */
		{
		if (Sensor == Aux1) {ADMUX = 0x80;}
		if (Sensor == Drain) {ADMUX = 0x81;}
		if (Sensor == Trigger) {ADMUX = 0x82;}
		if (Sensor == Aux0) {ADMUX = 0x83;}
		if (Sensor == Ground) {ADMUX = 0x8D;}		/* Internal ground for calibration */
		if (Sensor == Temperature) {ADMUX = 0x8F;}	/* Can only be used with 1.1V referance */
		}
	/* Start A/D conversion */
	ADCSRA = 0b11000110;  						/* Start A/D conversion (bit 6 is set) 125kHz clock*/
	/* Test if done (bit 6 goes clear, but is set when A/D is complete) */
	do {} while (bit_is_set(ADCSRA,6));
	/* Get voltage Vref = 5000mV  Rdivider = 1/4  Full range is 20V */
	Voltage = ADCW;								/* This word is the 10 bit A/D result */
	if (ADCRange == 0)
	{
		Voltage = Voltage - VoltageOffset0;  	/* A/D Offset error adjustment */
		if (Voltage < 0) {Voltage = 0;}
		Voltage = ((Voltage * 20000) / 1023);	/* Convert A/D full range (1023) to 19980mV or 19.98V */
	}
	if (ADCRange == 1)
	{
		Voltage = Voltage - VoltageOffset1;  	/* A/D Offset error adjustment */
		if (Voltage < 0) {Voltage = 0;}
		Voltage = Voltage * 10;					/* Convert A/D full range (1023) to 10230mV or 10.23V */
	} 
	if (ADCRange == 2)
	{
		Voltage = Voltage - VoltageOffset2;  	/* A/D Offset error adjustment */
		if (Voltage < 0) {Voltage = 0;}
		Voltage = Voltage * 1100 / 1023;		/* Convert A/D full range (1023) to 1100mV or 1.100V */
	} 
	return Voltage;
	}


void CalibrateAtoD(void)
	{
	/* 5.0V REF  Range = 20V */
	VoltageOffset0 = 0;							/* Clear old value */
	VoltageOffset0 = GetVoltage (Ground, 0);	/* Run the converter with grounded input */
	VoltageOffset0 = ADCW;
	/* 2.56V REF  Range = 10.23V */	
	VoltageOffset1 = 0;							/* Clear old value */
	VoltageOffset1 = GetVoltage (Ground, 1);	/* Run the converter with grounded input */
	VoltageOffset1 = ADCW;						/* Get the raw A/D converter output word */
	/* 1.1V REF  Range = 1.1V */	
	VoltageOffset2 = 0;							/* Clear old value */
	VoltageOffset2 = GetVoltage (Ground, 2);	/* Run the converter with grounded input */
	VoltageOffset2 = ADCW;						/* Get the raw A/D converter output word */
	}


void Vibrate(void)
		{
		/* Vibrate for 1/2 second */
		BreakOFF();
		Timer(1);
		for (counter2 = 0; counter2 < 10; counter2++)
			{
			PORTB = Motor;
			FastTimer(20);		/* Wait 500uS for motor on pulse */
			PORTB = Neutral;
			Timer(25);
			}
		}

void Vibrate1(unsigned char VibCount)
/* Vibrate multiple times */
		{
		for (counter8 = 0; counter8 < VibCount; counter8++)
			{
			Vibrate();
			Timer(250);
			}
		}


	/* EEPROM stuff copied directly from ATMEL data sheet */

void EEPROM_write(unsigned char ucAddress, unsigned char ucData)
	{
	/* Wait for completion of previous write */
	while (EECR & (1<<EEPE));
	/* Set programming mode */
	EECR = (0<<EEPM1) | (0<<EEPM0);
	/* Set up address and data registers */
	EEAR = ucAddress;
	EEDR = ucData;
	/* Write logical one to EEMPE */
	EECR |= (1<<EEMPE);
	/* Start eeprom write by setting EEPE */
	EECR |= (1<<EEPE);
	}


unsigned char EEPROM_read(unsigned char ucAddress)
	{
	/* Wait for completion of previous write */
	while (EECR & (1<<EEPE));
	/* Set up address register */
	EEAR = ucAddress;
	/* Start eeprom read by writing EERE */
	EECR |= (1<<EERE);
	/* Return data from data register */
	return EEDR;
	}


void ProgramEEPROM(void)
	{
	
	/* Change EEPROM Programming */
		/*
		0x00 = Version Number
		0x01 = GunFunction		1=Normal, 2=Burst/Full, 3=Semionly, 4=BurstOnly, 5=Sniper/DM, 6=SafeBurstFull
		0x02 = BurstTimeFactor
		0x03 = SingleShotTime
		0x04 = Motor Speed
		0x05 = Initial Battery Voltage / 100
		0x10 = Maximum Temperature
		0x11 = Maximum Current / 10
		0x20 = SemiOnlyLock
		0x21 = AutoRoll
		0x22 = SoftStartEnable
		0x23 = HardBatteryVoltage / 100
		//0x24 = ActiveBrakingEnable	// DonP 20080904
		0x24 = ShotDelayEndVibeEnable // DonP 20090217
		0x25 = ShotDelay // DonP 20080904 - is non-zero for sniper/DM mode.  (This # * 10)=Milliseconds
		0x26 = DisableBurstCompletion // DonP 20090223

	// Defaults below
	unsigned char SemiOnlyLock = 0;			// Semi-Only lock flag 0=off (default) 1=on 2=permant on
	unsigned char AutoRoll = 1;				// 3-Round auto rollover flag 0=off 1=on (default) 
	unsigned char SoftStartEnable = 1;		// Soft Start Enable 1=default 
	long HardBatteryVoltage = 0;			// Hard Battery Voltage Setting in mV.  0=off

	*/

	Timer(500);

	Vibrate();
	/* Pull Trigger now to go to set GunFunction by number of trigger pulls.  Limit = 6 */
	// HOWEVER - If 10 pulls are detected, then SEMI LOCK will be enabled.  A one way street!
	GunFunctionFlag = 0;
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} /* Trigger hold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage)) && (TriggerHold == 0)) 
		{
		// DonP 20080904 - Added for feedback (but only if within legal range of 1-6, or 10 exactly)
		if( (GunFunctionFlag < 6) || (GunFunctionFlag==9) )  // '9' is about to be 10
		{
			Vibrate();
		}

		GunFunctionFlag++;		// Inc this anyway.. we limit it to 6 (or allow it to be 10) below
		
		GunFunction = GunFunctionFlag;

		// Limit mode to 6 max, (or allow to be 10 exactly)
		if ( (GunFunctionFlag > 6) && (GunFunctionFlag != 10) )
		{
			GunFunction = 6;
		}
		
		count = 0; TriggerHold = 1;
		}
	}

	Vibrate1(1);Vibrate();
	/* Pull Trigger now to go to decrease BurstTimeFactor by number of trigger pulls.  Limit = 20*/
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} /* Tigger hold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage) && (TriggerHold == 0) && (BurstTimeFactor > 0))) 
		{
			if( BurstTimeFactor > 20 )		// DonP 20080911 - To stop vibing when it "bottoms out"
			{
				Vibrate();	// DonP 20080904 - For feedback
				BurstTimeFactor-=2; 		// DonP 20080904 - Changed to be -2 instead of --
			}
			count = 0; TriggerHold = 1;	
		}
	}

	Vibrate1(2);Vibrate();
	/* Pull Trigger now to go to increase BurstTimeFactor by number of trigger pulls.  Limit = 250 */
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} /* TiggerHold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage) && (TriggerHold == 0) && (BurstTimeFactor < 250))) 
		{
			if( BurstTimeFactor < 250 )		// DonP 20080911 - To stop vibing when it "tops out"
			{
				Vibrate();	// DonP 20080904 - For feedback
				BurstTimeFactor++; 
			}
			count = 0; TriggerHold = 1;
		}
	}

	Vibrate1(3);Vibrate();
	/* Pull Trigger now to go to decrease MotorSpeed by number of trigger pulls.  Limit = 52*/
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} /* Tigger hold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage) && (TriggerHold == 0) && (MotorSpeed > 0))) 
		{
			if(MotorSpeed > 52)		// DonP 20080911 - To stop vibing when it "bottoms out"
			{
				Vibrate();	// DonP 20080904 - For feedback
				MotorSpeed = (MotorSpeed * 9) / 10; 
			}
			count = 0; SBTCounter = 0; TriggerHold = 1;
		}
	}

	Vibrate1(4);Vibrate();
	/* Pull Trigger now to go to increase MotorSpeed by number of trigger pulls.  Limit = 254 */
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} /* TiggerHold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage) && (TriggerHold == 0) && (MotorSpeed < 255))) 
		{
			if (MotorSpeed < 232)
			{
				Vibrate();	// DonP 20080904 - For feedback
				MotorSpeed = (MotorSpeed * 11) / 10; count = 0; SBTCounter = 0; TriggerHold = 1;
			}
			else 
			{
				MotorSpeed = 254;count = 0; SBTCounter = 0; TriggerHold = 1;
			}
		}
	}


	Vibrate1(5);Vibrate();	// Six vibes
	// Pull Trigger now to select ShotDelayEndVibe On/Off when in Sniper/DM mode
	// No pull = No change.  1 = ShotDelayEndVibe On, 2 = ShotDelayEndVibe Off
	ShotDelayEndVibeEnable=0;	
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} /* TiggerHold is a debounce for the trigger switch */
	if (( (GetVoltage(Trigger,0) > LowBatteryVoltage) && (TriggerHold == 0) )) 
		{
			if (ShotDelayEndVibeEnable < 2)
			{
				Vibrate();	// DonP 20080904 - For feedback
				ShotDelayEndVibeEnable++;
				count=0; TriggerHold=1;
			}
			else
			{
				count=0; TriggerHold=1;
			}
		}
	}


	// DonP 20090223
	Vibrate1(6);Vibrate();	// Seven vibes
	// Pull Trigger now to select BurstCompletion On/Off (when in burst/full or burst-only mode)
	// No pull = No change.  1 = BurstCompletion On, 2 = BurstCompletion Off (Default)
	DisableBurstCompletion=0;	
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} /* TiggerHold is a debounce for the trigger switch */
	if (( (GetVoltage(Trigger,0) > LowBatteryVoltage) && (TriggerHold == 0) )) 
		{
			if (DisableBurstCompletion < 2)
			{
				Vibrate();
				DisableBurstCompletion++;
				count=0; TriggerHold=1;
			}
			else
			{
				count=0; TriggerHold=1;
			}
		}
	}



	/* Store EEPROM data */
								// DonP 20090217
	if( GunFunctionFlag != 0 )	// To fix a bug where re-entering pgm mode turns existing sniper/dm mode
	{							// into just semi-only.  We should only process this if GunFunctionFlag is 
								// nonzero (meaning we touched the trigger at all for option 1.)

		// DonP 20080904 - Process SEMI LOCK (if entered)
		if( GunFunction == 10 )
		{
			EEPROM_write(0x20, 0x02);	// PERMANENTLY program EEPROM byte for gun into SEMI LOCKED mode.
			GunFunction = 3;	// Put gun into runtime SEMI mode (before we commit it to EEPROM below)
		}

		// DonP 20080904 - Process BURST ONLY mode (if selected)
		// Burst only mode is really just burst mode (mode 2) with the AutoRoll EEPROM config OFF
		if( GunFunction == 4 )	
		{
			EEPROM_write(0x21, 0x00);	// AutoRoll config byte = autoroll OFF
			AutoRoll = 0;				// Runtime autoroll OFF
			GunFunction = 2;			// Override to Burstmode
		}
		else
		{
			// Ensure autoroll is (back) ON if we're NOT processing a selected MODE 4 (Burstonly)
			if (EEPROM_read(0x21) != 0xFF) 	// If not already at default
			{ 
				EEPROM_write(0x21, 0xFF);	// AutoRoll reset (default on)
				AutoRoll = 1;
			}
		}

		// DonP 20080904 - Process SNIPER/DM mode if selected
		// Sniper/DM mode is really just Semi-only mode with a shot delay set to non-zero
		if( GunFunction == 5 )
		{
			EEPROM_write(0x25, SNIPER_DM_MODE_SHOT_DELAY);	// 'X' * 10ms = the dead-time delay after each firing event
			GunFunction = 3;			// Semi-only mode
		}
		else
		{
			// Ensure NO shot delay if we're in any other mode
			if (EEPROM_read(0x25) != 0xFF) 	// If not already at default
			{ 
				EEPROM_write(0x25, 0xFF);	// Shot delay config byte to default (off)
				ShotDelay = 0;				// Runtime shot delay to 0.
			}
		}
	}


	/* Gun Function */
	EEPROM_write (0x01, (char)GunFunction);
	/* Burst time factor */
	if (BurstTimeFactor < 20) {BurstTimeFactor = 20;}	// DonP 20080904 - Changed '50' to '20'
	if (BurstTimeFactor >250) {BurstTimeFactor = 250;}
	EEPROM_write(0x02, (char)BurstTimeFactor);
	/* MotorSpeed */
	if (MotorSpeed < 52) {MotorSpeed = 52;}
	if (MotorSpeed >254) {MotorSpeed = 254;}
	EEPROM_write(0x04, (char)MotorSpeed);
	InitialBatteryVoltage = (GetVoltage(Drain,0));
	BatteryVoltageEEPROM = (char)(InitialBatteryVoltage / 100);
	EEPROM_write(0x05, (char)BatteryVoltageEEPROM);
	// DonP 20080904 - for ShotDelayEndVibeEnable
// Redundant - removed
//	if( ShotDelayEndVibeEnable==0 ) 		// "No change" - just read EEPROM
//	{ 
//		if (EEPROM_read(0x24) != 0xFF) {ShotDelayEndVibeEnable = EEPROM_read(0x24);}
//	}
	if( ShotDelayEndVibeEnable==1 ) 
	{ 
		EEPROM_write(0x24, 0x01);
		ShotDelayEndVibeEnable=1;			// On
	}
	if( ShotDelayEndVibeEnable==2 ) 
	{ 
		EEPROM_write(0x24, 0x00);
		ShotDelayEndVibeEnable=0;			// Off
	}
	// End DonP for ShotDelayEndVibe

	// DonP 20090223 - DisableBurstCompletion
// Redundant - removed
//	if( DisableBurstCompletion==0 ) 	// "No change" - just read EEPROM 0x26
//	{
//		if (EEPROM_read(0x26) != 0xFF) {DisableBurstCompletion = EEPROM_read(0x26);}
//	}
	if( DisableBurstCompletion==1 )			// On (one trigger pull)
	{
		EEPROM_write(0x26, 0x00);
		DisableBurstCompletion = 0;
	}
	if( DisableBurstCompletion==2 )			// Off (two trigger pulls)
	{
		EEPROM_write(0x26, 0x01);
		DisableBurstCompletion = 1;
	}
	// End DonP for DisableBurstCompletion

	Vibrate1(7);Vibrate();
	/* Pull and hold Trigger now to go to Master Reset */
	counter5 = 0;
	for (count=0; count < 600; count++)
	{
	while (GetVoltage(Trigger,0) > LowBatteryVoltage) 
		{
			counter5++;
			if (counter5 > 1500)
			{
			EventLog(6);				// Factory reset performed code
			EEPROM_write(0x00,0xFF);
			EEPROM_write(0x01,0xFF);
			EEPROM_write(0x02,0xFF);
			EEPROM_write(0x03,0xFF);
			EEPROM_write(0x04,0xFF);
			EEPROM_write(0x05,0xFF);
			EEPROM_write(0x24,0xFF);	// DonP 20080904 - For ShotDelayEndVibeEnable reset
			EEPROM_write(0x21,0xFF);	// DonP 20080904 - To reset AUTOROLL to default (on)
			EEPROM_write(0x25,0xFF);	// DonP 20080904 - To reset shot delay.			
			EEPROM_write(0x26,0xFF);	// DonP 20090223 - To reset DisableBurstCompletion

			Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();



	/* Pull Trigger now 10 times to go to secret mode */
	/*
	count6 = 0;
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} 
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage)) && (TriggerHold == 0)) 
		{
		count6++;
		if (count6 == 10) {SecretMode();}
		count = 0; TriggerHold = 1;
		}
	}
	*/

Halt1:		goto Halt1;
			}
		}
	}

	}


void ReadEEPROM(void)
	{
	/* Note - Erased EEPROM = FF not 00 */
	/*
		0x00 = Version Number
		0x01 = GunFunction
		0x02 = BurstTimeFactor
		0x03 = SingleShotTime
		0x04 = Motor Speed
		0x05 = Initial Battery Voltage / 100
		0x10 = Maximum Temperature
		0x11 = Maximum Current / 10
		0x20 = SemiOnlyLock
		0x21 = AutoRoll
		0x22 = SoftStartEnable
		0x23 = HardBatteryVoltage / 100
		0x24 = ShotDelayEndVibeEnable	// DonP 20090217
		0x25 = ShotDelay // DonP 20080904 (delay after each firing action, used for Sniper/DM mode)
		0x26 = BurstCompletion // DonP 20090223
	*/
	/* Version */
	EEPROM_write(0x00, (char)VersionNumber);

	/* Gun Function */
	if (EEPROM_read(0x01) != 0xFF) {GunFunction = EEPROM_read(0x01);}

	/* Burst time factor */
	if (EEPROM_read(0x02) != 0xFF) {BurstTimeFactor = EEPROM_read(0x02);}

	/* Single Shot Time */
	if (EEPROM_read(0x03) != 0xFF) 
	{
		SingleShotTime = EEPROM_read(0x03); 
		SBTCounter = 0;

		BurstTimeFactorPercent = 2 * BurstTimeFactor + 100; 		
		BurstTime = (BurstTimeFactorPercent * 117 * SingleShotTime) / 120 / 100 + (37 * (BurstTimeFactorPercent - 100) / 120);


		//if (GunFunction == 3) {BurstTime = SingleShotTime;}	/* Semi only mode */
	}
	if (GunFunction == 3) {BurstTime = SingleShotTime;}	/* Semi only mode */
	//
	// BUGFIX - DonP 20080911
	// This line used to live above.  The original has been commented out and it has been copied here.
	// If SingleShotTime has never been updated (i.e. SetBurstTime() has never run because the gun
	// has never fired in semi-auto) then the line overriding the burst time in Semi-Auto only mode
	// (or in Semi-Lock) will never actually run.  
	//
	// Result: Trigger pulls on a fresh install on a gun only in full-auto will fire default
	// bursttime value 3-round bursts on every trigger pull.
	/////////////////////////////////////////////////////////////////////////////////////////////

	/* Motor Speed */
	MotorSpeed = EEPROM_read(0x04);

	/*  0x05 = Initial Battery Voltage / 100  */
	if (EEPROM_read(0x05) != 0xFF) {InitialBatteryVoltage = EEPROM_read(0x05) * 100;}

	/* Maximum Temperature */
	if (EEPROM_read(0x10) != 0xFF) {TemperatureMAX = EEPROM_read(0x10);}

	/* Maximum Current */
	if (EEPROM_read(0x11) != 0xFF) {CurrentMAX = EEPROM_read(0x11);}

	// DonP 20090217
	if (EEPROM_read(0x24) != 0xFF) {ShotDelayEndVibeEnable = EEPROM_read(0x24);}

	// DonP 20080904
	if (EEPROM_read(0x25) != 0xFF) {ShotDelay = EEPROM_read(0x25);}	// This number x 10ms = delay

	// DonP 20080923
	if (EEPROM_read(0x26) != 0xFF) {DisableBurstCompletion = EEPROM_read(0x26);}


/* Lock Flags */
	if (EEPROM_read(0x20) != 0xFF) {SemiOnlyLock = EEPROM_read(0x20);}
	if (EEPROM_read(0x21) != 0xFF) {AutoRoll = EEPROM_read(0x21);}
	if (EEPROM_read(0x22) != 0xFF) {SoftStartEnable = EEPROM_read(0x22);}
	if (EEPROM_read(0x23) != 0xFF) {HardBatteryVoltage = (EEPROM_read(0x23)) * 100;}
	}


void SetBurstTime(long ShotCounter)
	{
	SBTCounter++;
	/* Shot Three after gun is powered up is the golden shot */
	//
	// DonP: That means that after shot #3, values will only be updated if they fall between +/10% of
	// it.
	// 
	if (SBTCounter == 3) 
	{
		SingleShotTime = ShotCounter;
		// DonP 20090217 - Set 'BurstCompletionThreshold' once at this time to SingleShotTime + 25%
		BurstCompletionThreshold = (SingleShotTime*125/100);
	}
	/* Do not update if ShotCounter is < 90% or > 110% of SingleShotTime (early user trigger release) */
	if (SBTCounter > 3)
		{
		if ((ShotCounter > (SingleShotTime * 11) / 10) || (ShotCounter < (SingleShotTime * 9) / 10)) {return;}
		}	
	else {SingleShotTime = ((SingleShotTime * 5) + ShotCounter) / (5 + 1);}
/*
	SingleShotTime = 1.17 x ShotCounter + 37 in mS.
	BurstTime = 1.20 x ShotCounter + 37 in mS.
	BurstTime = BurstTimeFactor x 1.17 / 1.20 x SingleShotTime + 37 / 1.20 x (BurstTimeFactor - 1)
*/
	BurstTimeFactorPercent = 2 * BurstTimeFactor + 100; 		
	BurstTime = (BurstTimeFactorPercent * 117 * SingleShotTime) / 120 / 100 + (37 * (BurstTimeFactorPercent - 100) / 120);

	if (BurstTime < SingleShotTime) {BurstTime = SingleShotTime;}	/* Be sure a BurstTime error will not lock out the gun */ 
	/* Store SingleShotTime now */
	if (SingleShotTime < 0) {SingleShotTime = 1;}
	if (SingleShotTime >254) {SingleShotTime = 254;}
	if (SingleShotTime != SingleShotTimeOld)
		{
		EEPROM_write(0x03, (char)SingleShotTime);
		SingleShotTimeOld = SingleShotTime;
		}
		/* Semi only mode */	
		if (SBTCounter < 3) {BurstTime = 2 * SingleShotTime;} else 
			{
			if (GunFunction == 3) {BurstTime = SingleShotTime - 2;}
			}
	}


void MotorPWM(char PWMduty)
	/* Controlling OC0B which is pin 6 to the motor drive */
	{
	if (PWMduty == 0) {TCCR0A = 0x03; GTCCR = 0x80; PORTB = Neutral; return;} /* Shutdown */
	GTCCR = 0x81; 				/* HALT, SYNC, and RESET the PWM counter */
	TCCR0A = 0x23;				/* Enable PWMcontrol of motor and set function */
	TCCR0B = 0x01;				/* Divide by 8 for ~4000hZ */
	TCNT0 = (char)PWMduty;		/* Zero the counter */
	OCR0B = (char)PWMduty;		/* This is the PWM comparator test value */
	TIMSK = 0x08;				/* Be sure the harware interupts are off but compare OS0B */
	GTCCR = 0x00;				/* Start the PWM */
	}	


/*  DonP 20090217 - Removed to save program space
void FactoryDiagnostics(void)
	// Factory test 
	{
	// Test for Trigger Key voltage 
	TestValue = GetVoltage(Trigger,0);
	if ((TestValue < 4392) || (TestValue > 5368)) {return;}
	EventLog(0);
	// Turn on data ports for output - Only reset when battery disconnected 
	DDRB = 0x2B;
	PORTB = 0x01;
TestResult:
	TestCode = 1;
	PORTB = 0x01; // Turn FETs off 
	Timer(2000);
	TestValue = GetVoltage(Trigger,0); // Trigger sense test 
	if ((TestValue < 4734) || (TestValue > 5025)) {TestCode = 2;}
	TestValue = GetVoltage(Trigger,1); // Trigger sense test 
	if ((TestValue < 4734) || (TestValue > 5025)) {TestCode = 3;}
	TestValue = GetVoltage(Drain,1); // Drain sense test 
	if ((TestValue < 4734) || (TestValue > 5025)) {TestCode = 4;}
	PORTB = 0x03; // Turn Q1 Drive FET on 
	Timer(1);
	TestValue = GetVoltage(Drain,1); // Q1 test 
	if ((TestValue < 0) || (TestValue > 10)) {TestCode = 5;}
	PORTB = 0x01; // Turn Q1 Drive FET off 
	Timer(1);
	PORTB = 0x00; // Turn Q2 Break FET on 
	Timer(1);
	TestValue = GetVoltage(Drain,1); // Q2 test 
	if ((TestValue < 9701) || (TestValue > 10301)) {TestCode = 6;}
	PORTB = 0x01; // Turn Q2 Break FET off 
	Timer(1);
	// Blink Error Code to AUX5 LED 
	for (TestCounter = 0; TestCounter < TestCode; TestCounter++)
		{
		PORTB = 0x21; // LED on 
		Timer(500);	// Wait 500mS 
		PORTB = 0x01; // LED off 
		Timer(500);	// Wait 500mS 
		}
	if (TestCode != 1)
		{
		// Output bad value visually in mS 
		Timer(1500);
		PORTB = 0x21; // LED on 
		Timer(TestValue);
		PORTB = 0x01; // LED off
		Timer(2000);
		}

	//
	//1 = Normal
	//2 = 20V Trigger Voltage
	//3 = 10V Trigger Voltage
	//4 = Drain open circuit voltage
	//5 = Q1 FET drive
	//6 = Q2 FET drive
	//

	// Store Lock flags to EEPROM 
	EEPROM_write(0x20, (char)SemiOnlyLock);
	EEPROM_write(0x21, (char)AutoRoll);
	EEPROM_write(0x22, (char)SoftStartEnable);
	EEPROM_write(0x23, (char)HardBatteryVoltage / 100);

goto TestResult; // Keep Flashing Code 

	}
*/

/*  DonP -- see below
void EventLog(char Event)
	//	0 = Factory Test Performed	0x40
	//	1 = High Battery Voltage  	0x41
	//	2 = Peak Current Limit		0x42
	//	3 = Over Temperature Limit	0x43
	//	4 = Battery Voltage Reset	0x44 
	{
	Event = Event + 64;
	EventOld = EEPROM_read((char)(Event));
	EventOld++;
	if (EventOld > 250) {EventOld = 250;}
	EEPROM_write((char)Event,(char)EventOld);
	return;
	}
*/


// DonP 20080904 - ErrorLog() to replace above EventLog()
// Logs errors between two EEPROM locations in a cyclic fashion.
void EventLog(char eventcode)
{
	// Self-maintaining
	//
	// Errors are logged between ERRLOG_MEMSTART and ERRLOG_MEMEND.
	// Last location written is always contained in ERRLOG_BOOKMARK_LOCN
	//
	// Read ERRLOG_BOOKMARK_LOCN, then write to that +1, and write the new address to
	// ERRLOG_BOOKMARK_LOCN.
	//
	// If ERRLOG_LOCN ever becomes higher than ERRLOG_MEMEND, then make it ERRLOG_MEMSTART.
	// --------------------
	// Error Code Meanings:
	//
	//	0  = Factory Test Performed
	//	1  = High Battery Voltage  
	//	2  = Peak Current Limit	
	//	3  = Over Temperature Limit
	//	4  = Battery Voltage Reset  		(This # and lower are from Terry's original)
	//  5  = Programming mode entered
	//  6  = Factory reset initiated
	//  7  = 
	//  8  =
	//  9  =
	//  10 =
	//  11 =
	//  12 =
	//  

	// Read last location to which we wrote a log entry.
	ErrorLogAddress = EEPROM_read( ERRLOG_BOOKMARK_LOCN );
	if( ErrorLogAddress == 0xFF )	// Fresh from programming!
		ErrorLogAddress = (ERRLOG_MEMSTART-1);	// -1 because we inc by 1 in a moment.

	// Inc by 1
	ErrorLogAddress++;

	// If it's now past the end of our log memory, loop it around to the beginning.
	if( ErrorLogAddress > ERRLOG_MEMEND )
		ErrorLogAddress = ERRLOG_MEMSTART;

	// Write out our entry (obliterating the existing one, which is also the oldest one.)
	EEPROM_write(ErrorLogAddress, (char)eventcode);

	// Write out the location of the last entry written.
	EEPROM_write(ERRLOG_BOOKMARK_LOCN, (char)ErrorLogAddress);

	return;
}
// End DonP


unsigned char LiPOStatus(void)
	/*	0 = No Monitor Present
		1 = LiPO Battery OK
		2 = LiPO Battery Weak
		3 = LiPO Battery Dead  */
	{
	PORTB = Neutral;	/* Be sure pullups are on */
	Timer(1);
	/* Aux voltages are /4 since there are no voltage dividers */
	/* Voltages are im mV */
	Aux0Data = GetVoltage(Aux0,0) / 4;
	Aux1Data = GetVoltage(Aux1,0) / 4;
	if ((Aux0Data < 300) && (Aux1Data < 300)) {LiPOInstalled = 1; return 1;}
	if (Aux1Data > 500) {return 3;}
	if (Aux0Data > 500) {return 2;}
	return 1;
	}


//void Diagnostic(long DiagTime)
/* program test output to led.  Only for development use */
/*	
	{
	DDRB = 0x2B;
	PORTB = 0x21; 		// LED on 
	Timer(RunningTemp);	// Wait 500mS 
	PORTB = 0x01; 		// LED off 
	Timer(500);			// Wait 500mS 
	SetAUXPorts();
	}
*/

/*
void SecretMode(void)
{
	// Semi Lock 
	Vibrate();
	if (EEPROM_read(0x20) != 0x02) {EEPROM_write(0x20, 0x00);}
	count6 = 0;
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} 
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage)) && (TriggerHold == 0)) 
		{
		if (EEPROM_read(0x20) == 0x00) {EEPROM_write(0x20, 0x01);}
		count6++;
		if (count6 == 10) {EEPROM_write(0x20, 0x02);}
		count = 0; TriggerHold = 1;
		}
	}

	// AutoRoll 
	Vibrate1(1);Vibrate();
	EEPROM_write(0x21, 0x01);
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} 
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage)) && (TriggerHold == 0)) 
		{
		EEPROM_write(0x21, 0x00);
		count = 0; TriggerHold = 1;
		}
	}

	// Soft Start 
	Vibrate1(2);Vibrate();
	EEPROM_write(0x22, 0x01);
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} 
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage)) && (TriggerHold == 0)) 
		{
		EEPROM_write(0x22, 0x00);
		count = 0; TriggerHold = 1;
		}
	}

	// HardBatteryVoltage 
	Vibrate1(3);Vibrate();
	EEPROM_write(0x23, 0x00);
	count6 = 0;
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < TrigTrip) {TriggerHold = 0;} 
	if (((GetVoltage(Trigger,0) > LowBatteryVoltage)) && (TriggerHold == 0)) 
		{
		EEPROM_write(0x23, (char)(70 + 5 * count6));
		count6++;
		count = 0; TriggerHold = 1;
		}
	} 
}
*/

/* End of program */

