/* ================================================================================== */
/* Copyright (c) 1998-1999 3Com Corporation or its subsidiaries. All rights reserved. */
/* ================================================================================== */

#include "EmulatorCommon.h"
#include "Bank_MC68EZ328.h"

#include "Bank_ROM.h"			// ROMBank::IsPCInRAM
#include "Byteswapping.h"		// ByteswapWords
#include "CPU_REG.h"			// Software::BusError
#include "DebugMgr.h"			// Debug::CheckStepSpy
#include "Miscellaneous.h"		// GetHostTime
#include "RAM_ROM.h"			// gMemoryAccess
#include "SessionFile.h"		// WriteHwrDBallEZType, ReadHwrDBallEZType


typedef uae_u32	(*ReadFunction)(uaecptr address, int size);
typedef void	(*WriteFunction)(uaecptr address, int size, uae_u32 value);


// Macro to return the DragonballEZ address of the specified register

#define addressof(x)	(kMemoryStart + offsetof(HwrDBallEZType, x))


// Macros for reading/writing DragonballEZ registers.

#define READ_REGISTER(reg)	\
	((sizeof(g68EZ328Regs.reg) == 1) ? do_get_mem_byte(&g68EZ328Regs.reg) :	\
	 (sizeof(g68EZ328Regs.reg) == 2) ? do_get_mem_word(&g68EZ328Regs.reg) :	\
									 do_get_mem_long(&g68EZ328Regs.reg))

#define WRITE_REGISTER(reg, value)	\
	(sizeof(g68EZ328Regs.reg) == 1) ? do_put_mem_byte(&g68EZ328Regs.reg, value) :	\
	(sizeof(g68EZ328Regs.reg) == 2) ? do_put_mem_word(&g68EZ328Regs.reg, value) :	\
									do_put_mem_long(&g68EZ328Regs.reg, value)

class MC68EZ328Reg
{
	public:
		static void		SetHandler				(ReadFunction, WriteFunction, uae_u32 start, int count);

		static uae_u32	UnsupportedRead			(uaecptr address, int size);
		static void		UnsupportedWrite		(uaecptr address, int size, uae_u32 value);

		static uae_u32	StdRead					(uaecptr address, int size);
		static void		StdWrite				(uaecptr address, int size, uae_u32 value);
		static void		NullWrite				(uaecptr address, int size, uae_u32 value);	// For read-only registers

		static uae_u32	pllFreqSelRead			(uaecptr address, int size);
		static uae_u32	portDDataRead			(uaecptr address, int size);
		static uae_u32	portEDataRead			(uaecptr address, int size);
		static uae_u32	tmr1StatusRead			(uaecptr address, int size);
		static uae_u32	UartRead				(uaecptr address, int size);
		static uae_u32	rtcHourMinSecRead		(uaecptr address, int size);

		static void		csDSelectWrite			(uaecptr address, int size, uae_u32 value);
		static void		intMaskHiWrite			(uaecptr address, int size, uae_u32 value);
		static void		intMaskLoWrite			(uaecptr address, int size, uae_u32 value);
		static void		intStatusHiWrite		(uaecptr address, int size, uae_u32 value);
		static void		portCDataWrite			(uaecptr address, int size, uae_u32 value);
		static void		portDDataWrite			(uaecptr address, int size, uae_u32 value);
		static void		portDIntReqEnWrite		(uaecptr address, int size, uae_u32 value);
		static void		portFDataWrite			(uaecptr address, int size, uae_u32 value);
		static void		tmr1StatusWrite			(uaecptr address, int size, uae_u32 value);
		static void		spiMasterControlWrite	(uaecptr address, int size, uae_u32 value);
		static void		UartWrite				(uaecptr address, int size, uae_u32 value);
		static void		lcdRegisterWrite		(uaecptr address, int size, uae_u32 value);
		static void		rtcControlWrite			(uaecptr address, int size, uae_u32 value);
		static void		rtcIntStatusWrite		(uaecptr address, int size, uae_u32 value);
		static void		rtcIntEnableWrite		(uaecptr address, int size, uae_u32 value);
};

// Values used to initialize the DragonBallEZ registers.

const HwrDBallEZType	kInitial68EZ328RegisterValues =
{
	0x1C,		// Byte		scr;							// $000: System Control Register
	{ 0 },		// Byte											___filler0[0x004-0x001];
	0x00,		// Byte		chipID;							// $004: Chip ID Register
	0x00,		// Byte		maskID;							// $005: Mask ID Register
	0x00,		// Word		swID;							// $006: Software ID Register
	{ 0 },		// Byte											___filler1[0x100-0x008];				 

	0x0000,		// Word		csAGroupBase;					// $100: Chip Select Group A Base Register
	0x0000,		// Word		csBGroupBase;					// $102: Chip Select Group B Base Register
	0x0000,		// Word		csCGroupBase;					// $104: Chip Select Group C Base Register
	0x0000,		// Word		csDGroupBase;					// $106: Chip Select Group D Base Register

	{ 0 },		// Byte											___filler6[0x110-0x108];

	0x00E0,		// Word		csASelect;						// $110: Group A Chip Select Register
	0x0000,		// Word		csBSelect;						// $112: Group B Chip Select Register
	0x0000,		// Word		csCSelect;						// $114: Group C Chip Select Register
	0x0000,		// Word		csDSelect;						// $116: Group D Chip Select Register

	0x0060,		// Word		emuCS;							// $118: EMU Chip Select Register

	{ 0 },		// Byte											___filler2[0x200-0x11A];		

	0x2430,		// Word		pllControl;						// $200: PLL Control Register
	0x0123,		// Word		pllFreqSel;						// $202: PLL Frequency Select Register
	0,		// !!! ---> Marked as reserved in 1.4 Word		pllTest;						// $204: PLL Test Register (do not access)
	{ 0 },		// Byte											___filler44;
	0x1F,		// Byte		pwrControl;						// $207: Power Control Register

	{ 0 },		// Byte											___filler3[0x300-0x208];

	0x00,		// Byte		intVector;						// $300: Interrupt Vector Register
	{ 0 },		// Byte											___filler4;
	0x0000,		// Word		intControl;						// $302: Interrupt Control Register
	0x00FF,		// Word		intMaskHi;						// $304: Interrupt Mask Register/HIGH word
	0xFFFF,		// Word		intMaskLo;						// $306: Interrupt Mask Register/LOW word
	{ 0 },		// Byte											___filler7[0x30c-0x308];
	0x0000,		// Word		intStatusHi;					// $30C: Interrupt Status Register/HIGH word
	0x0000,		// Word		intStatusLo;					// $30E: Interrupt Status Register/LOW word
	0x0000,		// Word		intPendingHi;					// $310: Interrupt Pending Register
	0x0000,		// Word		intPendingLo;					// $312: Interrupt Pending Register

	{ 0 },		// Byte 										___filler4a[0x400-0x314];

	0x00,		// Byte		portADir;						// $400: Port A Direction Register
	0x00,		// Byte		portAData;						// $401: Port A Data Register
	0xFF,		// Byte		portAPullupEn;					// $402: Port A Pullup Enable (similar to Select on DB)
	{ 0 },		// Byte											___filler8[5];	

	0x00,		// Byte		portBDir;						// $408: Port B Direction Register
	0x00,		// Byte		portBData;						// $409: Port B Data Register
	0xFF,		// Byte		portBPullupEn;					// $40A: Port B Pullup Enable
	0xFF,		// Byte		portBSelect;					// $40B: Port B Select Register	

	{ 0 },		// Byte											___filler9[4];

	0x00,		// Byte		portCDir;						// $410: Port C Direction Register
	0x00,		// Byte		portCData;						// $411: Port C Data Register
	0xFF,		// Byte		portCPulldnEn;					// $412: Port C Pulldown Enable
	0xFF,		// Byte		portCSelect;					// $413: Port C Select Register	

	{ 0 },		// Byte											___filler10[4];

	0x00,		// Byte		portDDir;						// $418: Port D Direction Register
	0x00,		// Byte		portDData;						// $419: Port D Data Register
	0xFF,		// Byte		portDPullupEn;					// $41A: Port D Pull-up Enable
	0xF0,		// Byte		portDSelect;					// $41B: Port D Select Register
	0x00,		// Byte		portDPolarity;					// $41C: Port D Polarity Register
	0x00,		// Byte		portDIntReqEn;					// $41D: Port D Interrupt Request Enable
	0x00,		// Byte		portDKbdIntEn;					// $41E: Port D Keyboard Interrupt Enable
	0x00,		// Byte		portDIntEdge;					// $41F: Port D IRQ Edge Register

	0x00,		// Byte		portEDir;						// $420: Port E Direction Register
	0x00,		// Byte		portEData;						// $421: Port E Data Register
	0xFF,		// Byte		portEPullupEn;					// $422: Port E Pull-up Enable
	0xFF,		// Byte		portESelect;					// $423: Port E Select Register

	{ 0 },		// Byte											___filler14[4];

	0x00,		// Byte		portFDir;						// $428: Port F Direction Register
	0x00,		// Byte		portFData;						// $429: Port F Data Register
	0xFF,		// Byte		portFPullupdnEn;				// $42A: Port F Pull-up/down Enable
	0x00,		// Byte		portFSelect;					// $42B: Port F Select Register

	{ 0 },		// Byte											___filler16[4];

	0x00,		// Byte		portGDir;						// $430: Port G Direction Register
	0x00,		// Byte		portGData;						// $431: Port G Data Register
	0x3D,		// Byte		portGPullupEn;					// $432: Port G Pull-up Enable
	0x08,		// Byte		portGSelect;					// $433: Port G Select Register

	{ 0 },		// Byte											___filler2000[0x500-0x434];

	0x0020,		// Word		pwmControl;						// $500: PWM Control Register
	0x00,		// Byte		pwmSampleHi;					// $502: PWM Sample - high byte
	0x00,		// Byte		pwmSampleLo;					// $503: PWM Sample - low byte
	0xFE,		// Byte		pwmPeriod;						// $504: PWM Period
	0x00,		// Byte		pwmCounter;						// $505: PWM Counter
	
	{ 0 },		// Byte											___filler24[0x600-0x506];

	0x0000,		// Word		tmr1Control;					// $600: Timer 1 Control Register
	0x0000,		// Word		tmr1Prescaler;					// $602: Timer 1 Prescaler Register
	0xFFFF,		// Word		tmr1Compare;					// $604: Timer 1 Compare Register
	0x0000,		// Word		tmr1Capture;					// $606: Timer 1 Capture Register
	0x0000,		// Word		tmr1Counter;					// $608: Timer 1 Counter Register
	0x0000,		// Word		tmr1Status;						// $60A: Timer 1 Status Register

	{ 0 },		// Byte											___filler25[0x800-0x61E];

	0x0000,		// Word		spiMasterData;					// $800: SPI Master Data Register
	0x0000,		// Word		spiMasterControl;				// $802: SPI Master Control Register

	{ 0 },		// Byte											___filler27[0x900-0x804];

	0x0000,		// Word		uControl;						// $900: Uart Control Register
	0x003F,		// Word		uBaud;							// $902: Uart Baud Control Register
	0x0000,		// Word		uReceive;						// $904: Uart Receive Register
	0x0000,		// Word		uTransmit;						// $906: Uart Transmit Register
	0x0000,		// Word		uMisc;							// $908: Uart Miscellaneous Register
	0x0000,		// Word		uNonIntPresc;					// $90A: Uart IRDA Non-Integer Prescaler

	{ 0 },		// Byte											___filler28[0xA00-0x90C];

	0x00000000,	// DWord	lcdStartAddr;					// $A00: Screen Starting Address Register
	{ 0 },		// Byte											___filler29;
	0xFF,		// Byte		lcdPageWidth;					// $A05: Virtual Page Width Register
	{ 0 },		// Byte											___filler30[2];
	0x03FF,		// Word		lcdScreenWidth;					// $A08: Screen Width Register
	0x01FF,		// Word		lcdScreenHeight;				// $A0A: Screen Height Register
	{ 0 },		// Byte											___filler31[0xA18-0xA0C];
	0x0000,		// Word		lcdCursorXPos;					// $A18: Cursor X Position
	0x0000,		// Word		lcdCursorYPos;					// $A1A:	Cursor Y Position
	0x0101,		// Word		lcdCursorWidthHeight;			// $A1C: Cursor Width and Height
	{ 0 },		// Byte											___filler32;
	0x7F,		// Byte		lcdBlinkControl;				// $A1F: Blink Control Register
	0x00,		// Byte		lcdPanelControl;				// $A20: Panel Interface Control Register
	0x00,		// Byte		lcdPolarity;					// $A21: Polarity Config Register
	{ 0 },		// Byte											___filler33;						
	0x00,		// Byte		lcdACDRate;						// $A23: ACD (M) Rate Control Register
	{ 0 },		// Byte											___filler34;
	0x00,		// Byte		lcdPixelClock;					// $A25: Pixel Clock Divider Register
	{ 0 },		// Byte											___filler35;
	0x40,		// Byte		lcdClockControl;				// $A27: Clocking Control Register
	{ 0 },		// Byte											___filler36;
	0xFF,		// Byte		lcdRefreshRateAdj;				// $A29: Refresh Rate Adjustment Register
	{ 0 },		// Byte											___filler2003[0xA2D-0xA2A];
	0x00,		// Byte		lcdPanningOffset;				// $A2D: Panning Offset Register

	{ 0 },		// Byte											___filler37[0xA31-0xA2E];

	0xB9,		// Byte		lcdFrameRate;					// $A31: Frame Rate Control Modulation Register
	{ 0 },		// Byte											___filler2004;
	0x84,		// Byte		lcdGrayPalette;					// $A33: Gray Palette Mapping Register
	0x00,		// Byte		lcdReserved;					// $A34: Reserved
	{ 0 },		// Byte											___filler2005;
	0x0000,		// Word		lcdContrastControlPWM;			// $A36: Contrast Control

	{ 0 },		// Byte											___filler40[0xB00-0xA38];

	0x00000000,	// DWord	rtcHourMinSec;					// $B00: RTC Hours, Minutes, Seconds Register
	0x00000000,	// DWord	rtcAlarm;						// $B04: RTC Alarm Register
	{ 0 },		// Byte											___filler2001[0xB0A-0xB08];
	0x0001,		// Word		rtcWatchDog;					// $B0A: RTC Watchdog Timer
	0x00,		// Word		rtcControl;						// $B0C: RTC Control Register
	0x00,		// Word		rtcIntStatus;					// $B0E: RTC Interrupt Status Register
	0x00,		// Word		rtcIntEnable;					// $B10: RTC Interrupt Enable Register
	0x00,		// Word		stopWatch;						// $B12: Stopwatch Minutes
	{ 0 },		// Byte											___filler2002[0xB1A-0xB14];
	0x0000,		// Word		rtcDay;							// $B1A: RTC Day
	0x0000,		// Word		rtcDayAlarm;					// $B1C: RTC Day Alarm

	{ 0 },		// Byte											___filler41[0xC00-0xB1E];

	0x0000,		// Word		dramConfig;						// $C00: DRAM Memory Config Register
	0x0000,		// Word		dramControl;					// $C02: DRAM Control Register

	{ 0 },		// Byte											___filler42[0xD00-0xC04];

	0x00000000,	// DWord	emuAddrCompare;					// $D00: Emulation Address Compare Register   
	0x00000000,	// DWord	emuAddrMask;					// $D04: Emulation Address Mask Register
	0x0000,		// Word		emuControlCompare;				// $D08: Emulation Control Compare Register
	0x0000,		// Word		emuControlMask;					// $D0A: Emulation Control Mask Register
	0x0000,		// Word		emuControl;						// $DOC: Emulation Control Register
	0x0000		// Word		emuStatus;						// $D0E: Emulation Status Register
};


#pragma mark -


// ===========================================================================
//		¥ Register Bank Accessors
// ===========================================================================
// These functions provide fetch and store access to the emulator's register
// memory.

static AddressBank gAddressBank =
{
	MC68EZ328Bank::GetLong,
	MC68EZ328Bank::GetWord,
	MC68EZ328Bank::GetByte,
	MC68EZ328Bank::SetLong,
	MC68EZ328Bank::SetWord,
	MC68EZ328Bank::SetByte,
	MC68EZ328Bank::GetRealAddress,
	MC68EZ328Bank::ValidAddress,
	NULL,
	NULL
};

static const uae_u32	kMemoryStart = hwr68328Base;

static HwrDBallEZType	g68EZ328Regs;

static bool			gHotSyncButtonDown;		// Use "bool" instead of "Bool" so that
											// we save a consistant type to the
											// session file.
static uae_u16			gKeyBits;
static uae_u16			gLastTmr1Status;
static uae_u8			gPortDEdge;

// These functions provide fetch and store access to the
// emulator's register memory.

static ReadFunction		gReadFunctions[sizeof(HwrDBallEZType)];
static WriteFunction	gWriteFunctions[sizeof(HwrDBallEZType)];

/***********************************************************************
 *
 * FUNCTION:	MC68EZ328Bank::Initialize
 *
 * DESCRIPTION:	Standard initialization function.  Responsible for
 *				initializing this sub-system when a new session is
 *				created.  May also be called from the Load function
 *				to share common functionality.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void MC68EZ328Bank::Initialize (void)
{
	// Memory bank 0xFFFF is managed by the functions in MC68EZ328Bank.

	Memory::InitializeBanks (gAddressBank, bankindex (kMemoryStart), 1);

	// Install the handlers for each register.

	MC68EZ328Bank::InstallHandlers ();
}


/***********************************************************************
 *
 * FUNCTION:	MC68EZ328Bank::Reset
 *
 * DESCRIPTION:	Standard reset function.  Sets the sub-system to a
 *				default state.  This occurs not only on a Reset (as
 *				from the menu item), but also when the sub-system
 *				is first initialized (Reset is called after Initialize)
 *				as well as when the system is re-loaded from an
 *				insufficient session file.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void MC68EZ328Bank::Reset (void)
{
	g68EZ328Regs = kInitial68EZ328RegisterValues;

	// Byteswap all the words in the DragonballEZ registers (if necessary).
	
	Canonical (g68EZ328Regs);
	ByteswapWords (&g68EZ328Regs, sizeof(g68EZ328Regs));

	gHotSyncButtonDown = false;
	gKeyBits = 0;

	gLastTmr1Status = 0;
	gPortDEdge = 0;

	Platform::CloseSerialPort ();
}


/***********************************************************************
 *
 * FUNCTION:	MC68EZ328Bank::Save
 *
 * DESCRIPTION:	Standard save function.  Saves any sub-system state to
 *				the given session file.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void MC68EZ328Bank::Save (SessionFile& f)
{
	StWordSwapper				swapper1 (&g68EZ328Regs, sizeof(g68EZ328Regs));
	StCanonical<HwrDBallEZType>	swapper2 (g68EZ328Regs);
	f.WriteHwrDBallEZType (g68EZ328Regs);

	const long	kCurrentVersion = 1;

	Chunk		chunk;
	ChunkStream	s (chunk);

	s << kCurrentVersion;
	s << gHotSyncButtonDown;
	s << gKeyBits;
	s << gLastTmr1Status;
	s << gPortDEdge;

	f.WriteDBallEZState (chunk);
}


/***********************************************************************
 *
 * FUNCTION:	MC68EZ328Bank::Load
 *
 * DESCRIPTION:	Standard load function.  Loads any sub-system state
 *				from the given session file.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void MC68EZ328Bank::Load (SessionFile& f)
{
	if (f.ReadHwrDBallEZType (g68EZ328Regs))
	{
		Canonical (g68EZ328Regs);
		ByteswapWords (&g68EZ328Regs, sizeof(g68EZ328Regs));

		// Re-open the serial port if needed.

		Bool	isOn = (READ_REGISTER (portFData) & hwrEZPortF232Enable) != 0;

		if (isOn)
		{
			Platform::OpenSerialPort ();
		}

		// Resync the UART sub-system.

		Bool	sendTxData = false;
		MC68EZ328Bank::UARTStateChanged (sendTxData);

		// Reset gMemAccessFlags.fProtect_SRAMSet

		gMemAccessFlags.fProtect_SRAMSet = (READ_REGISTER (csDSelect) & 0x2000) != 0;
	}
	else
	{
		f.SetCanReload (false);
	}

	Chunk		chunk;
	if (f.ReadDBallEZState (chunk))
	{
		long		version;
		ChunkStream	s (chunk);

		s >> version;

		if (version >= 1)
		{
			s >> gHotSyncButtonDown;
			s >> gKeyBits;
			s >> gLastTmr1Status;
			s >> gPortDEdge;
		}
	}
	else
	{
		f.SetCanReload (false);
	}
}


/***********************************************************************
 *
 * FUNCTION:	MC68EZ328Bank::Dispose
 *
 * DESCRIPTION:	Standard dispose function.  Completely release any
 *				resources acquired or allocated in Initialize and/or
 *				Load.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void MC68EZ328Bank::Dispose (void)
{
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::InstallHandlers
// ---------------------------------------------------------------------------

void MC68EZ328Bank::InstallHandlers (void)
{
	// The DragonballEZ register bank is further sub-divided into separate handlers
	// for each memory address.  First, install the default handlers for reading
	// and writing (which basically report the access as invalid).

	MC68EZ328Reg::SetHandler (MC68EZ328Reg::UnsupportedRead, MC68EZ328Reg::UnsupportedWrite,
						kMemoryStart, sizeof (HwrDBallEZType));

	// Now add standard/specialized handers for the defined registers.

#define INSTALL_HANDLER(read, write, reg) \
	MC68EZ328Reg::SetHandler (MC68EZ328Reg::read, MC68EZ328Reg::write, \
							addressof (reg), sizeof (g68EZ328Regs.reg))

	INSTALL_HANDLER (StdRead,			StdWrite,				scr);
	INSTALL_HANDLER (StdRead,			StdWrite,				chipID);
	INSTALL_HANDLER (StdRead,			StdWrite,				maskID);
	INSTALL_HANDLER (StdRead,			StdWrite,				swID);
	INSTALL_HANDLER (StdRead,			StdWrite,				csAGroupBase);
	INSTALL_HANDLER (StdRead,			StdWrite,				csBGroupBase);
	INSTALL_HANDLER (StdRead,			StdWrite,				csCGroupBase);
	INSTALL_HANDLER (StdRead,			StdWrite,				csDGroupBase);
	INSTALL_HANDLER (StdRead,			StdWrite,				csASelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				csBSelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				csCSelect);
	INSTALL_HANDLER (StdRead,			csDSelectWrite,			csDSelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				emuCS);
	INSTALL_HANDLER (StdRead,			StdWrite,				pllControl);
	INSTALL_HANDLER (pllFreqSelRead,	StdWrite,				pllFreqSel);
	INSTALL_HANDLER (StdRead,			StdWrite,				pllTest);
	INSTALL_HANDLER (StdRead,			StdWrite,				pwrControl);
	INSTALL_HANDLER (StdRead,			StdWrite,				intVector);
	INSTALL_HANDLER (StdRead,			StdWrite,				intControl);
	INSTALL_HANDLER (StdRead,			intMaskHiWrite,			intMaskHi);
	INSTALL_HANDLER (StdRead,			intMaskLoWrite,			intMaskLo);
	INSTALL_HANDLER (StdRead,			intStatusHiWrite,		intStatusHi);
	INSTALL_HANDLER (StdRead,			NullWrite,				intStatusLo);
	INSTALL_HANDLER (StdRead,			NullWrite,				intPendingHi);
	INSTALL_HANDLER (StdRead,			NullWrite,				intPendingLo);
	INSTALL_HANDLER (StdRead,			StdWrite,				portADir);
	INSTALL_HANDLER (StdRead,			StdWrite,				portAData);
	INSTALL_HANDLER (StdRead,			StdWrite,				portAPullupEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portBDir);
	INSTALL_HANDLER (StdRead,			StdWrite,				portBData);
	INSTALL_HANDLER (StdRead,			StdWrite,				portBPullupEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portBSelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				portCDir);
	INSTALL_HANDLER (StdRead,			portCDataWrite,			portCData);
	INSTALL_HANDLER (StdRead,			StdWrite,				portCPulldnEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portCSelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				portDDir);
	INSTALL_HANDLER (portDDataRead,		portDDataWrite,			portDData);
	INSTALL_HANDLER (StdRead,			StdWrite,				portDPullupEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portDSelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				portDPolarity);
	INSTALL_HANDLER (StdRead,			portDIntReqEnWrite,		portDIntReqEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portDKbdIntEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portDIntEdge);
	INSTALL_HANDLER (StdRead,			StdWrite,				portEDir);
	INSTALL_HANDLER (portEDataRead,		StdWrite,				portEData);
	INSTALL_HANDLER (StdRead,			StdWrite,				portEPullupEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portESelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				portFDir);
	INSTALL_HANDLER (StdRead,			portFDataWrite,			portFData);
	INSTALL_HANDLER (StdRead,			StdWrite,				portFPullupdnEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portFSelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				portGDir);
	INSTALL_HANDLER (StdRead,			StdWrite,				portGData);
	INSTALL_HANDLER (StdRead,			StdWrite,				portGPullupEn);
	INSTALL_HANDLER (StdRead,			StdWrite,				portGSelect);
	INSTALL_HANDLER (StdRead,			StdWrite,				pwmControl);
	INSTALL_HANDLER (StdRead,			StdWrite,				pwmSampleHi);
	INSTALL_HANDLER (StdRead,			StdWrite,				pwmSampleLo);
	INSTALL_HANDLER (StdRead,			StdWrite,				pwmPeriod);
	INSTALL_HANDLER (StdRead,			NullWrite,				pwmCounter);
	INSTALL_HANDLER (StdRead,			StdWrite,				tmr1Control);
	INSTALL_HANDLER (StdRead,			StdWrite,				tmr1Prescaler);
	INSTALL_HANDLER (StdRead,			StdWrite,				tmr1Compare);
	INSTALL_HANDLER (StdRead,			StdWrite,				tmr1Capture);
	INSTALL_HANDLER (StdRead,			NullWrite,				tmr1Counter);
	INSTALL_HANDLER (tmr1StatusRead,	tmr1StatusWrite,		tmr1Status);
	INSTALL_HANDLER (StdRead,			StdWrite,				spiMasterData);
	INSTALL_HANDLER (StdRead,			spiMasterControlWrite,	spiMasterControl);
	INSTALL_HANDLER (UartRead,			UartWrite,				uControl);
	INSTALL_HANDLER (UartRead,			UartWrite,				uBaud);
	INSTALL_HANDLER (UartRead,			UartWrite,				uReceive);
	INSTALL_HANDLER (UartRead,			UartWrite,				uTransmit);
	INSTALL_HANDLER (UartRead,			UartWrite,				uMisc);
	INSTALL_HANDLER (UartRead,			UartWrite,				uNonIntPresc);
	INSTALL_HANDLER (StdRead,			lcdRegisterWrite,		lcdStartAddr);
	INSTALL_HANDLER (StdRead,			lcdRegisterWrite,		lcdPageWidth);
	INSTALL_HANDLER (StdRead,			lcdRegisterWrite,		lcdScreenWidth);
	INSTALL_HANDLER (StdRead,			lcdRegisterWrite,		lcdScreenHeight);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdCursorXPos);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdCursorYPos);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdCursorWidthHeight);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdBlinkControl);
	INSTALL_HANDLER (StdRead,			lcdRegisterWrite,		lcdPanelControl);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdPolarity);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdACDRate);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdPixelClock);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdClockControl);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdRefreshRateAdj);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdPanningOffset);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdFrameRate);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdGrayPalette);
	INSTALL_HANDLER (StdRead,			StdWrite,				lcdContrastControlPWM);
	INSTALL_HANDLER (rtcHourMinSecRead,	StdWrite,				rtcHourMinSec);
	INSTALL_HANDLER (StdRead,			StdWrite,				rtcAlarm);
	INSTALL_HANDLER (StdRead,			StdWrite,				rtcWatchDog);
	INSTALL_HANDLER (StdRead,			rtcControlWrite,		rtcControl);
	INSTALL_HANDLER (StdRead,			rtcIntStatusWrite,		rtcIntStatus);
	INSTALL_HANDLER (StdRead,			rtcIntEnableWrite,		rtcIntEnable);
	INSTALL_HANDLER (StdRead,			StdWrite,				stopWatch);
	INSTALL_HANDLER (StdRead,			StdWrite,				rtcDay);
	INSTALL_HANDLER (StdRead,			StdWrite,				rtcDayAlarm);
	INSTALL_HANDLER (StdRead,			StdWrite,				dramConfig);
	INSTALL_HANDLER (StdRead,			StdWrite,				dramControl);
	INSTALL_HANDLER (StdRead,			StdWrite,				emuAddrCompare);
	INSTALL_HANDLER (StdRead,			StdWrite,				emuAddrMask);
	INSTALL_HANDLER (StdRead,			StdWrite,				emuControlCompare);
	INSTALL_HANDLER (StdRead,			StdWrite,				emuControlMask);
	INSTALL_HANDLER (StdRead,			StdWrite,				emuControl);
	INSTALL_HANDLER (StdRead,			StdWrite,				emuStatus);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetLong
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Bank::GetLong (uaecptr iAddress)
{
#if PROFILE_MEMORY
	gMemoryAccess[kREGLongRead]++;
	if (iAddress & 2)
		gMemoryAccess[kREGLongRead2]++;
#endif

#if (CHECK_FOR_ADDRESS_ERROR)
	if ((iAddress & 1) != 0)
	{
		Software::AddressError ();
	}
#endif

#if (PREVENT_USER_REGISTER_GET)
	if (gMemAccessFlags.fProtect_RegisterGet && ROMBank::IsPCInRAM ())
	{
		PreventedAccess (iAddress, 4, true);
	}
#endif

#if (VALIDATE_REGISTER_GET)
	if (gMemAccessFlags.fValidate_RegisterGet && !ValidAddress (iAddress, 4))
	{
		InvalidAccess (iAddress, 4, true);
	}
#endif

#if HAS_PROFILING
	CYCLE_GETLONG (WAITSTATES_REGISTERS);
#endif

	uae_u32			offset = iAddress - kMemoryStart;
	ReadFunction	fn = MC68EZ328Reg::UnsupportedRead;

	if (offset < sizeof (HwrDBallEZType))
	{
		fn = gReadFunctions[offset];
	}

	assert (fn);
	return fn (iAddress, 4);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetWord
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Bank::GetWord (uaecptr iAddress)
{
#if PROFILE_MEMORY
	gMemoryAccess[kREGWordRead]++;
#endif

#if (CHECK_FOR_ADDRESS_ERROR)
	if ((iAddress & 1) != 0)
	{
		Software::AddressError ();
	}
#endif

#if (PREVENT_USER_REGISTER_GET)
	if (gMemAccessFlags.fProtect_RegisterGet && ROMBank::IsPCInRAM ())
	{
		PreventedAccess (iAddress, 2, true);
	}
#endif

#if (VALIDATE_REGISTER_GET)
	if (gMemAccessFlags.fValidate_RegisterGet && !ValidAddress (iAddress, 2))
	{
		InvalidAccess (iAddress, 2, true);
	}
#endif

#if HAS_PROFILING
	CYCLE_GETWORD (WAITSTATES_REGISTERS);
#endif

	uae_u32			offset = iAddress - kMemoryStart;
	ReadFunction	fn = MC68EZ328Reg::UnsupportedRead;

	if (offset < sizeof (HwrDBallEZType))
	{
		fn = gReadFunctions[offset];
	}

	assert (fn);
	return fn (iAddress, 2);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetByte
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Bank::GetByte (uaecptr iAddress)
{
#if PROFILE_MEMORY
	gMemoryAccess[kREGByteRead]++;
#endif

#if (PREVENT_USER_REGISTER_GET)
	if (gMemAccessFlags.fProtect_RegisterGet && ROMBank::IsPCInRAM ())
	{
		PreventedAccess (iAddress, 1, true);
	}
#endif

#if (VALIDATE_REGISTER_GET)
	if (gMemAccessFlags.fValidate_RegisterGet && !ValidAddress (iAddress, 1))
	{
		InvalidAccess (iAddress, 1, true);
	}
#endif

#if HAS_PROFILING
	CYCLE_GETBYTE (WAITSTATES_REGISTERS);
#endif

	uae_u32			offset = iAddress - kMemoryStart;
	ReadFunction	fn = MC68EZ328Reg::UnsupportedRead;

	if (offset < sizeof (HwrDBallEZType))
	{
		fn = gReadFunctions[offset];
	}

	assert (fn);
	return fn (iAddress, 1);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::SetLong
// ---------------------------------------------------------------------------

void MC68EZ328Bank::SetLong (uaecptr iAddress, uae_u32 iLongValue)
{
#if PROFILE_MEMORY
	gMemoryAccess[kREGLongWrite]++;
	if (iAddress & 2)
		gMemoryAccess[kREGLongWrite2]++;
#endif

#if (CHECK_FOR_ADDRESS_ERROR)
	if ((iAddress & 1) != 0)
	{
		Software::AddressError ();
	}
#endif

#if (PREVENT_USER_REGISTER_SET)
	if (gMemAccessFlags.fProtect_RegisterSet && ROMBank::IsPCInRAM ())
	{
		PreventedAccess (iAddress, 4, false);
	}
#endif

#if (VALIDATE_REGISTER_SET)
	if (gMemAccessFlags.fValidate_RegisterSet && !ValidAddress (iAddress, 4))
	{
		InvalidAccess (iAddress, 4, false);
	}
#endif

#if HAS_PROFILING
	CYCLE_PUTLONG (WAITSTATES_REGISTERS);
#endif

	uae_u32			offset = iAddress - kMemoryStart;
	WriteFunction	fn = MC68EZ328Reg::UnsupportedWrite;

	if (offset < sizeof (HwrDBallEZType))
	{
		fn = gWriteFunctions[offset];
	}

	assert (fn);
	fn (iAddress, 4, iLongValue);

	// See if any interesting memory locations have changed.  If so,
	// CheckStepSpy will change gBreakReason, which we'll check later.

	Debug::CheckStepSpy (iAddress, 4);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::SetWord
// ---------------------------------------------------------------------------

void MC68EZ328Bank::SetWord (uaecptr iAddress, uae_u32 iWordValue)
{
#if PROFILE_MEMORY
	gMemoryAccess[kREGWordWrite]++;
#endif

#if (CHECK_FOR_ADDRESS_ERROR)
	if ((iAddress & 1) != 0)
	{
		Software::AddressError ();
	}
#endif

#if (PREVENT_USER_REGISTER_SET)
	if (gMemAccessFlags.fProtect_RegisterSet && ROMBank::IsPCInRAM ())
	{
		PreventedAccess (iAddress, 2, false);
	}
#endif

#if (VALIDATE_REGISTER_SET)
	if (gMemAccessFlags.fValidate_RegisterSet && !ValidAddress (iAddress, 2))
	{
		InvalidAccess (iAddress, 2, false);
	}
#endif

#if HAS_PROFILING
	CYCLE_PUTWORD (WAITSTATES_REGISTERS);
#endif

	uae_u32			offset = iAddress - kMemoryStart;
	WriteFunction	fn = MC68EZ328Reg::UnsupportedWrite;

	if (offset < sizeof (HwrDBallEZType))
	{
		fn = gWriteFunctions[offset];
	}

	assert (fn);
	fn (iAddress, 2, iWordValue);

	// See if any interesting memory locations have changed.  If so,
	// CheckStepSpy will change gBreakReason, which we'll check later.

	Debug::CheckStepSpy (iAddress, 2);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::SetByte
// ---------------------------------------------------------------------------

void MC68EZ328Bank::SetByte (uaecptr iAddress, uae_u32 iByteValue)
{
#if PROFILE_MEMORY
	gMemoryAccess[kREGByteWrite]++;
#endif

#if (PREVENT_USER_REGISTER_SET)
	if (gMemAccessFlags.fProtect_RegisterSet && ROMBank::IsPCInRAM ())
	{
		PreventedAccess (iAddress, 1, false);
	}
#endif

#if (VALIDATE_REGISTER_SET)
	if (gMemAccessFlags.fValidate_RegisterSet && !ValidAddress (iAddress, 1))
	{
		InvalidAccess (iAddress, 1, false);
	}
#endif

#if HAS_PROFILING
	CYCLE_PUTBYTE (WAITSTATES_REGISTERS);
#endif

	uae_u32			offset = iAddress - kMemoryStart;
	WriteFunction	fn = MC68EZ328Reg::UnsupportedWrite;

	if (offset < sizeof (HwrDBallEZType))
	{
		fn = gWriteFunctions[offset];
	}

	assert (fn);
	fn (iAddress, 1, iByteValue);

	// See if any interesting memory locations have changed.  If so,
	// CheckStepSpy will change gBreakReason, which we'll check later.

	Debug::CheckStepSpy (iAddress, 1);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::ValidAddress
// ---------------------------------------------------------------------------

int MC68EZ328Bank::ValidAddress (uaecptr iAddress, uae_u32 iSize)
{
	UNUSED_PARAM(iSize)

	uae_u32			offset = iAddress - kMemoryStart;
	WriteFunction	fn = MC68EZ328Reg::UnsupportedWrite;

	if (offset < sizeof (HwrDBallEZType))
	{
		fn = gWriteFunctions[offset];
	}	

	int result = (fn != MC68EZ328Reg::UnsupportedWrite);

	assert (result);

	return result;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetRealAddress
// ---------------------------------------------------------------------------

uae_u8* MC68EZ328Bank::GetRealAddress (uaecptr iAddress)
{
	uae_u8*	loc = ((uae_u8*) &g68EZ328Regs) + (iAddress - kMemoryStart);

	return loc;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::InvalidAccess
// ---------------------------------------------------------------------------

void MC68EZ328Bank::InvalidAccess (uaecptr iAddress, long size, Bool forRead)
{
	UNUSED_PARAM(iAddress)
	UNUSED_PARAM(size)
	UNUSED_PARAM(forRead)

	Software::BusError ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::PreventedAccess
// ---------------------------------------------------------------------------

void MC68EZ328Bank::PreventedAccess (uaecptr iAddress, long size, Bool forRead)
{
	Memory::PreventedAccess (iAddress, size, forRead, Errors::kRegisterAccess);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::Cycle
// ---------------------------------------------------------------------------
// Handles periodic events that need to occur when the processor cycles (like
// updating timer registers).  This function is called in two places from
// Emulator::Execute.  Interestingly, the loop runs 3% FASTER if this function
// is in its own separate function instead of being inline.

void MC68EZ328Bank::Cycle (Bool sleeping)
{
	// Determine whether timer is enabled.

	if ((READ_REGISTER (tmr1Control) & hwrEZ328TmrControlEnable) != 0)
	{
		// If so, increment the timer.

		WRITE_REGISTER (tmr1Counter, READ_REGISTER (tmr1Counter) + 1);

		// Determine whether the timer has reached the specified count.

		if (sleeping || READ_REGISTER (tmr1Counter) > READ_REGISTER (tmr1Compare))
		{
			// Flag the occurrence of the successful comparison.

			WRITE_REGISTER (tmr1Status, READ_REGISTER (tmr1Status) | hwrEZ328TmrStatusCompare);

			// If the Free Run/Restart flag is not set, clear the counter.

			if ((READ_REGISTER (tmr1Control) & hwrEZ328TmrControlFreeRun) == 0)
			{
				WRITE_REGISTER (tmr1Counter, 0);
			}

			// If the timer interrupt is enabled, post an interrupt.

			if ((READ_REGISTER (tmr1Control) & hwrEZ328TmrControlEnInterrupt) != 0)
			{
				WRITE_REGISTER (intPendingLo, READ_REGISTER (intPendingLo) | hwrEZ328IntLoTimer);
				MC68EZ328Bank::UpdateInterrupts ();
			}
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::CycleSlowly
// ---------------------------------------------------------------------------
// Handles periodic events that need to occur when the processor cycles (like
// updating timer registers).  This function is called in two places from
// Emulator::Execute.  Interestingly, the loop runs 3% FASTER if this function
// is in its own separate function instead of being inline.

void MC68EZ328Bank::CycleSlowly (Bool sleeping)
{
	UNUSED_PARAM(sleeping)

	// Check to see if the RTC alarm is ready to go off.  First see
	// if the RTC is enabled, and that the alarm event isn't already
	// registered (the latter check is just an optimization).

	if ((READ_REGISTER (rtcIntEnable) & hwrEZ328RTCIntEnableAlarm) != 0 &&
		(READ_REGISTER (rtcIntStatus) & hwrEZ328RTCIntStatusAlarm) == 0)
	{
		uae_u32	rtcAlarm = READ_REGISTER (rtcAlarm);

		long	almHour	 = (rtcAlarm & hwrEZ328RTCAlarmHoursMask) >> hwrEZ328RTCAlarmHoursOffset;
		long	almMin	 = (rtcAlarm & hwrEZ328RTCAlarmMinutesMask) >> hwrEZ328RTCAlarmMinutesOffset;
		long	almSec	 = (rtcAlarm & hwrEZ328RTCAlarmSecondsMask) >> hwrEZ328RTCAlarmSecondsOffset;
		long	almInSeconds = (almHour * 60 * 60) + (almMin * 60) + almSec;

		long	nowHour;
		long	nowMin;
		long	nowSec;
		::GetHostTime (&nowHour, &nowMin, &nowSec);
		long	nowInSeconds = (nowHour * 60 * 60) + (nowMin * 60) + nowSec;

		if (almInSeconds <= nowInSeconds)
		{
			WRITE_REGISTER (rtcIntStatus, READ_REGISTER (rtcIntStatus) | hwrEZ328RTCIntStatusAlarm);
			MC68EZ328Bank::UpdateRTCInterrupts ();
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetInterruptLevel
// ---------------------------------------------------------------------------

uae_s32 MC68EZ328Bank::GetInterruptLevel (void)
{
	uae_u16	intStatusHi = READ_REGISTER (intStatusHi);
	uae_u16	intStatusLo = READ_REGISTER (intStatusLo);

	// Level 7 = EMUIRQ.

	if ((intStatusHi & hwrEZ328IntHiEMU) != 0)
		return 7;

	// Level 6 = IRQ6, TMR, PWM.

	if ((intStatusHi & (hwrEZ328IntHiIRQ6)) != 0)
		return 6;

	if ((intStatusLo & (hwrEZ328IntLoTimer | hwrEZ328IntLoPWM)) != 0)
		return 6;

	// Level 5 = PEN.

	if ((intStatusHi & hwrEZ328IntHiPen) != 0)
		return 5;

	// Level 4 = SPIM, UART, WDT, RTC, RTC Sample, KB, INT0 - INT3.

	if ((intStatusLo & (	hwrEZ328IntLoSPIM |
							hwrEZ328IntLoUART |
							hwrEZ328IntLoWDT |
							hwrEZ328IntLoRTC |
							hwrEZ328IntLoKbd |
							hwrEZ328IntLoInt3 |
							hwrEZ328IntLoInt2 |
							hwrEZ328IntLoInt1 |
							hwrEZ328IntLoInt0)) != 0)
		return 4;

	if ((intStatusHi & hwrEZ328IntHiSampleTimer) != 0)
		return 4;

	// Level 3 = IRQ3.

	if ((intStatusHi & hwrEZ328IntHiIRQ3) != 0)
		return 3;

	// Level 2 = IRQ2.

	if ((intStatusHi & hwrEZ328IntHiIRQ2) != 0)
		return 2;

	// Level 1 = IRQ1.

	if ((intStatusHi & hwrEZ328IntHiIRQ1) != 0)
		return 1;

	// Level 0.

	return -1;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetInterruptBase
// ---------------------------------------------------------------------------

uae_s32 MC68EZ328Bank::GetInterruptBase (void)
{
	return READ_REGISTER (intVector) & 0xF8;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::ButtonEvent
// ---------------------------------------------------------------------------
// Handles a Palm device button event by updating the appropriate registers.

void MC68EZ328Bank::ButtonEvent (	Bool	iButton_IsDown,
									uae_s32	iButton)
{
	uae_u8	previousPortDData = READ_REGISTER (portDData);
	uae_u8	portDData = previousPortDData;

	// On the Dragonball, portD contained 8 bits, each of which corresponded
	// to a key (see keyBitFoo values in KeyMgr.h).  On the DragonballEZ,
	// 4 bits are allocated to keys.  Each bit corresponds to a "column" of
	// keys.  The "app" keys are in column 0, page up/down in column 1, and the
	// power and contrast keys in column 2.  Column 3 appears to be unused.  When
	// a key is pressed, we set its "column bit".

	int	whichCol;

	iButton = 1 << iButton;
	
	if (iButton == keyBitHard1 ||
		iButton == keyBitHard2 ||
		iButton == keyBitHard3 ||
		iButton == keyBitHard4)
	{
		whichCol = hwrEZPortDKbdCol0;
	}
	else if (iButton == keyBitPageUp || iButton == keyBitPageDown)
	{
		whichCol = hwrEZPortDKbdCol1;
	}
	else if (iButton == keyBitPower || ((iButton == keyBitContrast) && (Emulator::GetHardwareDevice () == kDevicePalmV)))
	{
		whichCol = hwrEZPortDKbdCol2;
	}
	else
	{
		return;
	}

	if (iButton_IsDown)
	{
		portDData |= whichCol;	// Set the column bit
		gKeyBits |= iButton;	// Remember the key bit
	}
	else
	{
		portDData &= ~whichCol;	// Clear the column bit
		gKeyBits &= ~iButton;	// Forget the key bit
	}

	WRITE_REGISTER (portDData, portDData);

	// Set the interrupt bits for the bits that went from off to on.

	gPortDEdge |= portDData & ~previousPortDData;

	// Set the new interrupt state.

	MC68EZ328Bank::UpdatePortDInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::HotSyncEvent
// ---------------------------------------------------------------------------
// Handles a HotSync button event by updating the appropriate registers.

void MC68EZ328Bank::HotSyncEvent (Bool iButton_IsDown)
{
	// If the button changes state, set or clear the HotSync interrupt.

	uae_u16	intPendingHi = READ_REGISTER (intPendingHi);

	if (iButton_IsDown)
	{
		intPendingHi |= hwrEZ328IntHiIRQ1;
		gHotSyncButtonDown = true;
	}
	else
	{
		intPendingHi &= ~hwrEZ328IntHiIRQ1;
		gHotSyncButtonDown = false;
	}

	WRITE_REGISTER (intPendingHi, intPendingHi);

	MC68EZ328Bank::UpdateInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetLCDDepth
// ---------------------------------------------------------------------------

int MC68EZ328Bank::GetLCDDepth (void)
{

//	Bool	grayscale = (READ_REGISTER (lcdPanelControl) & hwrEZ328LcdPanelControlGrayScale) != 0;
	uae_u8	lcdPanelControl = READ_REGISTER (lcdPanelControl) & 0x03;

	return 1 << (lcdPanelControl & 0x03);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetLCDRowBytes
// ---------------------------------------------------------------------------

int MC68EZ328Bank::GetLCDRowBytes (void)
{

	int width = READ_REGISTER (lcdPageWidth) * 2;

	return width;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetLCDWidth
// ---------------------------------------------------------------------------

int MC68EZ328Bank::GetLCDWidth (void)
{

	// NB: Dragonball difference here. On the Dragonball, we have to
	// add one. On DragonballEZ, we don't.

	int width = READ_REGISTER (lcdScreenWidth);

	return width;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetLCDHeight
// ---------------------------------------------------------------------------

int MC68EZ328Bank::GetLCDHeight (void)
{

	int height = READ_REGISTER (lcdScreenHeight) + 1;

	return height;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetLCDStartAddr
// ---------------------------------------------------------------------------

uaecptr MC68EZ328Bank::GetLCDStartAddr (void)
{

	uaecptr	addr = READ_REGISTER (lcdStartAddr);

	return addr;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::GetLCDOffset
// ---------------------------------------------------------------------------

int MC68EZ328Bank::GetLCDOffset (void)
{

	int	offset = READ_REGISTER (lcdPanningOffset) & 0x0F;

	return offset;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::LCDIsOn
// ---------------------------------------------------------------------------

Bool MC68EZ328Bank::LCDIsOn (void)
{

	Bool	on = (READ_REGISTER (portCData) & hwrEZPortCLCDEnableOn) != 0;

	return on;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::BacklightIsOn
// ---------------------------------------------------------------------------

Bool MC68EZ328Bank::BacklightIsOn (void)
{

	Bool	on = (READ_REGISTER (portFData) & hwrEZPortFBacklightOn) != 0;

	return on;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::TurnSoundOff
// ---------------------------------------------------------------------------

void MC68EZ328Bank::TurnSoundOff (void)
{
	uae_u16	pwmControl = READ_REGISTER (pwmControl);
	WRITE_REGISTER (pwmControl, pwmControl & ~hwrEZ328PWMControlEnable);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::CanStop
// ---------------------------------------------------------------------------

Bool MC68EZ328Bank::CanStop (void)
{
	// Make sure Timer is enabled or the RTC interrupt is enabled.

	if ((READ_REGISTER (tmr1Control) & hwrEZ328TmrControlEnable) != 0)
		return true;

	if ((READ_REGISTER (rtcIntEnable) & hwrEZ328RTCIntEnableAlarm) != 0)
		return true;

	return false;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::UpdateInterrupts
// ---------------------------------------------------------------------------
// Determines whether an interrupt has occurred by copying the Interrupt
// Pending Register to the Interrupt Status Register.

void MC68EZ328Bank::UpdateInterrupts (void)
{
	// Copy the Interrupt Pending Register to the Interrupt Status
	// Register, but ignore interrupts that are being masked.

	// Note: this function is not sensitive to the byte ordering of the registers,
	// so their contents don't need to be accessed via READ_REGISTER or WRITE_REGISTER.

	g68EZ328Regs.intStatusHi = g68EZ328Regs.intPendingHi & ~g68EZ328Regs.intMaskHi;
	g68EZ328Regs.intStatusLo = g68EZ328Regs.intPendingLo & ~g68EZ328Regs.intMaskLo;

	// If the Interrupt Status Register isn't clear, flag an interrupt.

	if (g68EZ328Regs.intStatusHi || g68EZ328Regs.intStatusLo)
	{
		regs.spcflags |= SPCFLAG_INT;
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::UpdatePortDInterrupts
// ---------------------------------------------------------------------------
// Determine what interrupts need to be generated based on the current
// settings in portDData and gPortDEdge.

void MC68EZ328Bank::UpdatePortDInterrupts (void)
{
	// Update INT0-INT7 of the Interrupt-Pending register (bits 8-15 of the low word).

	// First, get those bits and clear them out.

	uae_u32	intPendingLo	= READ_REGISTER (intPendingLo) & 0x00FF;

	// Determine the new interrupt settings. An interrupt is signalled if:
	//
	//	- its gPortDEdge bit is set along with its corresponding portDIntEdge bit, or
	//	- its portDData bit is set but its corresponding portDIntEdge bit is not, and
	//	- that interrupt is enabled in portDIntReqen
	//
	// (Technically, it seems to me that we should also be looking at portDPolarity when
	//  examining edge interrupts to signal falling or rising edges.  However, those bits
	//  are always set by the current Palm OS ROMs (making them active low or falling edge),
	//  so we can work assuming that for now.  It also seems to me that we should be
	//  looking at portDDir so that we signal interrupts only on input bits, and not
	//  output bits (as happens with the up/down keys when we go to sleep).)

	uae_u8	portDIntEdge	= READ_REGISTER (portDIntEdge);
	uae_u8	portDData		= READ_REGISTER (portDData);
	uae_u8	portDIntReqEn	= READ_REGISTER (portDIntReqEn);
	uae_u8	newBits			= (gPortDEdge & portDIntEdge | portDData & ~portDIntEdge) & portDIntReqEn;

	// Merge in the new values and write out the result.

	intPendingLo |=	newBits << 8;
	WRITE_REGISTER (intPendingLo, intPendingLo);

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::UpdateRTCInterrupts
// ---------------------------------------------------------------------------
// Determine whether to set or clear the RTC bit in the interrupt pending
// register based on the current RTC register values.

void MC68EZ328Bank::UpdateRTCInterrupts (void)
{
	// See if the RTC is enabled.

	Bool	rtcEnabled = (READ_REGISTER (rtcControl) & hwrEZ328RTCControlRTCEnable) != 0;

	// See if there are any RTC events that need to trigger an interrupt.

#define BITS_TO_CHECK				( \
	hwrEZ328RTCIntEnableSec			| \
	hwrEZ328RTCIntEnable24Hr		| \
	hwrEZ328RTCIntEnableAlarm		| \
	hwrEZ328RTCIntEnableMinute		| \
	hwrEZ328RTCIntEnableStopWatch	)

	uae_u16	rtcIntStatus = READ_REGISTER (rtcIntStatus);
	uae_u16	rtcIntEnable = READ_REGISTER (rtcIntEnable);
	uae_u16	rtcIntPending = rtcIntStatus & rtcIntEnable & BITS_TO_CHECK;

	Bool	havePendingEvents = rtcIntPending != 0;

	// If the RTC is enabled and there are pending events, set the interrupt.
	// Otherwise, clear the interrupt.

	uae_u16	intPendingLo = READ_REGISTER (intPendingLo);

	if (rtcEnabled && havePendingEvents)
	{
		intPendingLo |= hwrEZ328IntLoRTC;	// have events, so set interrupt
	}
	else
	{	intPendingLo &= ~hwrEZ328IntLoRTC;	// no events, so clear interrupt
	}

	// Update the interrupt pending register.

	WRITE_REGISTER (intPendingLo, intPendingLo);

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::UARTStateChanged
// ---------------------------------------------------------------------------

void MC68EZ328Bank::UARTStateChanged (Bool sendTxData)
{
	UART::State	state (UART::kUART_DragonballEZ);

	MarshalUARTState (state);
	UART::StateChanged (state, sendTxData);
	UnmarshalUARTState (state);

	MC68EZ328Bank::UpdateUARTInterrupts (state);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::UpdateUARTState
// ---------------------------------------------------------------------------

void MC68EZ328Bank::UpdateUARTState (Bool refreshRxData)
{
//	if (UART::GetStateNeedsUpdating ())
	{
		UART::State	state (UART::kUART_DragonballEZ);

		MarshalUARTState (state);
		UART::UpdateState (state, refreshRxData);
		UnmarshalUARTState (state);

		MC68EZ328Bank::UpdateUARTInterrupts (state);
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::UpdateUARTInterrupts
// ---------------------------------------------------------------------------

void MC68EZ328Bank::UpdateUARTInterrupts (const UART::State& state)
{
	// Generate the appropriate interrupts.  Don't generate interrupts for
	// TX_FIFO_EMPTY and TX_FIFO_HALF; from the manual's overview: "The transmitter
	// will not generate another interrupt until the FIFO has completely emptied."
	//
	// Actually, this code is not quite right.  If TX_AVAIL_ENABLE is false but one
	// of the other TX_xxx_ENABLEs is true, then we want to generate an interrupt
	// for one of those.  With the test below, that will never happen.  For now,
	// this is OK, as the Palm OS doesn't enable any of the TX interrupts.

	if (state.RX_FULL_ENABLE	&& state.RX_FIFO_FULL	||
		state.RX_HALF_ENABLE	&& state.RX_FIFO_HALF	||
		state.RX_RDY_ENABLE		&& state.DATA_READY		||
	//	state.TX_EMPTY_ENABLE	&& state.TX_FIFO_EMPTY	||
	//	state.TX_HALF_ENABLE	&& state.TX_FIFO_HALF	||
		state.TX_AVAIL_ENABLE	&& state.TX_AVAIL)
	{
		// Set the UART interrupt.

		WRITE_REGISTER (intPendingLo, READ_REGISTER (intPendingLo) | hwrEZ328IntLoUART);
	}
	else
	{
		// Clear the UART interrupt.

		WRITE_REGISTER (intPendingLo, READ_REGISTER (intPendingLo) & ~hwrEZ328IntLoUART);
	}

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::MarshalUARTState
// ---------------------------------------------------------------------------

void MC68EZ328Bank::MarshalUARTState (UART::State& state)
{
	uae_u16	uControl	= READ_REGISTER (uControl);
	uae_u16	uBaud		= READ_REGISTER (uBaud);
	uae_u16	uReceive	= READ_REGISTER (uReceive);
	uae_u16	uTransmit	= READ_REGISTER (uTransmit);
	uae_u16	uMisc		= READ_REGISTER (uMisc);

	state.UART_ENABLE		= (uControl & hwrEZ328UControlUARTEnable) != 0;
	state.RX_ENABLE			= (uControl & hwrEZ328UControlRxEnable) != 0;
	state.TX_ENABLE			= (uControl & hwrEZ328UControlTxEnable) != 0;
	state.RX_CLK_CONT		= (uControl & hwrEZ328UControlRxClock1xSync) != 0;
	state.PARITY_EN			= (uControl & hwrEZ328UControlParityEn) != 0;
	state.ODD_EVEN			= (uControl & hwrEZ328UControlParityOdd) != 0;
	state.STOP_BITS			= (uControl & hwrEZ328UControlStopBits2) != 0;
	state.CHAR8_7			= (uControl & hwrEZ328UControlDataBits8) != 0;
//	state.GPIO_DELTA_ENABLE	= (uControl & hwr328UControlGPIODeltaEn) != 0;	// 68328 only
	state.OLD_ENABLE		= (uControl & hwrEZ328UControlOldDataEn) != 0;	// 68EZ328 only
	state.CTS_DELTA_ENABLE	= (uControl & hwrEZ328UControlCTSDeltaEn) != 0;
	state.RX_FULL_ENABLE	= (uControl & hwrEZ328UControlRxFullEn) != 0;
	state.RX_HALF_ENABLE	= (uControl & hwrEZ328UControlRxHalfEn) != 0;
	state.RX_RDY_ENABLE		= (uControl & hwrEZ328UControlRxRdyEn) != 0;
	state.TX_EMPTY_ENABLE	= (uControl & hwrEZ328UControlTxEmptyEn) != 0;
	state.TX_HALF_ENABLE	= (uControl & hwrEZ328UControlTxHalfEn) != 0;
	state.TX_AVAIL_ENABLE	= (uControl & hwrEZ328UControlTxAvailEn) != 0;

	// Baud control register bits
	// These are all values the user sets; we just look at them.

//	state.GPIO_DELTA		= (uBaud & hwr328UBaudGPIODelta) != 0;			// 68328 only
//	state.GPIO				= (uBaud & hwr328UBaudGPIOData) != 0;			// 68328 only
//	state.GPIO_DIR			= (uBaud & hwr328UBaudGPIODirOut) != 0;			// 68328 only
//	state.GPIO_SRC			= (uBaud & hwrEZ328UBaudGPIOSrcBaudGen) != 0;	// 68328 only
	state.UCLK_DIR			= (uBaud & hwrEZ328UBaudUCLKDirOut) != 0;		// 68EZ328 only
	state.BAUD_SRC			= (uBaud & hwrEZ328UBaudBaudSrcUCLK) != 0;
	state.DIVIDE			= (uBaud & hwrEZ328UBaudDivider) >> hwrEZ328UBaudDivideBitOffset;
	state.PRESCALER			= (uBaud & hwrEZ328UBaudPrescaler);

	// Receive register bits
	// These are all input bits; we set them, not the user.

	state.RX_FIFO_FULL		= (uReceive & hwrEZ328UReceiveFIFOFull) != 0;
	state.RX_FIFO_HALF		= (uReceive & hwrEZ328UReceiveFIFOHalf) != 0;
	state.DATA_READY		= (uReceive & hwrEZ328UReceiveDataRdy) != 0;
	state.OLD_DATA			= (uReceive & hwrEZ328UReceiveOldData) != 0;	// 68EZ328 only
	state.OVRUN				= (uReceive & hwrEZ328UReceiveOverrunErr) != 0;
	state.FRAME_ERROR		= (uReceive & hwrEZ328UReceiveFrameErr) != 0;
	state.BREAK				= (uReceive & hwrEZ328UReceiveBreakErr) != 0;
	state.PARITY_ERROR		= (uReceive & hwrEZ328UReceiveParityErr) != 0;
	state.RX_DATA			= (uReceive & hwrEZ328UReceiveData);

	// Transmitter register bits
	// We set everything except TX_DATA; the user sets that
	// value and ONLY that value.

	state.TX_FIFO_EMPTY		= (uTransmit & hwrEZ328UTransmitFIFOEmpty) != 0;
	state.TX_FIFO_HALF		= (uTransmit & hwrEZ328UTransmitFIFOHalf) != 0;
	state.TX_AVAIL			= (uTransmit & hwrEZ328UTransmitTxAvail) != 0;
	state.SEND_BREAK		= (uTransmit & hwrEZ328UTransmitSendBreak) != 0;
	state.IGNORE_CTS		= (uTransmit & hwrEZ328UTransmitIgnoreCTS) != 0;
	state.BUSY				= (uTransmit & hwrEZ328UTransmitBusy) != 0;		// 68EZ328 only
	state.CTS_STATUS		= (uTransmit & hwrEZ328UTransmitCTSStatus) != 0;
	state.CTS_DELTA			= (uTransmit & hwrEZ328UTransmitCTSDelta) != 0;
	state.TX_DATA			= (uTransmit & hwrEZ328UTransmitData);

	// Misc register bits
	// These are all values the user sets; we just look at them.

	state.BAUD_TEST			= (uMisc & hwrEZ328UMiscBaudTest) != 0;			// 68EZ328 only
	state.CLK_SRC			= (uMisc & hwrEZ328UMiscClkSrcUCLK) != 0;
	state.FORCE_PERR		= (uMisc & hwrEZ328UMiscForceParityErr) != 0;
	state.LOOP				= (uMisc & hwrEZ328UMiscLoopback) != 0;
	state.BAUD_RESET		= (uMisc & hwrEZ328UMiscBaudReset) != 0;		// 68EZ328 only
	state.IR_TEST			= (uMisc & hwrEZ328UMiscIRTestEn) != 0;			// 68EZ328 only
	state.RTS_CONT			= (uMisc & hwrEZ328UMiscRTSThruFIFO) != 0;
	state.RTS				= (uMisc & hwrEZ328UMiscRTSOut) != 0;
	state.IRDA_ENABLE		= (uMisc & hwrEZ328UMiscIRDAEn) != 0;
	state.IRDA_LOOP			= (uMisc & hwrEZ328UMiscLoopIRDA) != 0;
	state.RX_POL			= (uMisc & hwrEZ328UMiscRXPolarityInv) != 0;	// 68EZ328 only
	state.TX_POL			= (uMisc & hwrEZ328UMiscTXPolarityInv) != 0;	// 68EZ328 only
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Bank::UnmarshalUARTState
// ---------------------------------------------------------------------------

void MC68EZ328Bank::UnmarshalUARTState (const UART::State& state)
{
	uae_u16	uControl	= 0;
	uae_u16	uBaud		= 0;
	uae_u16	uReceive	= 0;
	uae_u16	uTransmit	= 0;
	uae_u16	uMisc		= 0;

	if (state.UART_ENABLE)		uControl |= hwrEZ328UControlUARTEnable;
	if (state.RX_ENABLE)		uControl |= hwrEZ328UControlRxEnable;
	if (state.TX_ENABLE)		uControl |= hwrEZ328UControlTxEnable;
	if (state.RX_CLK_CONT)		uControl |= hwrEZ328UControlRxClock1xSync;
	if (state.PARITY_EN)		uControl |= hwrEZ328UControlParityEn;
	if (state.ODD_EVEN)			uControl |= hwrEZ328UControlParityOdd;
	if (state.STOP_BITS)		uControl |= hwrEZ328UControlStopBits2;
	if (state.CHAR8_7)			uControl |= hwrEZ328UControlDataBits8;
//	if (state.GPIO_DELTA_ENABLE)uControl |= hwr328UControlGPIODeltaEn;	// 68328 only
	if (state.OLD_ENABLE)		uControl |= hwrEZ328UControlOldDataEn;	// 68EZ328 only
	if (state.CTS_DELTA_ENABLE)	uControl |= hwrEZ328UControlCTSDeltaEn;
	if (state.RX_FULL_ENABLE)	uControl |= hwrEZ328UControlRxFullEn;
	if (state.RX_HALF_ENABLE)	uControl |= hwrEZ328UControlRxHalfEn;
	if (state.RX_RDY_ENABLE)	uControl |= hwrEZ328UControlRxRdyEn;
	if (state.TX_EMPTY_ENABLE)	uControl |= hwrEZ328UControlTxEmptyEn;
	if (state.TX_HALF_ENABLE)	uControl |= hwrEZ328UControlTxHalfEn;
	if (state.TX_AVAIL_ENABLE)	uControl |= hwrEZ328UControlTxAvailEn;

	// Baud control register bits
	// These are all values the user sets; we just look at them.

//	if (state.GPIO_DELTA)		uBaud |= hwr328UBaudGPIODelta;		// 68328 only
//	if (state.GPIO)				uBaud |= hwr328UBaudGPIOData;		// 68328 only
//	if (state.GPIO_DIR)			uBaud |= hwr328UBaudGPIODirOut;		// 68328 only
//	if (state.GPIO_SRC)			uBaud |= hwr328UBaudGPIOSrcBaudGen;	// 68328 only
	if (state.UCLK_DIR)			uBaud |= hwrEZ328UBaudUCLKDirOut;	// 68EZ328 only
	if (state.BAUD_SRC)			uBaud |= hwrEZ328UBaudBaudSrcUCLK;

	uBaud |= (state.DIVIDE << hwrEZ328UBaudDivideBitOffset) & hwrEZ328UBaudDivider;
	uBaud |= (state.PRESCALER) & hwrEZ328UBaudPrescaler;

	// Receive register bits
	// These are all input bits; we set them, not the user.

	if (state.RX_FIFO_FULL)		uReceive |= hwrEZ328UReceiveFIFOFull;
	if (state.RX_FIFO_HALF)		uReceive |= hwrEZ328UReceiveFIFOHalf;
	if (state.DATA_READY)		uReceive |= hwrEZ328UReceiveDataRdy;
	if (state.OLD_DATA)			uReceive |= hwrEZ328UReceiveOldData;	// 68EZ328 only
	if (state.OVRUN)			uReceive |= hwrEZ328UReceiveOverrunErr;
	if (state.FRAME_ERROR)		uReceive |= hwrEZ328UReceiveFrameErr;
	if (state.BREAK)			uReceive |= hwrEZ328UReceiveBreakErr;
	if (state.PARITY_ERROR)		uReceive |= hwrEZ328UReceiveParityErr;

	uReceive |= (state.RX_DATA) & hwrEZ328UReceiveData;

	// Transmitter register bits
	// We set everything except TX_DATA; the user sets that
	// value and ONLY that value.

	if (state.TX_FIFO_EMPTY)	uTransmit |= hwrEZ328UTransmitFIFOEmpty;
	if (state.TX_FIFO_HALF)		uTransmit |= hwrEZ328UTransmitFIFOHalf;
	if (state.TX_AVAIL)			uTransmit |= hwrEZ328UTransmitTxAvail;
	if (state.SEND_BREAK)		uTransmit |= hwrEZ328UTransmitSendBreak;
	if (state.IGNORE_CTS)		uTransmit |= hwrEZ328UTransmitIgnoreCTS;
	if (state.BUSY)				uTransmit |= hwrEZ328UTransmitBusy;		// 68EZ328 only
	if (state.CTS_STATUS)		uTransmit |= hwrEZ328UTransmitCTSStatus;
	if (state.CTS_DELTA)		uTransmit |= hwrEZ328UTransmitCTSDelta;

	uTransmit |= (state.TX_DATA) & hwrEZ328UTransmitData;

	// Misc register bits
	// These are all values the user sets; we just look at them.

	if (state.BAUD_TEST)		uMisc |= hwrEZ328UMiscBaudTest;			// 68EZ328 only
	if (state.CLK_SRC)			uMisc |= hwrEZ328UMiscClkSrcUCLK;
	if (state.FORCE_PERR)		uMisc |= hwrEZ328UMiscForceParityErr;
	if (state.LOOP)				uMisc |= hwrEZ328UMiscLoopback;
	if (state.BAUD_RESET)		uMisc |= hwrEZ328UMiscBaudReset;		// 68EZ328 only
	if (state.IR_TEST)			uMisc |= hwrEZ328UMiscIRTestEn;			// 68EZ328 only
	if (state.RTS_CONT)			uMisc |= hwrEZ328UMiscRTSThruFIFO;
	if (state.RTS)				uMisc |= hwrEZ328UMiscRTSOut;
	if (state.IRDA_ENABLE)		uMisc |= hwrEZ328UMiscIRDAEn;
	if (state.IRDA_LOOP)		uMisc |= hwrEZ328UMiscLoopIRDA;
	if (state.RX_POL)			uMisc |= hwrEZ328UMiscRXPolarityInv;	// 68EZ328 only
	if (state.TX_POL)			uMisc |= hwrEZ328UMiscTXPolarityInv;	// 68EZ328 only

	WRITE_REGISTER (uControl, uControl);
	WRITE_REGISTER (uBaud, uBaud);
	WRITE_REGISTER (uReceive, uReceive);
	WRITE_REGISTER (uTransmit, uTransmit);
	WRITE_REGISTER (uMisc, uMisc);
}


#pragma mark -


// ===========================================================================
//		¥ Register Accessors
// ===========================================================================

// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::SetHandler
// ---------------------------------------------------------------------------

void MC68EZ328Reg::SetHandler (ReadFunction read, WriteFunction write,
							uae_u32 start, int count)
{
	int	index = start - kMemoryStart;
	for (int ii = 0; ii < count; ++ii, ++index)
	{
		gReadFunctions[index] = read;
		gWriteFunctions[index] = write;
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::UnsupportedRead
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Reg::UnsupportedRead (uaecptr address, int size)
{
	if (!CEnableFullAccess::AccessOK ())
	{
		MC68EZ328Bank::InvalidAccess (address, size, true);
	}

	return ~0;
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::UnsupportedWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::UnsupportedWrite (uaecptr address, int size, uae_u32 value)
{
	UNUSED_PARAM(value)

	if (!CEnableFullAccess::AccessOK ())
	{
		MC68EZ328Bank::InvalidAccess (address, size, false);
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::StdRead
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Reg::StdRead (uaecptr address, int size)
{
	// The order of these tests is based on the frequency at which
	// the registers are accessed.

	if (size == 1)
		return do_get_mem_byte (MC68EZ328Bank::GetRealAddress (address));

	if (size == 2)
		return do_get_mem_word (MC68EZ328Bank::GetRealAddress (address));

	return do_get_mem_long (MC68EZ328Bank::GetRealAddress (address));
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::StdWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::StdWrite (uaecptr address, int size, uae_u32 value)
{
	// The order of these tests is based on the frequency at which
	// the registers are accessed.

	if (size == 2)
		do_put_mem_word (MC68EZ328Bank::GetRealAddress (address), value);

	else if (size == 4)
		do_put_mem_long (MC68EZ328Bank::GetRealAddress (address), value);

	else
		do_put_mem_byte (MC68EZ328Bank::GetRealAddress (address), value);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::NullWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::NullWrite (uaecptr address, int size, uae_u32 value)
{
	UNUSED_PARAM(address)
	UNUSED_PARAM(size)
	UNUSED_PARAM(value)

	// Do nothing (for read-only registers).
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::pllFreqSelRead
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Reg::pllFreqSelRead (uaecptr address, int size)
{
	// Simulate the rising and falling of the CLK32 signal so that functions like
	// HwrPreRAMInit, HwrShutDownPLL, PrvSetPLL, and PrvShutDownPLL won't hang.

	uae_u16	pllFreqSel = READ_REGISTER (pllFreqSel) ^ 0x8000;
	WRITE_REGISTER (pllFreqSel, pllFreqSel);

	// Finish up by doing a standard read.

	return MC68EZ328Reg::StdRead (address, size);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::portDDataRead
// ---------------------------------------------------------------------------
//	Used for:
//		Reading keys (KeySleep, KeyHandleInterrupt, prvKeyScan, KeyInit)
//		Serial enable/disable (hwrEZPortD232Enable, only on rev 1 HW)
//		Dock button (HwrDockSignals)
//		Hardware ID (HwrIdentifyFeatures)

uae_u32 MC68EZ328Reg::portDDataRead (uaecptr address, int size)
{
	// See if they're looking for the hardware ID.

			uae_u8	portGDir		= READ_REGISTER (portGDir);
			uae_u8	portGData		= READ_REGISTER (portGData);
			uae_u8	portGPullupEn	= READ_REGISTER (portGPullupEn);
			uae_u8	portGSelect		= READ_REGISTER (portGSelect);
	const	uae_u8	kMask			= hwrEZPortGIDDetect;

	Bool	lookingForID =	(portGSelect & kMask) == kMask &&
							(portGDir & kMask) == kMask &&
							(portGData & kMask) == 0 &&
							(portGPullupEn & kMask) == 0;


	if (lookingForID)
	{
		uae_u8	idBits = 0;

		// Determine the hardware ID.

		switch (Emulator::GetHardwareDevice ())
		{
			case kDevicePalmIIIx:
				idBits = hwrEZPortDKbdCol0 | hwrEZPortDKbdCol2 | hwrEZPortDKbdCol3;
				break;

			case kDevicePalmV:
				idBits = hwrEZPortDKbdCol0 | hwrEZPortDKbdCol1 | hwrEZPortDKbdCol3;
				break;


			case kDevicePilot1000:
			case kDevicePilot5000:
			case kDevicePalmPilotPersonal:
			case kDevicePalmPilotProfessional:
			case kDevicePalmPilotWithUpgrade:
			case kDevicePalmIII:
			case kDevicePalmVII:
				Platform::Debugger ();
				break;

			default: assert (false);
		}

		// Return its complement.

		return ~idBits;
	}

	// Ensure that bit hwrEZPortDDockIn is set.  If it's clear, HotSync
	// will sync via the modem instead of the serial port.
	//
	// Also make sure that hwrEZPortDPowerFail is set.  If it's clear,
	// the battery code will make the device go to sleep immediately.

	uae_u8	portDData = READ_REGISTER (portDData) | hwrEZPortDDockButton | hwrEZPortDPowerFail;
	WRITE_REGISTER (portDData, portDData);

	// Finish up by doing a standard read.

	return MC68EZ328Reg::StdRead (address, size);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::portEDataRead
// ---------------------------------------------------------------------------
//	Used for:
//		SPIM I/O
//		BurrBrown I/O
//		Hardware sub-ID

uae_u32 MC68EZ328Reg::portEDataRead (uaecptr address, int size)
{
	// See if they're looking for the hardware sub-ID.

			uae_u8	portEDir		= READ_REGISTER(portEDir);
			uae_u8	portEPullupEn	= READ_REGISTER(portEPullupEn);
			uae_u8	portESelect		= READ_REGISTER(portESelect);
	const	uae_u8	kMask			= hwrEZPortESpiClk | hwrEZPortESpiTxD | hwrEZPortESpiRxD;

	if ((portEDir & kMask) == 0 &&
		(portEPullupEn & kMask) == kMask &&
		(portESelect & kMask) == kMask)
	{
		return ~1 & kMask;	// Return Sub-ID value 1.
	}

	// Otherwise, finish up by doing a standard read.

	return MC68EZ328Reg::StdRead (address, size);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::tmr1StatusRead
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Reg::tmr1StatusRead (uaecptr address, int size)
{
	uae_u16	tmr1Counter = READ_REGISTER (tmr1Counter) + 16;
	uae_u16	tmr1Compare = READ_REGISTER (tmr1Compare);
	uae_u16	tmr1Control = READ_REGISTER (tmr1Control);

	// Increment the timer.

	WRITE_REGISTER (tmr1Counter, tmr1Counter);

	// If the timer has passed the specified value...

	if ((tmr1Counter - tmr1Compare) < 16)
	{
		// Set the flag saying the timer timed out.

		uae_u16	tmr1Status = READ_REGISTER (tmr1Status) | hwrEZ328TmrStatusCompare;
		WRITE_REGISTER (tmr1Status, tmr1Status);

		// If it's not a free-running timer, reset it to zero.

		if ((tmr1Control & hwrEZ328TmrControlFreeRun) == 0)
		{
			WRITE_REGISTER (tmr1Counter, 0);
		}
	}

	gLastTmr1Status |= READ_REGISTER (tmr1Status);	// remember this guy for later (see MC68EZ328Reg::tmr1StatusWrite())

	// Finish up by doing a standard read.

	return MC68EZ328Reg::StdRead (address, size);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::UartRead
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Reg::UartRead (uaecptr address, int size)
{
	// If this is a full read, get the next byte from the FIFO.

	Bool	refreshRxData = (address == addressof (uReceive)) && (size == 2);

	// See if there's anything new ("Put the data on the bus")

	MC68EZ328Bank::UpdateUARTState (refreshRxData);

	// Finish up by doing a standard read.

	return MC68EZ328Reg::StdRead (address, size);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::rtcHourMinSecRead
// ---------------------------------------------------------------------------

uae_u32 MC68EZ328Reg::rtcHourMinSecRead (uaecptr address, int size)
{
	// Get the desktop machine's time.

	long	hour, min, sec;
	::GetHostTime (&hour, &min, &sec);

	// Update the register.

	WRITE_REGISTER (rtcHourMinSec, (hour << hwrEZ328RTCHourMinSecHoursOffset)
								| (min << hwrEZ328RTCHourMinSecMinutesOffset)
								| (sec << hwrEZ328RTCHourMinSecSecondsOffset));

	// Finish up by doing a standard read.

	return MC68EZ328Reg::StdRead (address, size);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::csDSelectWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::csDSelectWrite (uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// Check its new state and update our ram-protect flag.

	gMemAccessFlags.fProtect_SRAMSet = (READ_REGISTER (csDSelect) & 0x2000) != 0;
}



// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::intMaskHiWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::intMaskHiWrite (uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::intMaskLoWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::intMaskLoWrite (uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::intStatusHiWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::intStatusHiWrite (uaecptr address, int size, uae_u32 value)
{
	// IRQ1, IRQ2, IRQ3, IRQ6 and IRQ7 are cleared by writing to their
	// respective status bits.  We handle those there. Since there are
	// no interrupt status bits like this in intStatusLo, we don't need
	// a handler for that register; we only handle intStatusHi.

	// Even though this is a 16-bit register as defined by the Palm headers,
	// it's a 32-bit register according to DragonballEZ docs, and is in fact
	// accessed that way in the kernal files (cf. HwrIRQ4Handler). In those
	// cases, we're still only interested in access to the IRQ# bits, so we
	// can turn 4-byte accesses into 2-byte accesses.

	if (size == 4)
		value >>= 16;

	// Take into account the possibility of 1-byte accesses, too. If we're
	// accessing the upper byte, just return. If we're accessing the lower
	// byte, we can treat it as a 2-byte access.

	else if (size == 1 && address == addressof (intStatusHi))
		return;

	// Now we can treat the rest of this function as a word-write to intStatusHi.

	uae_u16	intPendingHi = READ_REGISTER (intPendingHi);

	//	For each interrupt:
	//		If we're writing to that interrupt's status bit and its edge bit is set:
	//			- clear the interrupt's pending bit
	//			- respond to the new interrupt state.

	#undef CLEAR_PENDING_INTERRUPT
	#define CLEAR_PENDING_INTERRUPT(edge, irq)							\
		if ( (READ_REGISTER (intControl) & edge) && (value & (irq)) )	\
		{																\
			intPendingHi &= ~(irq);										\
		}

	CLEAR_PENDING_INTERRUPT (hwrEZ328IntCtlEdge1, hwrEZ328IntHiIRQ1);
	CLEAR_PENDING_INTERRUPT (hwrEZ328IntCtlEdge2, hwrEZ328IntHiIRQ2);
	CLEAR_PENDING_INTERRUPT (hwrEZ328IntCtlEdge3, hwrEZ328IntHiIRQ3);
	CLEAR_PENDING_INTERRUPT (hwrEZ328IntCtlEdge6, hwrEZ328IntHiIRQ6);

	// IRQ7 is not edge-programmable, so clear it if we're merely writing to it.
	// !!! Double check this for EZ!

	if (value & hwrEZ328IntHiEMU)
	{
		intPendingHi &= ~(hwrEZ328IntHiEMU);
	}

	// If we're emulating the user pressing the hotsync button, make sure the
	// interrupt stays asserted.  (!!! Should we use the same technique for
	// other buttons, too?  It doesn't seem to be needed right now, but doing
	// that may more closely mirror the hardware.)

	if (gHotSyncButtonDown)
	{
		intPendingHi |= hwrEZ328IntHiIRQ1;
	}
	else
	{
		intPendingHi &= ~hwrEZ328IntHiIRQ1;
	}

	// This makes the power on key work. If the signal is asserted, the
	// unit will not transition between asleep and awake (cf. HwrSleep, HwrWake).

	intPendingHi &= ~hwrEZ328IntHiIRQ6;

	WRITE_REGISTER (intPendingHi, intPendingHi);
	MC68EZ328Bank::UpdateInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::portCDataWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::portCDataWrite (uaecptr address, int size, uae_u32 value)
{
	// Chain to lcdRegisterWrite first.  It will update the register, and do
	// anything else it needs to.  We let _it_ update the register because
	// it needs to also first fetch the original value of the register for
	// comparison purposes.

	lcdRegisterWrite (address, size, value);

}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::portDDataWrite	// Byte $419: Port D Data Register
// ---------------------------------------------------------------------------

void MC68EZ328Reg::portDDataWrite (uaecptr address, int size, uae_u32 value)
{
	// Get the current value.

	uae_u8	oldPortDData = READ_REGISTER (portDData);

	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// If the serial driver enable bit changed, react to it.

	Bool	wasOn = (oldPortDData & hwrEZPortD232Enable) != 0;
	Bool	isOn = (READ_REGISTER (portDData) & hwrEZPortD232Enable) != 0;

	if (isOn && !wasOn)
	{
		Platform::OpenSerialPort ();
		
		Bool	sendTxData = false;
		MC68EZ328Bank::UARTStateChanged (sendTxData);
	}
	else if (!isOn && wasOn)
	{
		Platform::CloseSerialPort ();
	}

	// Clear the interrupt bits that are having a 1 written to them.

	gPortDEdge &= ~value;

	// Set the new interrupt state.

	MC68EZ328Bank::UpdatePortDInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::portDIntReqEnWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::portDIntReqEnWrite (uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// Set the new interrupt state.

	MC68EZ328Bank::UpdatePortDInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::portFDataWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::portFDataWrite (uaecptr address, int size, uae_u32 value)
{
	// Get the current value.

	uae_u8	oldPortFData = READ_REGISTER (portFData);

	// Chain to lcdRegisterWrite first.  It will update the register, and do
	// anything else it needs to.  We let _it_ update the register because
	// it needs to also first fetch the original value of the register for
	// comparison purposes.

	lcdRegisterWrite (address, size, value);

#if 0	// This was moved to Port D
	// If the serial driver enable bit changed, react to it.

	Bool	wasOn = (oldPortFData & hwrEZPortF232Enable) != 0;
	Bool	isOn = (READ_REGISTER (portFData) & hwrEZPortF232Enable) != 0;

	if (isOn && !wasOn)
	{
		Platform::OpenSerialPort ();
		
		Bool	sendTxData = false;
		MC68EZ328Bank::UARTStateChanged (sendTxData);
	}
	else if (!isOn && wasOn)
	{
		Platform::CloseSerialPort ();
	}
#endif

	{
		// Pump in any keys that were asked for.

		uae_u8	portDDir		= READ_REGISTER (portDDir);
		uae_u8	portDPullupEn	= READ_REGISTER (portDPullupEn);
		uae_u8	portFPullupdnEn	= READ_REGISTER (portFPullupdnEn);

		if ((portDDir & hwrEZPortDKeyBits) == 0 &&
			(portDPullupEn & hwrEZPortDKeyBits) == hwrEZPortDKeyBits &&
			(portFPullupdnEn & hwrEZPortFKbdRows) == 0)
		{
			// Get the current contents of portDData so that we can update it.

			uae_u8	portDData = READ_REGISTER (portDData);

			portDData &= ~hwrEZPortDKeyBits;		// Start by clearing out the key bits

			// Fill in the bits for the requested row.

			uae_u8	portFDir	= READ_REGISTER (portFDir);
			uae_u8	portFData	= READ_REGISTER (portFData);

			if ((portFDir & hwrEZPortFKbdRow0) != 0 && (portFData & hwrEZPortFKbdRow0) == 0)
			{
				// Load in the app keys

				if (gKeyBits & keyBitHard1)		portDData |= hwrEZPortDKbdCol0;
				if (gKeyBits & keyBitHard2)		portDData |= hwrEZPortDKbdCol1;
				if (gKeyBits & keyBitHard3)		portDData |= hwrEZPortDKbdCol2;
				if (gKeyBits & keyBitHard4)		portDData |= hwrEZPortDKbdCol3;
			}

			if ((portFDir & hwrEZPortFKbdRow1) != 0 && (portFData & hwrEZPortFKbdRow1) == 0)
			{
				// Load in the page up/down keys

				if (gKeyBits & keyBitPageUp)	portDData |= hwrEZPortDKbdCol0;
				if (gKeyBits & keyBitPageDown)	portDData |= hwrEZPortDKbdCol1;
			}

			if ((portFDir & hwrEZPortFKbdRow2) != 0 && (portFData & hwrEZPortFKbdRow2) == 0)
			{
				// Load in the power & contrast keys

				if (gKeyBits & keyBitPower)		portDData |= hwrEZPortDKbdCol0;
				if (gKeyBits & keyBitContrast)	portDData |= hwrEZPortDKbdCol1;
			}


			WRITE_REGISTER (portDData, portDData);
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::tmr1StatusWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::tmr1StatusWrite (uaecptr address, int size, uae_u32 value)
{
	UNUSED_PARAM(address)
	UNUSED_PARAM(size)

	assert (size == 2);	// This function's a hell of a lot easier to write if
						// we assume only full-register access.

	// Get the current value.

	uae_u16	tmr1Status = READ_REGISTER (tmr1Status);

	// If the user had previously read the status bits while they
	// were set, then it's OK for them to be clear now.  Otherwise,
	// we have to merge any set status bits back in.

	tmr1Status &= value | ~gLastTmr1Status;	// gLastTmr1Status was set in MC68EZ328Reg::tmr1StatusRead()

	WRITE_REGISTER (tmr1Status, tmr1Status);

	gLastTmr1Status = 0;
	if ((tmr1Status & hwrEZ328TmrStatusCompare) == 0)
	{
		uae_u16	intPendingLo = READ_REGISTER (intPendingLo) & ~hwrEZ328IntLoTimer;
		WRITE_REGISTER (intPendingLo, intPendingLo);

		// Respond to the new interrupt state.

		MC68EZ328Bank::UpdateInterrupts ();
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::spiMasterControlWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::spiMasterControlWrite (uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// Get the current value.

	uae_u16	spiMasterControl = READ_REGISTER (spiMasterControl);

	// Check to see if data exchange and interrupts are enabled.

	#define BIT_MASK (hwrEZ328SPIMControlExchange | hwrEZ328SPIMControlIntEnable)
	if ((spiMasterControl & BIT_MASK) != 0)
	{
		// If so, assert the interrupt and clear the exchange bit.

		spiMasterControl |= hwrEZ328SPIMControlIntStatus;
		spiMasterControl &= ~hwrEZ328SPIMControlExchange;

		WRITE_REGISTER (spiMasterControl, spiMasterControl);

/*
		// If we wanted digitizer data, load it into the SPIM data register.

		switch (READ_REGISTER (portFData) & hwrTD1PortFPanelMask)
		{
			case (hwrTD1PortFPanelCfgXMeas):
				WRITE_REGISTER (spiMasterData, (0xFF - Hardware::fgPen_HorzLocation) * 2);
				break;

			case (hwrTD1PortFPanelCfgYMeas):
				WRITE_REGISTER (spiMasterData, (0xFF - Hardware::fgPen_VertLocation) * 2);
				break;
		}
*/
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::UartWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::UartWrite(uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// If this write included the TX_DATA field, signal that it needs to
	// be transmitted.

	Bool	sendTxData =
				((address == addressof (uTransmit)) && (size == 2)) ||
				((address == addressof (uTransmit) + 1) && (size == 1));

	// React to any changes.

	MC68EZ328Bank::UARTStateChanged (sendTxData);
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::lcdRegisterWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::lcdRegisterWrite (uaecptr address, int size, uae_u32 value)
{
	// First, get the old value in case we need to see what changed.

	uae_u32	oldValue = MC68EZ328Reg::StdRead (address, size);

	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);


	// Note what changed.

	if (address == addressof (lcdScreenWidth))
	{
		Screen::InvalidateAll ();
	}
	else if (address == addressof (lcdScreenHeight))
	{
		Screen::InvalidateAll ();
	}
	else if (address == addressof (lcdPanelControl))
	{
//		if (((value ^ oldValue) & hwrEZ328LcdPanelControlGrayScale) != 0)
		if (((value ^ oldValue) & 0x03) != 0)
		{
			Screen::InvalidateAll ();
		}
	}
	else if (address == addressof (lcdStartAddr))
	{
		// Make sure the low-bit is always zero.
		// Make sure bits 31-29 are always zero.

		uae_u32	lcdStartAddr = READ_REGISTER (lcdStartAddr) & 0x1FFFFFFE;
		WRITE_REGISTER (lcdStartAddr, lcdStartAddr);

		Screen::InvalidateAll ();
	}
	else if (address == addressof (lcdPageWidth))
	{
		if (value != oldValue)
		{
			Screen::InvalidateAll ();
		}
	}
	else if (address == addressof (portCData))
	{
		if (((value ^ oldValue) & hwrEZPortCLCDEnableOn) != 0)
		{
			Screen::InvalidateAll ();
		}
	}
	else if (address == addressof (portFData))
	{
		if (((value ^ oldValue) & hwrEZPortFBacklightOn) != 0)
		{
			Screen::InvalidateAll ();
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::rtcControlWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::rtcControlWrite (uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateRTCInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::rtcIntStatusWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::rtcIntStatusWrite (uaecptr address, int size, uae_u32 value)
{
	// Status bits are cleared by writing ones to them.

	// If we're doing a byte-write to the upper byte, shift the byte
	// so that we can treat the operation as a word write.  If we're
	// doing a byte-write to the lower byte, this extension will happen
	// automatically.

	if (address == addressof (rtcIntStatus) && size == 1)
		value <<= 8;

	// Get the current value.

	uae_u16	rtcIntStatus = READ_REGISTER (rtcIntStatus);

	// Clear the requested bits.

	rtcIntStatus &= ~value;

	// Update the register.

	WRITE_REGISTER (rtcIntStatus, rtcIntStatus);

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateRTCInterrupts ();
}


// ---------------------------------------------------------------------------
//		¥ MC68EZ328Reg::rtcIntEnableWrite
// ---------------------------------------------------------------------------

void MC68EZ328Reg::rtcIntEnableWrite (uaecptr address, int size, uae_u32 value)
{
	// Do a standard update of the register.

	MC68EZ328Reg::StdWrite (address, size, value);

	// Respond to the new interrupt state.

	MC68EZ328Bank::UpdateRTCInterrupts ();
}

