//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
//    be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//

//////////////////////////////////////////////////////////////////////////////
//                       Handy - An Atari Lynx Emulator                     //
//                          Copyright (c) 1996,1997                         //
//                                 K. Wilkins                               //
//////////////////////////////////////////////////////////////////////////////
// Mikey class header file                                                  //
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// This header file provides the interface definition and some of the code  //
// for the Mikey chip within the Lynx. The most crucial code is the         //
// Update() function which as you can probably guess updates all of the     //
// Mikey hardware counters and screen DMA from the prevous time it was      //
// called. Yes I know how to spell Mikey but I cant be bothered to change   //
// it everywhere.                                                           //
//                                                                          //
//    K. Wilkins                                                            //
// August 1997                                                              //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
// Revision History:                                                        //
// -----------------                                                        //
//                                                                          //
// 01Aug1997 KW Document header added & class documented.                   //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

#ifndef MIKIE_H
#define MIKIE_H

//#include <crtdbg.h>
//#define	TRACE_MIKIE

#ifdef TRACE_MIKIE

#define TRACE_MIKIE0(msg)					_RPT1(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",gSystemCycleCount)
#define TRACE_MIKIE1(msg,arg1)				_RPT2(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,gSystemCycleCount)
#define TRACE_MIKIE2(msg,arg1,arg2)			_RPT3(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount)
#define TRACE_MIKIE3(msg,arg1,arg2,arg3)	_RPT4(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount)

#else

#define TRACE_MIKIE0(msg)
#define TRACE_MIKIE1(msg,arg1)
#define TRACE_MIKIE2(msg,arg1,arg2)
#define TRACE_MIKIE3(msg,arg1,arg2,arg3)

#endif

class CSystem;

#define MIKIE_START	0xfd00
#define MIKIE_SIZE	0x100

//
// Define counter types and defines
//

#define CTRL_A_IRQEN	0x80
#define CTRL_A_RTD		0x40
#define CTRL_A_RELOAD	0x10
#define CTRL_A_COUNT	0x08
#define CTRL_A_DIVIDE	0x07

#define CTRL_B_TDONE	0x08
#define CTRL_B_LASTCK	0x04
#define CTRL_B_CIN		0x02
#define CTRL_B_COUT		0x01

#define LINE_TIMER		0x00
#define SCREEN_TIMER	0x02

#define LINE_WIDTH		160
#define	LINE_SIZE		80

#define UART_TX_INACTIVE	0x80000000
#define UART_RX_INACTIVE	0x80000000
#define UART_BREAK_CODE		0x00008000
#define	UART_MAX_RX_QUEUE	32
#define UART_TX_TIME_PERIOD	(11)
#define UART_RX_TIME_PERIOD	(11)
#define UART_RX_NEXT_DELAY	(44)

typedef struct
{
	UBYTE	backup;
	UBYTE	count;
	UBYTE	controlA;
	UBYTE	controlB;
	bool	linkedlastcarry;
}MTIMER;

typedef struct
{
	union
	{
		struct
		{
#ifdef MSB_FIRST
			UBYTE unused:4;
			UBYTE Colour:1;
			UBYTE FourColour:1;
			UBYTE Flip:1;
			UBYTE DMAEnable:1;
#else

			UBYTE DMAEnable:1;
			UBYTE Flip:1;
			UBYTE FourColour:1;
			UBYTE Colour:1;
			UBYTE unused:4;
#endif
		}Bits;
		UBYTE Byte;
	};
}TDISPCTL;

typedef struct
{
	union
	{
		struct
		{
#ifdef MSB_FIRST
			UBYTE unused:8;
			UBYTE unused2:8;
			UBYTE unused3:4;
			UBYTE Blue:4;
			UBYTE Red:4;
			UBYTE Green:4;
#else
			UBYTE Green:4;
			UBYTE Red:4;
			UBYTE Blue:4;
#endif
          }Colours;
          ULONG     Index;
     };
}TPALETTE;


//
// Emumerated types for possible mikie windows independant modes
//
enum
{
	MIKIE_BAD_MODE=0,
	MIKIE_NO_ROTATE,
	MIKIE_ROTATE_L,
	MIKIE_ROTATE_R
};

enum
{
	MIKIE_PIXEL_FORMAT_8BPP=0,
	MIKIE_PIXEL_FORMAT_16BPP_555,
	MIKIE_PIXEL_FORMAT_16BPP_565,
	MIKIE_PIXEL_FORMAT_24BPP,
	MIKIE_PIXEL_FORMAT_32BPP,
};

// Wii
extern int gAudioSkipCount;

class CMikie : public CLynxBase
{
	public:
		CMikie(CSystem& parent);
		~CMikie();
	
		bool	ContextSave(FILE *fp);
		bool	ContextLoad(LSS_FILE *fp);
		void	Reset(void);

		UBYTE	Peek(ULONG addr);
		void	Poke(ULONG addr,UBYTE data);
		ULONG	ReadCycle(void) {return 5;};
		ULONG	WriteCycle(void) {return 5;};
		ULONG	ObjectSize(void) {return MIKIE_SIZE;};
		void	PresetForHomebrew(void);
		ULONG	GetLfsrNext(ULONG current);

		void	ComLynxCable(int status);
		void	ComLynxRxData(int data);
		void	ComLynxTxLoopback(int data);
		void	ComLynxTxCallback(void (*function)(int data,ULONG objref),ULONG objref);
		
		void	DisplaySetAttributes(ULONG Rotate, ULONG Format, ULONG Pitch, UBYTE* (*DisplayCallback)(ULONG objref),ULONG objref);
		
		void	BlowOut(void);

		ULONG	DisplayRenderLine(void);
		ULONG	DisplayEndOfFrame(void);

		inline void SetCPUSleep(void) {gSystemCPUSleep=TRUE;};
		inline void ClearCPUSleep(void) {gSystemCPUSleep=FALSE;gSystemCPUSleep_Saved=FALSE;};

		inline void	Update(void)
		{
			SLONG divide;
			SLONG decval;
			ULONG tmp;
			ULONG mikie_work_done=0;

			//
			// To stop problems with cycle count wrap we will check and then correct the
			// cycle counter.
			//

//			TRACE_MIKIE0("Update()");

			if(gSystemCycleCount>0xf0000000)
			{
				gSystemCycleCount-=0x80000000;
				gThrottleNextCycleCheckpoint-=0x80000000;
				gAudioLastUpdateCycle-=0x80000000;
				mTIM_0_LAST_COUNT-=0x80000000;
				mTIM_1_LAST_COUNT-=0x80000000;
				mTIM_2_LAST_COUNT-=0x80000000;
				mTIM_3_LAST_COUNT-=0x80000000;
				mTIM_4_LAST_COUNT-=0x80000000;
				mTIM_5_LAST_COUNT-=0x80000000;
				mTIM_6_LAST_COUNT-=0x80000000;
				mTIM_7_LAST_COUNT-=0x80000000;
				mAUDIO_0_LAST_COUNT-=0x80000000;
				mAUDIO_1_LAST_COUNT-=0x80000000;
				mAUDIO_2_LAST_COUNT-=0x80000000;
				mAUDIO_3_LAST_COUNT-=0x80000000;
				// Only correct if sleep is active
				if(gCPUWakeupTime)
				{
					gCPUWakeupTime-=0x80000000;
					gIRQEntryCycle-=0x80000000;
				}
			}

			gNextTimerEvent=0xffffffff;

			//
			// Check if the CPU needs to be woken up from sleep mode
			//
			if(gCPUWakeupTime)
			{
				if(gSystemCycleCount>=gCPUWakeupTime)
				{
					TRACE_MIKIE0("*********************************************************");
					TRACE_MIKIE0("****              CPU SLEEP COMPLETED                ****");
					TRACE_MIKIE0("*********************************************************");
					ClearCPUSleep();
					gCPUWakeupTime=0;			
				}
				else
				{
					if(gCPUWakeupTime>gSystemCycleCount) gNextTimerEvent=gCPUWakeupTime;
				}
			}

			//	Timer updates, rolled out flat in group order
			//
			//	Group A:
			//	Timer 0 -> Timer 2 -> Timer 4. 
			//
			//	Group B:
			//	Timer 1 -> Timer 3 -> Timer 5 -> Timer 7 -> Audio 0 -> Audio 1-> Audio 2 -> Audio 3 -> Timer 1. 
			//

			//
			// Within each timer code block we will predict the cycle count number of
			// the next timer event
			//
			// We don't need to count linked timers as the timer they are linked
			// from will always generate earlier events.
			//
			// As Timer 4 (UART) will generate many events we will ignore it
			//
			// We set the next event to the end of time at first and let the timers
			// overload it. Any writes to timer controls will force next event to
			// be immediate and hence a new preidction will be done. The prediction
			// causes overflow as opposed to zero i.e. current+1
			// (In reality T0 line counter should always be running.)
			//

			
			//
			// Timer 0 of Group A
			//

			//
			// Optimisation, assume T0 (Line timer) is never in one-shot,
			// never placed in link mode
			//

			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
//			if(mTIM_0_ENABLE_COUNT && (mTIM_0_ENABLE_RELOAD || !mTIM_0_TIMER_DONE))
			if(mTIM_0_ENABLE_COUNT)
			{
				// Timer 0 has no linking
//				if(mTIM_0_LINKING!=0x07)
				{
					// Ordinary clocked mode as opposed to linked mode
					// 16MHz clock downto 1us == cyclecount >> 4 
					divide=(4+mTIM_0_LINKING);
					decval=(gSystemCycleCount-mTIM_0_LAST_COUNT)>>divide;

					if(decval)
					{
						mTIM_0_LAST_COUNT+=decval<<divide;
						mTIM_0_CURRENT-=decval;

						if(mTIM_0_CURRENT&0x80000000)
						{
							// Set carry out
							mTIM_0_BORROW_OUT=TRUE;
	
//							// Reload if neccessary
//							if(mTIM_0_ENABLE_RELOAD)
//							{
								mTIM_0_CURRENT+=mTIM_0_BKUP+1;
//							}
//							else
//							{
//								mTIM_0_CURRENT=0;
//							}

							mTIM_0_TIMER_DONE=TRUE;

							// Interupt flag setting code moved into DisplayRenderLine()

							// Line timer has expired, render a line, we cannot incrememnt
							// the global counter at this point as it will screw the other timers
							// so we save under work done and inc at the end.
							mikie_work_done+=DisplayRenderLine();

						}
						else
						{
							mTIM_0_BORROW_OUT=FALSE;
						}
						// Set carry in as we did a count
						mTIM_0_BORROW_IN=TRUE;
					}
					else
					{
						// Clear carry in as we didn't count
						mTIM_0_BORROW_IN=FALSE;
						// Clear carry out
						mTIM_0_BORROW_OUT=FALSE;
					}
				}

				// Prediction for next timer event cycle number

//				if(mTIM_0_LINKING!=7)
				{
					// Sometimes timeupdates can be >2x rollover in which case
					// then CURRENT may still be negative and we can use it to
					// calc the next timer value, we just want another update ASAP
					tmp=(mTIM_0_CURRENT&0x80000000)?1:((mTIM_0_CURRENT+1)<<divide);
					tmp+=gSystemCycleCount;
					if(tmp<gNextTimerEvent)
					{
						gNextTimerEvent=tmp;
//						TRACE_MIKIE1("Update() - TIMER 0 Set NextTimerEvent = %012d",gNextTimerEvent);
					}
				}
//				TRACE_MIKIE1("Update() - mTIM_0_CURRENT = %012d",mTIM_0_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_0_BKUP    = %012d",mTIM_0_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_0_LASTCNT = %012d",mTIM_0_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_0_LINKING = %012d",mTIM_0_LINKING);
			}
	
			//
			// Timer 2 of Group A
			//

			//
			// Optimisation, assume T2 (Frame timer) is never in one-shot
			// always in linked mode i.e clocked by Line Timer
			//
			
			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
//			if(mTIM_2_ENABLE_COUNT && (mTIM_2_ENABLE_RELOAD || !mTIM_2_TIMER_DONE))
			if(mTIM_2_ENABLE_COUNT)
			{
				decval=0;
		
//				if(mTIM_2_LINKING==0x07)
				{
					if(mTIM_0_BORROW_OUT) decval=1;
					mTIM_2_LAST_LINK_CARRY=mTIM_0_BORROW_OUT;
				}
//				else
//				{
//					// Ordinary clocked mode as opposed to linked mode
//					// 16MHz clock downto 1us == cyclecount >> 4 
//					divide=(4+mTIM_2_LINKING);
//					decval=(gSystemCycleCount-mTIM_2_LAST_COUNT)>>divide;
//				}
		
				if(decval)
				{
//					mTIM_2_LAST_COUNT+=decval<<divide;
					mTIM_2_CURRENT-=decval;
					if(mTIM_2_CURRENT&0x80000000)
					{
						// Set carry out
						mTIM_2_BORROW_OUT=TRUE;
				
//						// Reload if neccessary
//						if(mTIM_2_ENABLE_RELOAD)
//						{
							mTIM_2_CURRENT+=mTIM_2_BKUP+1;
//						}
//						else
//						{
//							mTIM_2_CURRENT=0;
//						}
						mTIM_2_TIMER_DONE=TRUE;

						// Interupt flag setting code moved into DisplayEndOfFrame(), also
						// park any CPU cycles lost for later inclusion
						mikie_work_done+=DisplayEndOfFrame();
					}
					else
					{
						mTIM_2_BORROW_OUT=FALSE;
					}
					// Set carry in as we did a count
					mTIM_2_BORROW_IN=TRUE;
				}
				else
				{
					// Clear carry in as we didn't count
					mTIM_2_BORROW_IN=FALSE;
					// Clear carry out
					mTIM_2_BORROW_OUT=FALSE;
				}

				// Prediction for next timer event cycle number
// We dont need to predict this as its the frame timer and will always
// be beaten by the line timer on Timer 0
//				if(mTIM_2_LINKING!=7)
//				{
//					tmp=gSystemCycleCount+((mTIM_2_CURRENT+1)<<divide);
//					if(tmp<gNextTimerEvent)	gNextTimerEvent=tmp;
//				}
//				TRACE_MIKIE1("Update() - mTIM_2_CURRENT = %012d",mTIM_2_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_2_BKUP    = %012d",mTIM_2_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_2_LASTCNT = %012d",mTIM_2_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_2_LINKING = %012d",mTIM_2_LINKING);
			}
		
			//
			// Timer 4 of Group A
			//
			// For the sake of speed it is assumed that Timer 4 (UART timer)
			// never uses one-shot mode, never uses linking, hence the code
			// is commented out. Timer 4 is at the end of a chain and seems
			// no reason to update its carry in-out variables
			//

			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
//			if(mTIM_4_ENABLE_COUNT && (mTIM_4_ENABLE_RELOAD || !mTIM_4_TIMER_DONE))
			if(mTIM_4_ENABLE_COUNT)
			{
				decval=0;
		
//				if(mTIM_4_LINKING==0x07)
//				{
////				if(mTIM_2_BORROW_OUT && !mTIM_4_LAST_LINK_CARRY) decval=1;
//					if(mTIM_2_BORROW_OUT) decval=1;
//					mTIM_4_LAST_LINK_CARRY=mTIM_2_BORROW_OUT;
//				}
//				else
				{
					// Ordinary clocked mode as opposed to linked mode
					// 16MHz clock downto 1us == cyclecount >> 4 
					// Additional /8 (+3) for 8 clocks per bit transmit
					divide=4+3+mTIM_4_LINKING;
					decval=(gSystemCycleCount-mTIM_4_LAST_COUNT)>>divide;
				}
		
				if(decval)
				{
					mTIM_4_LAST_COUNT+=decval<<divide;
					mTIM_4_CURRENT-=decval;
					if(mTIM_4_CURRENT&0x80000000)
					{
						// Set carry out
						mTIM_4_BORROW_OUT=TRUE;
		
						//
						// Update the UART counter models for Rx & Tx
						//
		
						//
						// According to the docs IRQ's are level triggered and hence will always assert
						// what a pain in the arse
						//
						// Rx & Tx are loopedback due to comlynx structure

						//
						// Receive
						//
						if(!mUART_RX_COUNTDOWN)
						{
							// Fetch a byte from the input queue
							if(mUART_Rx_waiting>0)
							{
								mUART_RX_DATA=mUART_Rx_input_queue[mUART_Rx_output_ptr];
								mUART_Rx_output_ptr=(++mUART_Rx_output_ptr)%UART_MAX_RX_QUEUE;
								mUART_Rx_waiting--;
								TRACE_MIKIE2("Update() - RX Byte output ptr=%02d waiting=%02d",mUART_Rx_output_ptr,mUART_Rx_waiting);
							}
							else
							{
								TRACE_MIKIE0("Update() - RX Byte but no data waiting ????");
							}

							// Retrigger input if more bytes waiting
							if(mUART_Rx_waiting>0)
							{
								mUART_RX_COUNTDOWN=UART_RX_TIME_PERIOD+UART_RX_NEXT_DELAY;
								TRACE_MIKIE1("Update() - RX Byte retriggered, %d waiting",mUART_Rx_waiting);
							}
							else
							{
								mUART_RX_COUNTDOWN=UART_RX_INACTIVE;
								TRACE_MIKIE0("Update() - RX Byte nothing waiting, deactivated");
							}

							// If RX_READY already set then we have an overrun
							// as previous byte hasnt been read
							if(mUART_RX_READY) mUART_Rx_overun_error=1;

							// Flag byte as being recvd
							mUART_RX_READY=1;
						}
						else if(!(mUART_RX_COUNTDOWN&UART_RX_INACTIVE))
						{
							mUART_RX_COUNTDOWN--;
						}

						if(!mUART_TX_COUNTDOWN)
						{
							if(mUART_SENDBREAK)
							{
								mUART_TX_DATA=UART_BREAK_CODE;
								// Auto-Respawn new transmit
								mUART_TX_COUNTDOWN=UART_TX_TIME_PERIOD;
								// Loop back what we transmitted
								ComLynxTxLoopback(mUART_TX_DATA);
							}
							else
							{
								// Serial activity finished 
								mUART_TX_COUNTDOWN=UART_TX_INACTIVE;
							}

							// If a networking object is attached then use its callback to send the data byte.
							if(mpUART_TX_CALLBACK)
							{
								TRACE_MIKIE0("Update() - UART_TX_CALLBACK");
								(*mpUART_TX_CALLBACK)(mUART_TX_DATA,mUART_TX_CALLBACK_OBJECT);
							}

						}
						else if(!(mUART_TX_COUNTDOWN&UART_TX_INACTIVE))
						{
							mUART_TX_COUNTDOWN--;
						}

						// Set the timer status flag
						// Timer 4 is the uart timer and doesn't generate IRQ's using this method
		
						// 16 Clocks = 1 bit transmission. Hold separate Rx & Tx counters
		
						// Reload if neccessary
//						if(mTIM_4_ENABLE_RELOAD)
//						{
							mTIM_4_CURRENT+=mTIM_4_BKUP+1;
							// The low reload values on TIM4 coupled with a longer
							// timer service delay can sometimes cause
							// an underun, check and fix
							if(mTIM_4_CURRENT&0x80000000)
							{
								mTIM_4_CURRENT=mTIM_4_BKUP;
								mTIM_4_LAST_COUNT=gSystemCycleCount;
							}
//						}
//						else
//						{
//							mTIM_4_CURRENT=0;
//						}
//						mTIM_4_TIMER_DONE=TRUE;
					}
//					else
//					{
//						mTIM_4_BORROW_OUT=FALSE;
//					}
//					// Set carry in as we did a count
//					mTIM_4_BORROW_IN=TRUE;
				}
//				else
//				{
//					// Clear carry in as we didn't count
//					mTIM_4_BORROW_IN=FALSE;
//					// Clear carry out
//					mTIM_4_BORROW_OUT=FALSE;
//				}
//
//				// Prediction for next timer event cycle number
//
//				if(mTIM_4_LINKING!=7)
//				{
					// Sometimes timeupdates can be >2x rollover in which case
					// then CURRENT may still be negative and we can use it to
					// calc the next timer value, we just want another update ASAP
					tmp=(mTIM_4_CURRENT&0x80000000)?1:((mTIM_4_CURRENT+1)<<divide);
					tmp+=gSystemCycleCount;
					if(tmp<gNextTimerEvent)
					{
						gNextTimerEvent=tmp;
						TRACE_MIKIE1("Update() - TIMER 4 Set NextTimerEvent = %012d",gNextTimerEvent);
					}
//				}
//				TRACE_MIKIE1("Update() - mTIM_4_CURRENT = %012d",mTIM_4_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_4_BKUP    = %012d",mTIM_4_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_4_LASTCNT = %012d",mTIM_4_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_4_LINKING = %012d",mTIM_4_LINKING);
			}

			// Emulate the UART bug where UART IRQ is level sensitive
			// in that it will continue to generate interrupts as long
			// as they are enabled and the interrupt condition is true

			// If Tx is inactive i.e ready for a byte to eat and the
			// IRQ is enabled then generate it always
			if((mUART_TX_COUNTDOWN&UART_TX_INACTIVE) && mUART_TX_IRQ_ENABLE)
			{
				TRACE_MIKIE0("Update() - UART TX IRQ Triggered");
				mTimerStatusFlags|=0x10;
				gSystemIRQ=TRUE;	// Added 19/09/06 fix for IRQ issue
			}
			// Is data waiting and the interrupt enabled, if so then
			// what are we waiting for....
			if(mUART_RX_READY && mUART_RX_IRQ_ENABLE)
			{
				TRACE_MIKIE0("Update() - UART RX IRQ Triggered");
				mTimerStatusFlags|=0x10;
				gSystemIRQ=TRUE;	// Added 19/09/06 fix for IRQ issue
			}
		
			//
			// Timer 1 of Group B
			//
			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
			if(mTIM_1_ENABLE_COUNT && (mTIM_1_ENABLE_RELOAD || !mTIM_1_TIMER_DONE))
			{
				if(mTIM_1_LINKING!=0x07)
				{
					// Ordinary clocked mode as opposed to linked mode
					// 16MHz clock downto 1us == cyclecount >> 4 
					divide=(4+mTIM_1_LINKING);
					decval=(gSystemCycleCount-mTIM_1_LAST_COUNT)>>divide;
		
					if(decval)
					{
						mTIM_1_LAST_COUNT+=decval<<divide;
						mTIM_1_CURRENT-=decval;
						if(mTIM_1_CURRENT&0x80000000)
						{
							// Set carry out
							mTIM_1_BORROW_OUT=TRUE;
		
							// Set the timer status flag
							if(mTimerInterruptMask&0x02)
							{
								TRACE_MIKIE0("Update() - TIMER1 IRQ Triggered");
								mTimerStatusFlags|=0x02;
								gSystemIRQ=TRUE;	// Added 19/09/06 fix for IRQ issue
							}
		
							// Reload if neccessary
							if(mTIM_1_ENABLE_RELOAD)
							{
								mTIM_1_CURRENT+=mTIM_1_BKUP+1;
							}
							else
							{
								mTIM_1_CURRENT=0;
							}
							mTIM_1_TIMER_DONE=TRUE;
						}
						else
						{
							mTIM_1_BORROW_OUT=FALSE;
						}
						// Set carry in as we did a count
						mTIM_1_BORROW_IN=TRUE;
					}
					else
					{
						// Clear carry in as we didn't count
						mTIM_1_BORROW_IN=FALSE;
						// Clear carry out
						mTIM_1_BORROW_OUT=FALSE;
					}
				}

				// Prediction for next timer event cycle number

				if(mTIM_1_LINKING!=7)
				{
					// Sometimes timeupdates can be >2x rollover in which case
					// then CURRENT may still be negative and we can use it to
					// calc the next timer value, we just want another update ASAP
					tmp=(mTIM_1_CURRENT&0x80000000)?1:((mTIM_1_CURRENT+1)<<divide);
					tmp+=gSystemCycleCount;
					if(tmp<gNextTimerEvent)
					{
						gNextTimerEvent=tmp;
						TRACE_MIKIE1("Update() - TIMER 1 Set NextTimerEvent = %012d",gNextTimerEvent);
					}
				}
//				TRACE_MIKIE1("Update() - mTIM_1_CURRENT = %012d",mTIM_1_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_1_BKUP    = %012d",mTIM_1_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_1_LASTCNT = %012d",mTIM_1_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_1_LINKING = %012d",mTIM_1_LINKING);
			}
		
			//
			// Timer 3 of Group A
			//
			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
			if(mTIM_3_ENABLE_COUNT && (mTIM_3_ENABLE_RELOAD || !mTIM_3_TIMER_DONE))
			{
				decval=0;
		
				if(mTIM_3_LINKING==0x07)
				{
					if(mTIM_1_BORROW_OUT) decval=1;
					mTIM_3_LAST_LINK_CARRY=mTIM_1_BORROW_OUT;
				}
				else
				{
					// Ordinary clocked mode as opposed to linked mode
					// 16MHz clock downto 1us == cyclecount >> 4 
					divide=(4+mTIM_3_LINKING);
					decval=(gSystemCycleCount-mTIM_3_LAST_COUNT)>>divide;
				}
		
				if(decval)
				{
					mTIM_3_LAST_COUNT+=decval<<divide;
					mTIM_3_CURRENT-=decval;
					if(mTIM_3_CURRENT&0x80000000)
					{
						// Set carry out
						mTIM_3_BORROW_OUT=TRUE;
		
						// Set the timer status flag
						if(mTimerInterruptMask&0x08)
						{
							TRACE_MIKIE0("Update() - TIMER3 IRQ Triggered");
							mTimerStatusFlags|=0x08;
							gSystemIRQ=TRUE;	// Added 19/09/06 fix for IRQ issue
						}
		
						// Reload if neccessary
						if(mTIM_3_ENABLE_RELOAD)
						{
							mTIM_3_CURRENT+=mTIM_3_BKUP+1;
						}
						else
						{
							mTIM_3_CURRENT=0;
						}
						mTIM_3_TIMER_DONE=TRUE;
					}
					else
					{
						mTIM_3_BORROW_OUT=FALSE;
					}
					// Set carry in as we did a count
					mTIM_3_BORROW_IN=TRUE;
				}
				else
				{
					// Clear carry in as we didn't count
					mTIM_3_BORROW_IN=FALSE;
					// Clear carry out
					mTIM_3_BORROW_OUT=FALSE;
				}

				// Prediction for next timer event cycle number

				if(mTIM_3_LINKING!=7)
				{
					// Sometimes timeupdates can be >2x rollover in which case
					// then CURRENT may still be negative and we can use it to
					// calc the next timer value, we just want another update ASAP
					tmp=(mTIM_3_CURRENT&0x80000000)?1:((mTIM_3_CURRENT+1)<<divide);
					tmp+=gSystemCycleCount;
					if(tmp<gNextTimerEvent)
					{
						gNextTimerEvent=tmp;
						TRACE_MIKIE1("Update() - TIMER 3 Set NextTimerEvent = %012d",gNextTimerEvent);
					}
				}
//				TRACE_MIKIE1("Update() - mTIM_3_CURRENT = %012d",mTIM_3_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_3_BKUP    = %012d",mTIM_3_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_3_LASTCNT = %012d",mTIM_3_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_3_LINKING = %012d",mTIM_3_LINKING);
			}
		
			//
			// Timer 5 of Group A
			//
			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
			if(mTIM_5_ENABLE_COUNT && (mTIM_5_ENABLE_RELOAD || !mTIM_5_TIMER_DONE))
			{
				decval=0;
		
				if(mTIM_5_LINKING==0x07)
				{
					if(mTIM_3_BORROW_OUT) decval=1;
					mTIM_5_LAST_LINK_CARRY=mTIM_3_BORROW_OUT;
				}
				else
				{
					// Ordinary clocked mode as opposed to linked mode
					// 16MHz clock downto 1us == cyclecount >> 4 
					divide=(4+mTIM_5_LINKING);
					decval=(gSystemCycleCount-mTIM_5_LAST_COUNT)>>divide;
				}
		
				if(decval)
				{
					mTIM_5_LAST_COUNT+=decval<<divide;
					mTIM_5_CURRENT-=decval;
					if(mTIM_5_CURRENT&0x80000000)
					{
						// Set carry out
						mTIM_5_BORROW_OUT=TRUE;
		
						// Set the timer status flag
						if(mTimerInterruptMask&0x20)
						{
							TRACE_MIKIE0("Update() - TIMER5 IRQ Triggered");
							mTimerStatusFlags|=0x20;
							gSystemIRQ=TRUE;	// Added 19/09/06 fix for IRQ issue
						}
		
						// Reload if neccessary
						if(mTIM_5_ENABLE_RELOAD)
						{
							mTIM_5_CURRENT+=mTIM_5_BKUP+1;
						}
						else
						{
							mTIM_5_CURRENT=0;
						}
						mTIM_5_TIMER_DONE=TRUE;
					}
					else
					{
						mTIM_5_BORROW_OUT=FALSE;
					}
					// Set carry in as we did a count
					mTIM_5_BORROW_IN=TRUE;
				}
				else
				{
					// Clear carry in as we didn't count
					mTIM_5_BORROW_IN=FALSE;
					// Clear carry out
					mTIM_5_BORROW_OUT=FALSE;
				}

				// Prediction for next timer event cycle number

				if(mTIM_5_LINKING!=7)
				{
					// Sometimes timeupdates can be >2x rollover in which case
					// then CURRENT may still be negative and we can use it to
					// calc the next timer value, we just want another update ASAP
					tmp=(mTIM_5_CURRENT&0x80000000)?1:((mTIM_5_CURRENT+1)<<divide);
					tmp+=gSystemCycleCount;
					if(tmp<gNextTimerEvent)
					{
						gNextTimerEvent=tmp;
						TRACE_MIKIE1("Update() - TIMER 5 Set NextTimerEvent = %012d",gNextTimerEvent);
					}
				}
//				TRACE_MIKIE1("Update() - mTIM_5_CURRENT = %012d",mTIM_5_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_5_BKUP    = %012d",mTIM_5_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_5_LASTCNT = %012d",mTIM_5_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_5_LINKING = %012d",mTIM_5_LINKING);
			}
		
			//
			// Timer 7 of Group A
			//
			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
			if(mTIM_7_ENABLE_COUNT && (mTIM_7_ENABLE_RELOAD || !mTIM_7_TIMER_DONE))
			{
				decval=0;
		
				if(mTIM_7_LINKING==0x07)
				{
					if(mTIM_5_BORROW_OUT) decval=1;
					mTIM_7_LAST_LINK_CARRY=mTIM_5_BORROW_OUT;
				}
				else
				{
					// Ordinary clocked mode as opposed to linked mode
					// 16MHz clock downto 1us == cyclecount >> 4 
					divide=(4+mTIM_7_LINKING);
					decval=(gSystemCycleCount-mTIM_7_LAST_COUNT)>>divide;
				}
		
				if(decval)
				{
					mTIM_7_LAST_COUNT+=decval<<divide;
					mTIM_7_CURRENT-=decval;
					if(mTIM_7_CURRENT&0x80000000)
					{
						// Set carry out
						mTIM_7_BORROW_OUT=TRUE;
		
						// Set the timer status flag
						if(mTimerInterruptMask&0x80)
						{
							TRACE_MIKIE0("Update() - TIMER7 IRQ Triggered");
							mTimerStatusFlags|=0x80;
							gSystemIRQ=TRUE;	// Added 19/09/06 fix for IRQ issue
						}
		
						// Reload if neccessary
						if(mTIM_7_ENABLE_RELOAD)
						{
							mTIM_7_CURRENT+=mTIM_7_BKUP+1;
						}
						else
						{
							mTIM_7_CURRENT=0;
						}
						mTIM_7_TIMER_DONE=TRUE;
		
					}
					else
					{
						mTIM_7_BORROW_OUT=FALSE;
					}
					// Set carry in as we did a count
					mTIM_7_BORROW_IN=TRUE;
				}
				else
				{
					// Clear carry in as we didn't count
					mTIM_7_BORROW_IN=FALSE;
					// Clear carry out
					mTIM_7_BORROW_OUT=FALSE;
				}

				// Prediction for next timer event cycle number

				if(mTIM_7_LINKING!=7)
				{
					// Sometimes timeupdates can be >2x rollover in which case
					// then CURRENT may still be negative and we can use it to
					// calc the next timer value, we just want another update ASAP
					tmp=(mTIM_7_CURRENT&0x80000000)?1:((mTIM_7_CURRENT+1)<<divide);
					tmp+=gSystemCycleCount;
					if(tmp<gNextTimerEvent)
					{
						gNextTimerEvent=tmp;
						TRACE_MIKIE1("Update() - TIMER 7 Set NextTimerEvent = %012d",gNextTimerEvent);
					}
				}
//				TRACE_MIKIE1("Update() - mTIM_7_CURRENT = %012d",mTIM_7_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_7_BKUP    = %012d",mTIM_7_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_7_LASTCNT = %012d",mTIM_7_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_7_LINKING = %012d",mTIM_7_LINKING);
			}
		
			//
			// Timer 6 has no group
			//
			// KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD ||  ..) 
			if(mTIM_6_ENABLE_COUNT && (mTIM_6_ENABLE_RELOAD || !mTIM_6_TIMER_DONE))
			{
//				if(mTIM_6_LINKING!=0x07)
				{
					// Ordinary clocked mode as opposed to linked mode
					// 16MHz clock downto 1us == cyclecount >> 4 
					divide=(4+mTIM_6_LINKING);
					decval=(gSystemCycleCount-mTIM_6_LAST_COUNT)>>divide;
		
					if(decval)
					{
						mTIM_6_LAST_COUNT+=decval<<divide;
						mTIM_6_CURRENT-=decval;
						if(mTIM_6_CURRENT&0x80000000)
						{
							// Set carry out
							mTIM_6_BORROW_OUT=TRUE;
		
							// Set the timer status flag
							if(mTimerInterruptMask&0x40)
							{
								TRACE_MIKIE0("Update() - TIMER6 IRQ Triggered");
								mTimerStatusFlags|=0x40;
								gSystemIRQ=TRUE;	// Added 19/09/06 fix for IRQ issue
							}
		
							// Reload if neccessary
							if(mTIM_6_ENABLE_RELOAD)
							{
								mTIM_6_CURRENT+=mTIM_6_BKUP+1;
							}
							else
							{
								mTIM_6_CURRENT=0;
							}
							mTIM_6_TIMER_DONE=TRUE;
						}
						else
						{
							mTIM_6_BORROW_OUT=FALSE;
						}
						// Set carry in as we did a count
						mTIM_6_BORROW_IN=TRUE;
					}
					else
					{
						// Clear carry in as we didn't count
						mTIM_6_BORROW_IN=FALSE;
						// Clear carry out
						mTIM_6_BORROW_OUT=FALSE;
					}
				}

				// Prediction for next timer event cycle number
				// (Timer 6 doesn't support linking)

//				if(mTIM_6_LINKING!=7)
				{
					// Sometimes timeupdates can be >2x rollover in which case
					// then CURRENT may still be negative and we can use it to
					// calc the next timer value, we just want another update ASAP
					tmp=(mTIM_6_CURRENT&0x80000000)?1:((mTIM_6_CURRENT+1)<<divide);
					tmp+=gSystemCycleCount;
					if(tmp<gNextTimerEvent)
					{
						gNextTimerEvent=tmp;
						TRACE_MIKIE1("Update() - TIMER 6 Set NextTimerEvent = %012d",gNextTimerEvent);
					}
				}
//				TRACE_MIKIE1("Update() - mTIM_6_CURRENT = %012d",mTIM_6_CURRENT);
//				TRACE_MIKIE1("Update() - mTIM_6_BKUP    = %012d",mTIM_6_BKUP);
//				TRACE_MIKIE1("Update() - mTIM_6_LASTCNT = %012d",mTIM_6_LAST_COUNT);
//				TRACE_MIKIE1("Update() - mTIM_6_LINKING = %012d",mTIM_6_LINKING);
			}

			//
			// If sound is enabled then update the sound subsystem
			//
			if(gAudioEnabled)
			{
				static SLONG sample=0;
				ULONG mix=0;
				
				//
				// Catch audio buffer up to current time
				//
				
				// Mix the sample
				sample=0;
				if(mSTEREO&0x11) { sample+=mAUDIO_0_OUTPUT; mix++; }
				if(mSTEREO&0x22) { sample+=mAUDIO_1_OUTPUT; mix++; }
				if(mSTEREO&0x44) { sample+=mAUDIO_2_OUTPUT; mix++; }
				if(mSTEREO&0x88) { sample+=mAUDIO_3_OUTPUT; mix++; }
				if(mix)
				{
					sample+=128*mix; // Correct for sign
					sample/=mix;	// Keep the audio volume at max
				}
				else
				{
					sample=128;
				}

//				sample+=(mSTEREO&0x11)?mAUDIO_0_OUTPUT:0;
//				sample+=(mSTEREO&0x22)?mAUDIO_1_OUTPUT:0;
//				sample+=(mSTEREO&0x44)?mAUDIO_2_OUTPUT:0;
//				sample+=(mSTEREO&0x88)?mAUDIO_3_OUTPUT:0;
//				sample=sample>>2;
//				sample+=128;

				for(;gAudioLastUpdateCycle+HANDY_AUDIO_SAMPLE_PERIOD<gSystemCycleCount;gAudioLastUpdateCycle+=HANDY_AUDIO_SAMPLE_PERIOD)
				{
					// Output audio sample
					gAudioBuffer[gAudioBufferPointer++]=(UBYTE)sample;

					// Check buffer overflow condition, stick at the endpoint
					// teh audio output system will reset the input pointer
					// when it reads out the data.

					// We should NEVER overflow, this buffer holds 0.25 seconds
					// of data if this happens the the multimedia system above
					// has failed so the corruption of the buffer contents wont matter

                    // Wii
                    ULONG prev = gAudioBufferPointer;

					gAudioBufferPointer%=HANDY_AUDIO_BUFFER_SIZE;

                    // Wii
                    if( prev > gAudioBufferPointer )
                    {
                        gAudioSkipCount++;
                    }
				}

				//
				// Audio 0 
				//
//				if(mAUDIO_0_ENABLE_COUNT && !mAUDIO_0_TIMER_DONE && mAUDIO_0_VOLUME && mAUDIO_0_BKUP)
				if(mAUDIO_0_ENABLE_COUNT && (mAUDIO_0_ENABLE_RELOAD || !mAUDIO_0_TIMER_DONE) && mAUDIO_0_VOLUME && mAUDIO_0_BKUP)
				{
					decval=0;
		
					if(mAUDIO_0_LINKING==0x07)
					{
						if(mTIM_7_BORROW_OUT) decval=1;
						mAUDIO_0_LAST_LINK_CARRY=mTIM_7_BORROW_OUT;
					}
					else
					{
						// Ordinary clocked mode as opposed to linked mode
						// 16MHz clock downto 1us == cyclecount >> 4 
						divide=(4+mAUDIO_0_LINKING);
						decval=(gSystemCycleCount-mAUDIO_0_LAST_COUNT)>>divide;
					}
		
					if(decval)
					{
						mAUDIO_0_LAST_COUNT+=decval<<divide;
						mAUDIO_0_CURRENT-=decval;
						if(mAUDIO_0_CURRENT&0x80000000)
						{
							// Set carry out
							mAUDIO_0_BORROW_OUT=TRUE;
		
							// Reload if neccessary
							if(mAUDIO_0_ENABLE_RELOAD)
							{
								mAUDIO_0_CURRENT+=mAUDIO_0_BKUP+1;
								if(mAUDIO_0_CURRENT&0x80000000) mAUDIO_0_CURRENT=0;
							}
							else
							{
								// Set timer done
								mAUDIO_0_TIMER_DONE=TRUE;
								mAUDIO_0_CURRENT=0;
							}

							//
							// Update audio circuitry
							//
							mAUDIO_0_WAVESHAPER=GetLfsrNext(mAUDIO_0_WAVESHAPER);

							if(mAUDIO_0_INTEGRATE_ENABLE)
							{
								SLONG temp=mAUDIO_0_OUTPUT;
								if(mAUDIO_0_WAVESHAPER&0x0001) temp+=mAUDIO_0_VOLUME; else temp-=mAUDIO_0_VOLUME;
								if(temp>127) temp=127;
								if(temp<-128) temp=-128;
								mAUDIO_0_OUTPUT=(SBYTE)temp;
							}
							else
							{
								if(mAUDIO_0_WAVESHAPER&0x0001) mAUDIO_0_OUTPUT=mAUDIO_0_VOLUME; else mAUDIO_0_OUTPUT=-mAUDIO_0_VOLUME;
							}
						}
						else
						{
							mAUDIO_0_BORROW_OUT=FALSE;
						}
						// Set carry in as we did a count
						mAUDIO_0_BORROW_IN=TRUE;
					}
					else
					{
						// Clear carry in as we didn't count
						mAUDIO_0_BORROW_IN=FALSE;
						// Clear carry out
						mAUDIO_0_BORROW_OUT=FALSE;
					}

					// Prediction for next timer event cycle number

					if(mAUDIO_0_LINKING!=7)
					{
						// Sometimes timeupdates can be >2x rollover in which case
						// then CURRENT may still be negative and we can use it to
						// calc the next timer value, we just want another update ASAP
						tmp=(mAUDIO_0_CURRENT&0x80000000)?1:((mAUDIO_0_CURRENT+1)<<divide);
						tmp+=gSystemCycleCount;
						if(tmp<gNextTimerEvent)
						{
							gNextTimerEvent=tmp;
							TRACE_MIKIE1("Update() - AUDIO 0 Set NextTimerEvent = %012d",gNextTimerEvent);
						}
					}
//					TRACE_MIKIE1("Update() - mAUDIO_0_CURRENT = %012d",mAUDIO_0_CURRENT);
//					TRACE_MIKIE1("Update() - mAUDIO_0_BKUP    = %012d",mAUDIO_0_BKUP);
//					TRACE_MIKIE1("Update() - mAUDIO_0_LASTCNT = %012d",mAUDIO_0_LAST_COUNT);
//					TRACE_MIKIE1("Update() - mAUDIO_0_LINKING = %012d",mAUDIO_0_LINKING);
				}

				//
				// Audio 1 
				//
//				if(mAUDIO_1_ENABLE_COUNT && !mAUDIO_1_TIMER_DONE && mAUDIO_1_VOLUME && mAUDIO_1_BKUP)
				if(mAUDIO_1_ENABLE_COUNT && (mAUDIO_1_ENABLE_RELOAD || !mAUDIO_1_TIMER_DONE) && mAUDIO_1_VOLUME && mAUDIO_1_BKUP)
				{
					decval=0;
		
					if(mAUDIO_1_LINKING==0x07)
					{
						if(mAUDIO_0_BORROW_OUT) decval=1;
						mAUDIO_1_LAST_LINK_CARRY=mAUDIO_0_BORROW_OUT;
					}
					else
					{
						// Ordinary clocked mode as opposed to linked mode
						// 16MHz clock downto 1us == cyclecount >> 4 
						divide=(4+mAUDIO_1_LINKING);
						decval=(gSystemCycleCount-mAUDIO_1_LAST_COUNT)>>divide;
					}
		
					if(decval)
					{
						mAUDIO_1_LAST_COUNT+=decval<<divide;
						mAUDIO_1_CURRENT-=decval;
						if(mAUDIO_1_CURRENT&0x80000000)
						{
							// Set carry out
							mAUDIO_1_BORROW_OUT=TRUE;
		
							// Reload if neccessary
							if(mAUDIO_1_ENABLE_RELOAD)
							{
								mAUDIO_1_CURRENT+=mAUDIO_1_BKUP+1;
								if(mAUDIO_1_CURRENT&0x80000000) mAUDIO_1_CURRENT=0;
							}
							else
							{
								// Set timer done
								mAUDIO_1_TIMER_DONE=TRUE;
								mAUDIO_1_CURRENT=0;
							}

							//
							// Update audio circuitry
							//
							mAUDIO_1_WAVESHAPER=GetLfsrNext(mAUDIO_1_WAVESHAPER);

							if(mAUDIO_1_INTEGRATE_ENABLE)
							{
								SLONG temp=mAUDIO_1_OUTPUT;
								if(mAUDIO_1_WAVESHAPER&0x0001) temp+=mAUDIO_1_VOLUME; else temp-=mAUDIO_1_VOLUME;
								if(temp>127) temp=127;
								if(temp<-128) temp=-128;
								mAUDIO_1_OUTPUT=(SBYTE)temp;
							}
							else
							{
								if(mAUDIO_1_WAVESHAPER&0x0001) mAUDIO_1_OUTPUT=mAUDIO_1_VOLUME; else mAUDIO_1_OUTPUT=-mAUDIO_1_VOLUME;
							}
						}
						else
						{
							mAUDIO_1_BORROW_OUT=FALSE;
						}
						// Set carry in as we did a count
						mAUDIO_1_BORROW_IN=TRUE;
					}
					else
					{
						// Clear carry in as we didn't count
						mAUDIO_1_BORROW_IN=FALSE;
						// Clear carry out
						mAUDIO_1_BORROW_OUT=FALSE;
					}

					// Prediction for next timer event cycle number

					if(mAUDIO_1_LINKING!=7)
					{
						// Sometimes timeupdates can be >2x rollover in which case
						// then CURRENT may still be negative and we can use it to
						// calc the next timer value, we just want another update ASAP
						tmp=(mAUDIO_1_CURRENT&0x80000000)?1:((mAUDIO_1_CURRENT+1)<<divide);
						tmp+=gSystemCycleCount;
						if(tmp<gNextTimerEvent)
						{
							gNextTimerEvent=tmp;
							TRACE_MIKIE1("Update() - AUDIO 1 Set NextTimerEvent = %012d",gNextTimerEvent);
						}
					}
//					TRACE_MIKIE1("Update() - mAUDIO_1_CURRENT = %012d",mAUDIO_1_CURRENT);
//					TRACE_MIKIE1("Update() - mAUDIO_1_BKUP    = %012d",mAUDIO_1_BKUP);
//					TRACE_MIKIE1("Update() - mAUDIO_1_LASTCNT = %012d",mAUDIO_1_LAST_COUNT);
//					TRACE_MIKIE1("Update() - mAUDIO_1_LINKING = %012d",mAUDIO_1_LINKING);
				}

				//
				// Audio 2 
				//
//				if(mAUDIO_2_ENABLE_COUNT && !mAUDIO_2_TIMER_DONE && mAUDIO_2_VOLUME && mAUDIO_2_BKUP)
				if(mAUDIO_2_ENABLE_COUNT && (mAUDIO_2_ENABLE_RELOAD || !mAUDIO_2_TIMER_DONE) && mAUDIO_2_VOLUME && mAUDIO_2_BKUP)
				{
					decval=0;
		
					if(mAUDIO_2_LINKING==0x07)
					{
						if(mAUDIO_1_BORROW_OUT) decval=1;
						mAUDIO_2_LAST_LINK_CARRY=mAUDIO_1_BORROW_OUT;
					}
					else
					{
						// Ordinary clocked mode as opposed to linked mode
						// 16MHz clock downto 1us == cyclecount >> 4 
						divide=(4+mAUDIO_2_LINKING);
						decval=(gSystemCycleCount-mAUDIO_2_LAST_COUNT)>>divide;
					}
		
					if(decval)
					{
						mAUDIO_2_LAST_COUNT+=decval<<divide;
						mAUDIO_2_CURRENT-=decval;
						if(mAUDIO_2_CURRENT&0x80000000)
						{
							// Set carry out
							mAUDIO_2_BORROW_OUT=TRUE;
		
							// Reload if neccessary
							if(mAUDIO_2_ENABLE_RELOAD)
							{
								mAUDIO_2_CURRENT+=mAUDIO_2_BKUP+1;
								if(mAUDIO_2_CURRENT&0x80000000) mAUDIO_2_CURRENT=0;
							}
							else
							{
								// Set timer done
								mAUDIO_2_TIMER_DONE=TRUE;
								mAUDIO_2_CURRENT=0;
							}

							//
							// Update audio circuitry
							//
							mAUDIO_2_WAVESHAPER=GetLfsrNext(mAUDIO_2_WAVESHAPER);

							if(mAUDIO_2_INTEGRATE_ENABLE)
							{
								SLONG temp=mAUDIO_2_OUTPUT;
								if(mAUDIO_2_WAVESHAPER&0x0001) temp+=mAUDIO_2_VOLUME; else temp-=mAUDIO_2_VOLUME;
								if(temp>127) temp=127;
								if(temp<-128) temp=-128;
								mAUDIO_2_OUTPUT=(SBYTE)temp;
							}
							else
							{
								if(mAUDIO_2_WAVESHAPER&0x0001) mAUDIO_2_OUTPUT=mAUDIO_2_VOLUME; else mAUDIO_2_OUTPUT=-mAUDIO_2_VOLUME;
							}
						}
						else
						{
							mAUDIO_2_BORROW_OUT=FALSE;
						}
						// Set carry in as we did a count
						mAUDIO_2_BORROW_IN=TRUE;
					}
					else
					{
						// Clear carry in as we didn't count
						mAUDIO_2_BORROW_IN=FALSE;
						// Clear carry out
						mAUDIO_2_BORROW_OUT=FALSE;
					}

					// Prediction for next timer event cycle number

					if(mAUDIO_2_LINKING!=7)
					{
						// Sometimes timeupdates can be >2x rollover in which case
						// then CURRENT may still be negative and we can use it to
						// calc the next timer value, we just want another update ASAP
						tmp=(mAUDIO_2_CURRENT&0x80000000)?1:((mAUDIO_2_CURRENT+1)<<divide);
						tmp+=gSystemCycleCount;
						if(tmp<gNextTimerEvent)
						{
							gNextTimerEvent=tmp;
							TRACE_MIKIE1("Update() - AUDIO 2 Set NextTimerEvent = %012d",gNextTimerEvent);
						}
					}
//					TRACE_MIKIE1("Update() - mAUDIO_2_CURRENT = %012d",mAUDIO_2_CURRENT);
//					TRACE_MIKIE1("Update() - mAUDIO_2_BKUP    = %012d",mAUDIO_2_BKUP);
//					TRACE_MIKIE1("Update() - mAUDIO_2_LASTCNT = %012d",mAUDIO_2_LAST_COUNT);
//					TRACE_MIKIE1("Update() - mAUDIO_2_LINKING = %012d",mAUDIO_2_LINKING);
				}

				//
				// Audio 3
				//
//				if(mAUDIO_3_ENABLE_COUNT && !mAUDIO_3_TIMER_DONE && mAUDIO_3_VOLUME && mAUDIO_3_BKUP)
				if(mAUDIO_3_ENABLE_COUNT && (mAUDIO_3_ENABLE_RELOAD || !mAUDIO_3_TIMER_DONE) && mAUDIO_3_VOLUME && mAUDIO_3_BKUP)
				{
					decval=0;
		
					if(mAUDIO_3_LINKING==0x07)
					{
						if(mAUDIO_2_BORROW_OUT) decval=1;
						mAUDIO_3_LAST_LINK_CARRY=mAUDIO_2_BORROW_OUT;
					}
					else
					{
						// Ordinary clocked mode as opposed to linked mode
						// 16MHz clock downto 1us == cyclecount >> 4 
						divide=(4+mAUDIO_3_LINKING);
						decval=(gSystemCycleCount-mAUDIO_3_LAST_COUNT)>>divide;
					}
		
					if(decval)
					{
						mAUDIO_3_LAST_COUNT+=decval<<divide;
						mAUDIO_3_CURRENT-=decval;
						if(mAUDIO_3_CURRENT&0x80000000)
						{
							// Set carry out
							mAUDIO_3_BORROW_OUT=TRUE;
		
							// Reload if neccessary
							if(mAUDIO_3_ENABLE_RELOAD)
							{
								mAUDIO_3_CURRENT+=mAUDIO_3_BKUP+1;
								if(mAUDIO_3_CURRENT&0x80000000) mAUDIO_3_CURRENT=0;
							}
							else
							{
								// Set timer done
								mAUDIO_3_TIMER_DONE=TRUE;
								mAUDIO_3_CURRENT=0;
							}

							//
							// Update audio circuitry
							//
							mAUDIO_3_WAVESHAPER=GetLfsrNext(mAUDIO_3_WAVESHAPER);

							if(mAUDIO_3_INTEGRATE_ENABLE)
							{
								SLONG temp=mAUDIO_3_OUTPUT;
								if(mAUDIO_3_WAVESHAPER&0x0001) temp+=mAUDIO_3_VOLUME; else temp-=mAUDIO_3_VOLUME;
								if(temp>127) temp=127;
								if(temp<-128) temp=-128;
								mAUDIO_3_OUTPUT=(SBYTE)temp;
							}
							else
							{
								if(mAUDIO_3_WAVESHAPER&0x0001) mAUDIO_3_OUTPUT=mAUDIO_3_VOLUME; else mAUDIO_3_OUTPUT=-mAUDIO_3_VOLUME;
							}
						}
						else
						{
							mAUDIO_3_BORROW_OUT=FALSE;
						}
						// Set carry in as we did a count
						mAUDIO_3_BORROW_IN=TRUE;
					}
					else
					{
						// Clear carry in as we didn't count
						mAUDIO_3_BORROW_IN=FALSE;
						// Clear carry out
						mAUDIO_3_BORROW_OUT=FALSE;
					}

					// Prediction for next timer event cycle number

					if(mAUDIO_3_LINKING!=7)
					{
						// Sometimes timeupdates can be >2x rollover in which case
						// then CURRENT may still be negative and we can use it to
						// calc the next timer value, we just want another update ASAP
						tmp=(mAUDIO_3_CURRENT&0x80000000)?1:((mAUDIO_3_CURRENT+1)<<divide);
						tmp+=gSystemCycleCount;
						if(tmp<gNextTimerEvent)
						{
							gNextTimerEvent=tmp;
							TRACE_MIKIE1("Update() - AUDIO 3 Set NextTimerEvent = %012d",gNextTimerEvent);
						}
					}
//					TRACE_MIKIE1("Update() - mAUDIO_3_CURRENT = %012d",mAUDIO_3_CURRENT);
//					TRACE_MIKIE1("Update() - mAUDIO_3_BKUP    = %012d",mAUDIO_3_BKUP);
//					TRACE_MIKIE1("Update() - mAUDIO_3_LASTCNT = %012d",mAUDIO_3_LAST_COUNT);
//					TRACE_MIKIE1("Update() - mAUDIO_3_LINKING = %012d",mAUDIO_3_LINKING);
				}
			}

//			if(gSystemCycleCount==gNextTimerEvent) gError->Warning("CMikie::Update() - gSystemCycleCount==gNextTimerEvent, system lock likely");
//			TRACE_MIKIE1("Update() - NextTimerEvent = %012d",gNextTimerEvent);

			// Now all the timer updates are done we can increment the system
			// counter for any work done within the Update() function, gSystemCycleCounter
			// cannot be updated until this point otherwise it screws up the counters.
			gSystemCycleCount+=mikie_work_done;
		}

	private:
		CSystem		&mSystem;

		// Hardware storage
		
		ULONG		mDisplayAddress;
		ULONG		mAudioInputComparator;
		ULONG		mTimerStatusFlags;
		ULONG		mTimerInterruptMask;

		TPALETTE	mPalette[16];
		ULONG		mColourMap[4096];

		ULONG		mIODAT;
		ULONG		mIODIR;
		ULONG		mIODAT_REST_SIGNAL;

		ULONG		mDISPCTL_DMAEnable;
		ULONG		mDISPCTL_Flip;
		ULONG		mDISPCTL_FourColour;
		ULONG		mDISPCTL_Colour;

		ULONG		mTIM_0_BKUP;
		ULONG		mTIM_0_ENABLE_RELOAD;
		ULONG		mTIM_0_ENABLE_COUNT;
		ULONG		mTIM_0_LINKING;
		ULONG		mTIM_0_CURRENT;
		ULONG		mTIM_0_TIMER_DONE;
		ULONG		mTIM_0_LAST_CLOCK;
		ULONG		mTIM_0_BORROW_IN;
		ULONG		mTIM_0_BORROW_OUT;
		ULONG		mTIM_0_LAST_LINK_CARRY;
		ULONG		mTIM_0_LAST_COUNT;

		ULONG		mTIM_1_BKUP;
		ULONG		mTIM_1_ENABLE_RELOAD;
		ULONG		mTIM_1_ENABLE_COUNT;
		ULONG		mTIM_1_LINKING;
		ULONG		mTIM_1_CURRENT;
		ULONG		mTIM_1_TIMER_DONE;
		ULONG		mTIM_1_LAST_CLOCK;
		ULONG		mTIM_1_BORROW_IN;
		ULONG		mTIM_1_BORROW_OUT;
		ULONG		mTIM_1_LAST_LINK_CARRY;
		ULONG		mTIM_1_LAST_COUNT;

		ULONG		mTIM_2_BKUP;
		ULONG		mTIM_2_ENABLE_RELOAD;
		ULONG		mTIM_2_ENABLE_COUNT;
		ULONG		mTIM_2_LINKING;
		ULONG		mTIM_2_CURRENT;
		ULONG		mTIM_2_TIMER_DONE;
		ULONG		mTIM_2_LAST_CLOCK;
		ULONG		mTIM_2_BORROW_IN;
		ULONG		mTIM_2_BORROW_OUT;
		ULONG		mTIM_2_LAST_LINK_CARRY;
		ULONG		mTIM_2_LAST_COUNT;

		ULONG		mTIM_3_BKUP;
		ULONG		mTIM_3_ENABLE_RELOAD;
		ULONG		mTIM_3_ENABLE_COUNT;
		ULONG		mTIM_3_LINKING;
		ULONG		mTIM_3_CURRENT;
		ULONG		mTIM_3_TIMER_DONE;
		ULONG		mTIM_3_LAST_CLOCK;
		ULONG		mTIM_3_BORROW_IN;
		ULONG		mTIM_3_BORROW_OUT;
		ULONG		mTIM_3_LAST_LINK_CARRY;
		ULONG		mTIM_3_LAST_COUNT;

		ULONG		mTIM_4_BKUP;
		ULONG		mTIM_4_ENABLE_RELOAD;
		ULONG		mTIM_4_ENABLE_COUNT;
		ULONG		mTIM_4_LINKING;
		ULONG		mTIM_4_CURRENT;
		ULONG		mTIM_4_TIMER_DONE;
		ULONG		mTIM_4_LAST_CLOCK;
		ULONG		mTIM_4_BORROW_IN;
		ULONG		mTIM_4_BORROW_OUT;
		ULONG		mTIM_4_LAST_LINK_CARRY;
		ULONG		mTIM_4_LAST_COUNT;

		ULONG		mTIM_5_BKUP;
		ULONG		mTIM_5_ENABLE_RELOAD;
		ULONG		mTIM_5_ENABLE_COUNT;
		ULONG		mTIM_5_LINKING;
		ULONG		mTIM_5_CURRENT;
		ULONG		mTIM_5_TIMER_DONE;
		ULONG		mTIM_5_LAST_CLOCK;
		ULONG		mTIM_5_BORROW_IN;
		ULONG		mTIM_5_BORROW_OUT;
		ULONG		mTIM_5_LAST_LINK_CARRY;
		ULONG		mTIM_5_LAST_COUNT;

		ULONG		mTIM_6_BKUP;
		ULONG		mTIM_6_ENABLE_RELOAD;
		ULONG		mTIM_6_ENABLE_COUNT;
		ULONG		mTIM_6_LINKING;
		ULONG		mTIM_6_CURRENT;
		ULONG		mTIM_6_TIMER_DONE;
		ULONG		mTIM_6_LAST_CLOCK;
		ULONG		mTIM_6_BORROW_IN;
		ULONG		mTIM_6_BORROW_OUT;
		ULONG		mTIM_6_LAST_LINK_CARRY;
		ULONG		mTIM_6_LAST_COUNT;

		ULONG		mTIM_7_BKUP;
		ULONG		mTIM_7_ENABLE_RELOAD;
		ULONG		mTIM_7_ENABLE_COUNT;
		ULONG		mTIM_7_LINKING;
		ULONG		mTIM_7_CURRENT;
		ULONG		mTIM_7_TIMER_DONE;
		ULONG		mTIM_7_LAST_CLOCK;
		ULONG		mTIM_7_BORROW_IN;
		ULONG		mTIM_7_BORROW_OUT;
		ULONG		mTIM_7_LAST_LINK_CARRY;
		ULONG		mTIM_7_LAST_COUNT;

		ULONG		mAUDIO_0_BKUP;
		ULONG		mAUDIO_0_ENABLE_RELOAD;
		ULONG		mAUDIO_0_ENABLE_COUNT;
		ULONG		mAUDIO_0_LINKING;
		ULONG		mAUDIO_0_CURRENT;
		ULONG		mAUDIO_0_TIMER_DONE;
		ULONG		mAUDIO_0_LAST_CLOCK;
		ULONG		mAUDIO_0_BORROW_IN;
		ULONG		mAUDIO_0_BORROW_OUT;
		ULONG		mAUDIO_0_LAST_LINK_CARRY;
		ULONG		mAUDIO_0_LAST_COUNT;
		SBYTE		mAUDIO_0_VOLUME;
		SBYTE		mAUDIO_0_OUTPUT;
		ULONG		mAUDIO_0_INTEGRATE_ENABLE;
		ULONG		mAUDIO_0_WAVESHAPER;

		ULONG		mAUDIO_1_BKUP;
		ULONG		mAUDIO_1_ENABLE_RELOAD;
		ULONG		mAUDIO_1_ENABLE_COUNT;
		ULONG		mAUDIO_1_LINKING;
		ULONG		mAUDIO_1_CURRENT;
		ULONG		mAUDIO_1_TIMER_DONE;
		ULONG		mAUDIO_1_LAST_CLOCK;
		ULONG		mAUDIO_1_BORROW_IN;
		ULONG		mAUDIO_1_BORROW_OUT;
		ULONG		mAUDIO_1_LAST_LINK_CARRY;
		ULONG		mAUDIO_1_LAST_COUNT;
		SBYTE		mAUDIO_1_VOLUME;
		SBYTE		mAUDIO_1_OUTPUT;
		ULONG		mAUDIO_1_INTEGRATE_ENABLE;
		ULONG		mAUDIO_1_WAVESHAPER;

		ULONG		mAUDIO_2_BKUP;
		ULONG		mAUDIO_2_ENABLE_RELOAD;
		ULONG		mAUDIO_2_ENABLE_COUNT;
		ULONG		mAUDIO_2_LINKING;
		ULONG		mAUDIO_2_CURRENT;
		ULONG		mAUDIO_2_TIMER_DONE;
		ULONG		mAUDIO_2_LAST_CLOCK;
		ULONG		mAUDIO_2_BORROW_IN;
		ULONG		mAUDIO_2_BORROW_OUT;
		ULONG		mAUDIO_2_LAST_LINK_CARRY;
		ULONG		mAUDIO_2_LAST_COUNT;
		SBYTE		mAUDIO_2_VOLUME;
		SBYTE		mAUDIO_2_OUTPUT;
		ULONG		mAUDIO_2_INTEGRATE_ENABLE;
		ULONG		mAUDIO_2_WAVESHAPER;

		ULONG		mAUDIO_3_BKUP;
		ULONG		mAUDIO_3_ENABLE_RELOAD;
		ULONG		mAUDIO_3_ENABLE_COUNT;
		ULONG		mAUDIO_3_LINKING;
		ULONG		mAUDIO_3_CURRENT;
		ULONG		mAUDIO_3_TIMER_DONE;
		ULONG		mAUDIO_3_LAST_CLOCK;
		ULONG		mAUDIO_3_BORROW_IN;
		ULONG		mAUDIO_3_BORROW_OUT;
		ULONG		mAUDIO_3_LAST_LINK_CARRY;
		ULONG		mAUDIO_3_LAST_COUNT;
		SBYTE		mAUDIO_3_VOLUME;
		SBYTE		mAUDIO_3_OUTPUT;
		ULONG		mAUDIO_3_INTEGRATE_ENABLE;
		ULONG		mAUDIO_3_WAVESHAPER;

		ULONG		mSTEREO;

		//
		// Serial related variables
		//
		ULONG		mUART_RX_IRQ_ENABLE;
		ULONG		mUART_TX_IRQ_ENABLE;

		ULONG		mUART_RX_COUNTDOWN;
		ULONG		mUART_TX_COUNTDOWN;

		ULONG		mUART_SENDBREAK;
		ULONG		mUART_TX_DATA;
		ULONG		mUART_RX_DATA;
		ULONG		mUART_RX_READY;

		ULONG		mUART_PARITY_ENABLE;
		ULONG		mUART_PARITY_EVEN;

		int			mUART_CABLE_PRESENT;
		void		(*mpUART_TX_CALLBACK)(int data,ULONG objref);
		ULONG		mUART_TX_CALLBACK_OBJECT;

		int			mUART_Rx_input_queue[UART_MAX_RX_QUEUE];
		unsigned int mUART_Rx_input_ptr;
		unsigned int mUART_Rx_output_ptr;
		int			mUART_Rx_waiting;
		int			mUART_Rx_framing_error;
		int			mUART_Rx_overun_error;

		//
		// Screen related
		//
		
		UBYTE		*mpDisplayBits;
		UBYTE		*mpDisplayCurrent;
		UBYTE		*mpRamPointer;
		ULONG		mLynxLine;
		ULONG		mLynxLineDMACounter;
		ULONG		mLynxAddr;

		ULONG		mDisplayRotate;
		ULONG		mDisplayFormat;
		ULONG		mDisplayPitch;
		UBYTE*		(*mpDisplayCallback)(ULONG objref);
		ULONG		mDisplayCallbackObject;
};


#endif
