/*
	WinSTon

	This is where we intercept read/writes to/from the hardware. The ST's memory is nicely split into
	four main parts - the bottom area of RAM is for user programs. This is followed by a large area which
	causes a Bus Error. After this is the ROM addresses for TOS and finally an area for hardware mapping.
	To gain speed any address in the user area can simply read/write, but anything above this range needs
	to be checked for validity and sent to the various handlers.
	A big problem for ST emulation is the use of the hardware registers. These often consist of an 'odd' byte
	in memory and is usually addressed as a single byte. A number of applications, however, write to the address
	using a word or even long word - which may span over two hardware registers! Rather than check for any and
	all combinations we use a tables for byte/word/long and for read/write. These are lists of functions which
	access the ST Ram area any bytes which maybe affected by the operation. Eg, a long write to a PSG register
	(which access two registers) will write the long into ST Ram and then call the two handlers which read off
	the bytes for each register. This means that any access to any hardware register in such a way will work
	correctly - it certainly fixes a lot of bugs and means writing just one routine for each hardware register
	we mean to intercept! Phew!
	Note the 'mirror'(or shadow) registers of the PSG - this is used by most games. We also have a means of
	testing for addressing into 'no-mans-land' which are parts of the hardware map which are not valid on a
	standard STfm.
*/

#include "..\includes\winston.h"
#include "..\includes\debug.h"
#include "..\includes\decode.h"
#include "..\includes\dialog.h"
#include "..\includes\fdc.h"
#include "..\includes\int.h"
#include "..\includes\intercept.h"
#include "..\includes\ikbd.h"
#include "..\includes\m68000.h"
#include "..\includes\memory.h"
#include "..\includes\mfp.h"
#include "..\includes\psg.h"
#include "..\includes\screen.h"
#include "..\includes\spec512.h"
#include "..\includes\vdi.h"
#include "..\includes\video.h"

//#define CHECK_FOR_NO_MANS_LAND							// Check for read/write from unknown hardware addresses

//-----------------------------------------------------------------------
// List of functions to handle read/write hardware intercepts. MUST match INTERCEPT_xxx enum
INTERCEPT_ACCESS_FUNC InterceptAccessFuncs[INTERCEPT_COUNT] = {
	0x0,SIZE_BYTE,NULL,NULL,
	0xff8205,SIZE_BYTE,Intercept_VideoHigh_ReadByte,Intercept_VideoHigh_WriteByte,				// INTERCEPT_VIDEOHIGH
	0xff8207,SIZE_BYTE,Intercept_VideoMed_ReadByte,Intercept_VideoMed_WriteByte,				// INTERCEPT_VIDEOMED
	0xff8209,SIZE_BYTE,Intercept_VideoLow_ReadByte,Intercept_VideoLow_WriteByte,				// INTERCEPT_VIDEOLOW
	0xff820a,SIZE_BYTE,Intercept_VideoSync_ReadByte,Intercept_VideoSync_WriteByte,				// INTERCEPT_VIDEOSYNC
	0xff820d,SIZE_BYTE,Intercept_VideoBaseLow_ReadByte,Intercept_VideoBaseLow_WriteByte,		// INTERCEPT_VIDEOBASELOW
	0xff820f,SIZE_BYTE,Intercept_LineWidth_ReadByte,Intercept_LineWidth_WriteByte,				// INTERCEPT_LINEWIDTH
	0xff8240,SIZE_WORD,Intercept_Colour0_ReadWord,Intercept_Colour0_WriteWord,					// INTERCEPT_COLOUR0
	0xff8242,SIZE_WORD,Intercept_Colour1_ReadWord,Intercept_Colour1_WriteWord,					// INTERCEPT_COLOUR1
	0xff8244,SIZE_WORD,Intercept_Colour2_ReadWord,Intercept_Colour2_WriteWord,					// INTERCEPT_COLOUR2
	0xff8246,SIZE_WORD,Intercept_Colour3_ReadWord,Intercept_Colour3_WriteWord,					// INTERCEPT_COLOUR3
	0xff8248,SIZE_WORD,Intercept_Colour4_ReadWord,Intercept_Colour4_WriteWord,					// INTERCEPT_COLOUR4
	0xff824a,SIZE_WORD,Intercept_Colour5_ReadWord,Intercept_Colour5_WriteWord,					// INTERCEPT_COLOUR5
	0xff824c,SIZE_WORD,Intercept_Colour6_ReadWord,Intercept_Colour6_WriteWord,					// INTERCEPT_COLOUR6
	0xff824e,SIZE_WORD,Intercept_Colour7_ReadWord,Intercept_Colour7_WriteWord,					// INTERCEPT_COLOUR7
	0xff8250,SIZE_WORD,Intercept_Colour8_ReadWord,Intercept_Colour8_WriteWord,					// INTERCEPT_COLOUR8
	0xff8252,SIZE_WORD,Intercept_Colour9_ReadWord,Intercept_Colour9_WriteWord,					// INTERCEPT_COLOUR9
	0xff8254,SIZE_WORD,Intercept_Colour10_ReadWord,Intercept_Colour10_WriteWord,				// INTERCEPT_COLOUR10
	0xff8256,SIZE_WORD,Intercept_Colour11_ReadWord,Intercept_Colour11_WriteWord,				// INTERCEPT_COLOUR11
	0xff8258,SIZE_WORD,Intercept_Colour12_ReadWord,Intercept_Colour12_WriteWord,				// INTERCEPT_COLOUR12
	0xff825a,SIZE_WORD,Intercept_Colour13_ReadWord,Intercept_Colour13_WriteWord,				// INTERCEPT_COLOUR13
	0xff825c,SIZE_WORD,Intercept_Colour14_ReadWord,Intercept_Colour14_WriteWord,				// INTERCEPT_COLOUR14
	0xff825e,SIZE_WORD,Intercept_Colour15_ReadWord,Intercept_Colour15_WriteWord,				// INTERCEPT_COLOUR15
	0xff8260,SIZE_BYTE,Intercept_ShifterMode_ReadByte,Intercept_ShifterMode_WriteByte,			// INTERCEPT_SHIFTERMODE
	0xff8604,SIZE_WORD,Intercept_DiskControl_ReadWord,Intercept_DiskControl_WriteWord,			// INTERCEPT_DISKCONTROL
	0xff8606,SIZE_WORD,Intercept_DmaStatus_ReadWord,Intercept_DmaStatus_WriteWord,				// INTERCEPT_DMASTATUS
	0xff8800,SIZE_BYTE,Intercept_PSGRegister_ReadByte,Intercept_PSGRegister_WriteByte,			// INTERCEPT_PSG_REGISTER
	0xff8802,SIZE_BYTE,Intercept_PSGData_ReadByte,Intercept_PSGData_WriteByte,					// INTERCEPT_PSG_DATA
	0xff8922,SIZE_WORD,Intercept_MicrowireData_ReadWord,Intercept_MicrowireData_WriteWord,		// INTERCEPT_MICROWIREDATA
	0xfffa01,SIZE_BYTE,Intercept_Monitor_ReadByte,Intercept_Monitor_WriteByte,					// INTERCEPT_MONITOR
	0xfffa03,SIZE_BYTE,Intercept_ActiveEdge_ReadByte,Intercept_ActiveEdge_WriteByte,			// INTERCEPT_ACTIVE_EDGE
	0xfffa05,SIZE_BYTE,Intercept_DataDirection_ReadByte,Intercept_DataDirection_WriteByte,		// INTERCEPT_DATA_DIRECTION
	0xfffa07,SIZE_BYTE,Intercept_EnableA_ReadByte,Intercept_EnableA_WriteByte,					// INTERCEPT_ENABLE_A
	0xfffa09,SIZE_BYTE,Intercept_EnableB_ReadByte,Intercept_EnableB_WriteByte,					// INTERCEPT_ENABLE_B
	0xfffa0b,SIZE_BYTE,Intercept_PendingA_ReadByte,Intercept_PendingA_WriteByte,				// INTERCEPT_PENDING_A
	0xfffa0d,SIZE_BYTE,Intercept_PendingB_ReadByte,Intercept_PendingB_WriteByte,				// INTERCEPT_PENDING_B
	0xfffa0f,SIZE_BYTE,Intercept_InServiceA_ReadByte,Intercept_InServiceA_WriteByte,			// INTERCEPT_INSERVICE_A
	0xfffa11,SIZE_BYTE,Intercept_InServiceB_ReadByte,Intercept_InServiceB_WriteByte,			// INTERCEPT_INSERVICE_B
	0xfffa13,SIZE_BYTE,Intercept_MaskA_ReadByte,Intercept_MaskA_WriteByte,						// INTERCEPT_MASK_A
	0xfffa15,SIZE_BYTE,Intercept_MaskB_ReadByte,Intercept_MaskB_WriteByte,						// INTERCEPT_MASK_B
	0xfffa17,SIZE_BYTE,Intercept_VectorReg_ReadByte,Intercept_VectorReg_WriteByte,				// INTERCEPT_VECTOR_REG
	0xfffa19,SIZE_BYTE,Intercept_TimerACtrl_ReadByte,Intercept_TimerACtrl_WriteByte,			// INTERCEPT_TIMERA_CTRL
	0xfffa1b,SIZE_BYTE,Intercept_TimerBCtrl_ReadByte,Intercept_TimerBCtrl_WriteByte,			// INTERCEPT_TIMERB_CTRL
	0xfffa1d,SIZE_BYTE,Intercept_TimerCDCtrl_ReadByte,Intercept_TimerCDCtrl_WriteByte,			// INTERCEPT_TIMERCD_CTRL
	0xfffa1f,SIZE_BYTE,Intercept_TimerAData_ReadByte,Intercept_TimerAData_WriteByte,			// INTERCEPT_TIMERA_DATA
	0xfffa21,SIZE_BYTE,Intercept_TimerBData_ReadByte,Intercept_TimerBData_WriteByte,			// INTERCEPT_TIMERB_DATA
	0xfffa23,SIZE_BYTE,Intercept_TimerCData_ReadByte,Intercept_TimerCData_WriteByte,			// INTERCEPT_TIMERC_DATA
	0xfffa25,SIZE_BYTE,Intercept_TimerDData_ReadByte,Intercept_TimerDData_WriteByte,			// INTERCEPT_TIMERD_DATA
	0xfffc00,SIZE_BYTE,Intercept_KeyboardControl_ReadByte,Intercept_KeyboardControl_WriteByte,	// INTERCEPT_KEYBOARDCONTROL
	0xfffc02,SIZE_BYTE,Intercept_KeyboardData_ReadByte,Intercept_KeyboardData_WriteByte,		// INTERCEPT_KEYBOARDDATA
	0xfffc04,SIZE_BYTE,Intercept_MidiControl_ReadByte,Intercept_MidiControl_WriteByte,			// INTERCEPT_MIDICONTROL
	0xfffc06,SIZE_BYTE,Intercept_MidiData_ReadByte,Intercept_MidiData_WriteByte,				// INTERCEPT_MIDIDATA
};

unsigned long *pInterceptWorkspace;						// Memory used to store all read/write NULL terminated function call tables
unsigned long *pCurrentInterceptWorkspace;				// Index into above
unsigned long *pInterceptReadByteTable[0x8000],*pInterceptReadWordTable[0x8000],*pInterceptReadLongTable[0x8000];
unsigned long *pInterceptWriteByteTable[0x8000],*pInterceptWriteWordTable[0x8000],*pInterceptWriteLongTable[0x8000];

//-----------------------------------------------------------------------
/*
	Create 'intercept' tables for hardware address access
*/
void Intercept_Init(void)
{
	// Allocate memory for intercept tables
	pCurrentInterceptWorkspace = pInterceptWorkspace = (unsigned long *)Memory_Alloc(INTERCEPT_WORKSPACE_SIZE);

	// Clear intercept tables(NULL signifies no entries for that location)
	Memory_Clear(pInterceptReadByteTable,sizeof(unsigned long *)*0x8000);
	Memory_Clear(pInterceptReadWordTable,sizeof(unsigned long *)*0x8000);
	Memory_Clear(pInterceptReadLongTable,sizeof(unsigned long *)*0x8000);
	Memory_Clear(pInterceptWriteByteTable,sizeof(unsigned long *)*0x8000);
	Memory_Clear(pInterceptWriteWordTable,sizeof(unsigned long *)*0x8000);
	Memory_Clear(pInterceptWriteLongTable,sizeof(unsigned long *)*0x8000);

	// Create 'read' tables
	Intercept_CreateTable(pInterceptReadByteTable,SIZE_BYTE,0);
	Intercept_CreateTable(pInterceptReadWordTable,SIZE_WORD,0);
	Intercept_CreateTable(pInterceptReadLongTable,SIZE_LONG,0);
	// And 'write' tables
	Intercept_CreateTable(pInterceptWriteByteTable,SIZE_BYTE,1);
	Intercept_CreateTable(pInterceptWriteWordTable,SIZE_WORD,1);
	Intercept_CreateTable(pInterceptWriteLongTable,SIZE_LONG,1);

#ifdef CHECK_FOR_NO_MANS_LAND
	// This causes a error when an application tries to access illegal hardware registers(maybe mirror'd)
	Intercept_ModifyTablesForNoMansLand();
#endif	//CHECK_FOR_NO_MANS_LAND

	// And modify for bus-error in hardware space
	Intercept_ModifyTablesForBusErrors();
}

//-----------------------------------------------------------------------
/*
	Free 'intercept' hardware lists
*/
void Intercept_UnInit(void)
{
	Memory_Free(pInterceptWorkspace);
}

//-----------------------------------------------------------------------
/*
	Set Intercept hardware address table index's

	Each 'intercept table' is a list of 0x8000 pointers to a list of functions to call when that
	location in the ST's memory is accessed. Each entry is terminated by a NULL
	Eg, if we write a long word to address '0xff8800', we
	need to call the functions 'InterceptPSGRegister_WriteByte' and then 'InterceptPSGData_WriteByte'.
*/

void Intercept_CreateTable(unsigned long *pInterceptTable[],int Span,int ReadWrite)
{
	unsigned int Address, LowAddress, HiAddress;
	int i;

	// Scan each hardware address
	for(Address=0xff8000; Address<=0xffffff; Address++) {
		// Does this hardware location/span appear in our list of possible intercepted functions?
		for (i=0; i<INTERCEPT_COUNT; i++) {
			LowAddress = InterceptAccessFuncs[i].Address;
			HiAddress = InterceptAccessFuncs[i].Address+InterceptAccessFuncs[i].SpanInBytes;

			if ( (Address+Span) <= LowAddress )
				continue;
			if ( Address >= HiAddress )
				continue;

			// This location needs to be intercepted, so add entry to list
			if (pInterceptTable[Address-0xff8000]==NULL)
				pInterceptTable[Address-0xff8000] = pCurrentInterceptWorkspace;
			*pCurrentInterceptWorkspace++ = (unsigned long)InterceptAccessFuncs[i].ReadWriteFunc[ReadWrite];
		}
		// Terminate table?
		if (pInterceptTable[Address-0xff8000])
			*pCurrentInterceptWorkspace++ = NULL;
	}
}

//-----------------------------------------------------------------------
/*
	Check list of handlers to see if address needs to be intercepted and call routines
	'ecx' is pointer to function list (or NULL)
*/
NAKED void Intercept_ScanHandlers(void)
{
	__asm {
		or		ecx,ecx									// Do we have any routines to run for this address?
		je		no_intercept_table

		push	eax										// Store condition codes
intercept_loop:
		mov		eax,[ecx]								// Read function in pointer
		add		ecx,4
		or		eax,eax									// Is NULL?
		je		completed_intercept_table

		push	ebx
		push	ecx
		call	eax										// Call routine(don't corrupt 'ebx','ecx')
		pop		ecx
		pop		ebx
		jmp		intercept_loop

completed_intercept_table:
		pop		eax

no_intercept_table:
		ret
	}
}

//-----------------------------------------------------------------------
/*
	Check if need to change our address 'ebp' as maybe a mirror register
	Currently we only have a PSG mirror area
*/
NAKED void Intercept_CheckMirrorAddresses(void)
{
	__asm {
		// Is a PSG mirror registers
		cmp		ebp,0xff8800
		jl		not_a_psg
		cmp		ebp,0xff8900
		jge		not_a_psg

		// Yes, it's a mirror
		and		ebp,0x3									// Bring into 0xff8800-0xff8804 range
		add		ebp,0xff8800

not_a_psg:
		ret
	}
}

//-----------------------------------------------------------------------
// NOTE! These MUST not corrupt 'ecx'
extern "C" NAKED void Intercept_ReadByte(void)
{
	__asm {
		// Check if need to intercept address read
		cmp		ebp,0x00ff8000							// Is hardware address?
		jge		intercept_address
		cmp		ebp,[STRamEnd_BusErr]
		jl		null_area
		cmp		ebp,BUS_ERROR_ADDR						// Is a bus error? (invalid memory addressing)
		jl		M68000_BusError

address_ok:
		mov		bl,BYTE PTR STRam[ebp]
		ret
null_area:
		xor		bl,bl									// Blank area between RAM and bus error
		ret
	}

intercept_address:
	__asm {
		mov		[BusAddressLocation],ebp				// Store for exception frame, just in case
		// Run list of 'readbyte' using 'ebp', via call's(do not corrupt anything, eg ebp)
		call	Intercept_CheckMirrorAddresses
		push	ecx
		mov		ecx,ebp
		sub		ecx,0x00ff8000
		mov		ecx,pInterceptReadByteTable[ecx*4]
		call	Intercept_ScanHandlers
		pop		ecx

		jmp		address_ok
	}
}

// NOTE! These MUST not corrupt 'ecx'
extern "C" NAKED void Intercept_ReadWord(void)
{
	__asm {
		// Check if need to intercept address read
		test	ebp,0x00000001							// Is address error? (not correct alignment)
		jne		M68000_AddressError
		cmp		ebp,0x00ff8000							// Is hardware address?
		jge		intercept_address
		cmp		ebp,[STRamEnd_BusErr]
		jl		null_area
		cmp		ebp,BUS_ERROR_ADDR						// Is a bus error? (invalid memory addressing)
		jl		M68000_BusError

address_ok:
		mov		bx,WORD PTR STRam[ebp]
		SWAP_ENDIAN_WORD_BX
		ret
null_area:
		xor		bx,bx									// Blank area between RAM and bus error
		ret
	}

intercept_address:
	__asm {
		mov		[BusAddressLocation],ebp				// Store for exception frame, just in case
		// Run list of 'readword' using 'ebp', via call's(do not corrupt anything, eg ebp)
		call	Intercept_CheckMirrorAddresses
		push	ecx
		mov		ecx,ebp
		sub		ecx,0x00ff8000
		mov		ecx,pInterceptReadWordTable[ecx*4]
		call	Intercept_ScanHandlers
		pop		ecx

		jmp		address_ok
	}
}

// NOTE! These MUST not corrupt 'ecx'
extern "C" NAKED void Intercept_ReadLong(void)
{
	__asm {
		// Check if need to intercept address read
		test	ebp,0x00000001							// Is address error? (not correct alignment)
		jne		M68000_AddressError
		cmp		ebp,0x00ff8000							// Is hardware address?
		jge		intercept_address
		cmp		ebp,[STRamEnd_BusErr]
		jl		null_area
		cmp		ebp,BUS_ERROR_ADDR						// Is a bus error? (invalid memory addressing)
		jl		M68000_BusError

address_ok:
		mov		ebx,DWORD PTR STRam[ebp]
		SWAP_ENDIAN_LONG_EBX
		ret
null_area:
		xor		ebx,ebx									// Blank area between RAM and bus error
		ret
	}

intercept_address:
	__asm {
		mov		[BusAddressLocation],ebp				// Store for exception frame, just in case
		// Run list of 'readlong' using 'ebp', via call's(do not corrupt anything, eg ebp)
		call	Intercept_CheckMirrorAddresses
		push	ecx
		mov		ecx,ebp
		sub		ecx,0x00ff8000
		mov		ecx,pInterceptReadLongTable[ecx*4]
		call	Intercept_ScanHandlers
		pop		ecx

		jmp		address_ok
	}
}

//-----------------------------------------------------------------------
extern "C" NAKED void Intercept_WriteByte(void)
{
	__asm {
		// Check if need to intercept address read
		cmp		ebp,0x00ff8000							// Is hardware address?
		jge		intercept_address
		cmp		ebp,[STRamEnd_BusErr]					// Allow writes to no-mans-land(just return)
		jl		null_area
		cmp		ebp,BUS_ERROR_ADDR						// Is a bus error? (invalid memory addressing)
		jl		M68000_BusError

		mov		BYTE PTR STRam[ebp],bl
null_area:
		ret
	}

intercept_address:
	__asm {
		mov		[BusAddressLocation],ebp				// Store for exception frame, just in case
		call	Intercept_CheckMirrorAddresses
		mov		BYTE PTR STRam[ebp],bl
		// Run list of 'writebyte' using 'ebp', via call's(do not corrupt anything, eg ebp)
		push	ecx
		mov		ecx,ebp
		sub		ecx,0x00ff8000
		mov		ecx,pInterceptWriteByteTable[ecx*4]
		call	Intercept_ScanHandlers
		pop		ecx

		ret
	}
}

extern "C" NAKED void Intercept_WriteWord(void)
{
	__asm {
		// Check if need to intercept address read
		test	ebp,0x00000001							// Is address error? (not correct alignment)
		jne		M68000_AddressError
		cmp		ebp,0x00ff8000							// Is hardware address?
		jge		intercept_address
		cmp		ebp,[STRamEnd_BusErr]					// Allow writes to no-mans-land(just return)
		jl		null_area
		cmp		ebp,BUS_ERROR_ADDR						// Is a bus error? (invalid memory addressing)
		jl		M68000_BusError

		SWAP_ENDIAN_WORD_BX
		mov		WORD PTR STRam[ebp],bx
		SWAP_ENDIAN_WORD_BX								// Must return 'bx' to original state for ccodes checks
null_area:
		ret
	}

intercept_address:
	__asm {
		mov		[BusAddressLocation],ebp				// Store for exception frame, just in case
		call	Intercept_CheckMirrorAddresses
		SWAP_ENDIAN_WORD_BX
		mov		WORD PTR STRam[ebp],bx
		SWAP_ENDIAN_WORD_BX								// Must return 'bx' to original state for ccodes checks
		// Run list of 'writeword' using 'ebp', via call's(do not corrupt anything, eg ebp)
		push	ecx
		mov		ecx,ebp
		sub		ecx,0x00ff8000
		mov		ecx,pInterceptWriteWordTable[ecx*4]
		call	Intercept_ScanHandlers
		pop		ecx

		ret
	}
}

extern "C" NAKED void Intercept_WriteLong(void)
{
	__asm {
		// Check if need to intercept address read
		test	ebp,0x00000001							// Is address error? (not correct alignment)
		jne		M68000_AddressError
		cmp		ebp,0x00ff8000							// Is hardware address?
		jge		intercept_address
		cmp		ebp,[STRamEnd_BusErr]					// Allow writes to no-mans-land(just return)
		jl		null_area
		cmp		ebp,BUS_ERROR_ADDR						// Is a bus error? (invalid memory addressing)
		jl		M68000_BusError	

		SWAP_ENDIAN_LONG_EBX
		mov		DWORD PTR STRam[ebp],ebx
		SWAP_ENDIAN_LONG_EBX							// Must return 'ebx' to original state for ccodes checks
null_area:
		ret
	}

intercept_address:
	__asm {
		mov		[BusAddressLocation],ebp				// Store for exception frame, just in case
		call	Intercept_CheckMirrorAddresses
		SWAP_ENDIAN_LONG_EBX
		mov		DWORD PTR STRam[ebp],ebx
		SWAP_ENDIAN_LONG_EBX							// Must return 'ebx' to original state for ccodes checks
		// Run list of 'writelong' using 'ebp', via call's(do not corrupt anything, eg ebp)
		push	ecx
		mov		ecx,ebp
		sub		ecx,0x00ff8000
		mov		ecx,pInterceptWriteLongTable[ecx*4]
		call	Intercept_ScanHandlers
		pop		ecx

		ret
	}
}


//-----------------------------------------------------------------------
/*
	Read from Hardware(0x00ff8000 to 0xffffff)
*/

// INTERCEPT_VIDEOHIGH(0xff8205 byte)
NAKED void Intercept_VideoHigh_ReadByte(void)
{
	__asm {
		call	Video_ReadAddress						// Stores video address into 'ebx'
		ror		ebx,16									// Take high byte
		mov		STRam[0xff8205],bl
		ret
	}
}

// INTERCEPT_VIDEOMED(0xff8207 byte)
NAKED void Intercept_VideoMed_ReadByte(void)
{
	__asm {
		call	Video_ReadAddress						// Stores video address into 'ebx'
		mov		STRam[0xff8207],bh						// Take middle byte
		ret
	}
}

// INTERCEPT_VIDEOLOW(0xff8209 byte)
NAKED void Intercept_VideoLow_ReadByte(void)
{
	__asm {
		call	Video_ReadAddress						// Stores video address into 'ebx'
		mov		STRam[0xff8209],bl						// Take low byte
		ret
	}
}

// INTERCEPT_VIDEOSYNC(0xff820a byte)
NAKED void Intercept_VideoSync_ReadByte(void)
{
	__asm {
		mov		bl,[VideoSyncByte]					// Read shifter register
		mov		STRam[0xff820a],bl
		ret
	}

	//__asm {
		//ret												// Nothing...
	//}
}

// INTERCEPT_VIDEOBASELOW(0xff820d byte)
NAKED void Intercept_VideoBaseLow_ReadByte(void)
{
	__asm {
		xor		bl,bl									// ST can only store screen address to 256 bytes(ie no lower byte)
		mov		STRam[0xff820d],bl
		ret
	}
}

// INTERCEPT_LINEWIDTH(0xff820f byte)
NAKED void Intercept_LineWidth_ReadByte(void)
{
	__asm {
		xor		bl,bl									// On ST this is always 0
		mov		STRam[0xff820f],bl
		ret
	}
}

// INTERCEPT_COLOUR0(0xff8240 word)
NAKED void Intercept_Colour0_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR1(0xff8242 word)
NAKED void Intercept_Colour1_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR2(0xff8244 word)
NAKED void Intercept_Colour2_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR3(0xff8246 word)
NAKED void Intercept_Colour3_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR4(0xff8248 word)
NAKED void Intercept_Colour4_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR5(0xff824a word)
NAKED void Intercept_Colour5_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR6(0xff824c word)
NAKED void Intercept_Colour6_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR7(0xff824e word)
NAKED void Intercept_Colour7_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR8(0xff8250 word)
NAKED void Intercept_Colour8_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR9(0xff8252 word)
NAKED void Intercept_Colour9_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR10(0xff8254 word)
NAKED void Intercept_Colour10_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR11(0xff8256 word)
NAKED void Intercept_Colour11_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR12(0xff8258 word)
NAKED void Intercept_Colour12_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR13(0xff825a word)
NAKED void Intercept_Colour13_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR14(0xff825c word)
NAKED void Intercept_Colour14_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_COLOUR15(0xff825e word)
NAKED void Intercept_Colour15_ReadWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_SHIFTERMODE(0xff8260 byte)
NAKED void Intercept_ShifterMode_ReadByte(void)
{
	__asm {
		mov		bl,[VideoShifterByte]					// Read shifter register
		cmp		[bUseHighRes],FALSE
		je		do_not_set_high_res
		mov		bl,0x2									// If mono monitor, force to high resolution
do_not_set_high_res:
		mov		STRam[0xff8260],bl
		ret
	}
}

// INTERCEPT_DISKCONTROL(0xff8604 word)
NAKED void Intercept_DiskControl_ReadWord(void)
{
	__asm {
		call	FDC_ReadDiscControllerStatus
		SWAP_ENDIAN_WORD_BX
		mov		WORD PTR STRam[0xff8604],bx
		ret
	}
}

// INTERCEPT_DMASTATUS(0xff8606 word)
NAKED void Intercept_DmaStatus_ReadWord(void)
{
	__asm {
		call	FDC_ReadDMAStatus
		SWAP_ENDIAN_WORD_BX
		mov		WORD PTR STRam[0xff8606],bx
		ret
	}
}

// INTERCEPT_PSG_REGISTER(0xff8800 byte)
NAKED void Intercept_PSGRegister_ReadByte(void)
{
	__asm {
		call	PSG_ReadSelectRegister
		mov		STRam[0xff8800],bl
		ret
	}
}

// INTERCEPT_PSG_DATA(0xff8802 byte)
NAKED void Intercept_PSGData_ReadByte(void)
{
	__asm {
		call	PSG_ReadDataRegister
		mov		STRam[0xff8802],bl
		ret
	}
}

// INTERCEPT_MICROWIREDATA(0xff8922 word)
NAKED void Intercept_MicrowireData_ReadWord(void)
{
	__asm {
		xor		bx,bx
		mov		WORD PTR STRam[0xff8922],bx
		ret
	}
}

// INTERCEPT_MONITOR(0xfffa01 byte)
NAKED void Intercept_Monitor_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_GPIP]							// Lower 7-bits are GPIP(Top bit is monitor type)
		
		and		bl,0x7f									// Default Mono monitor(0)
		cmp		[bUseHighRes],TRUE
		je		set_high_res
		or		bl,0x80									// Colour monitor(1)
set_high_res:

		mov		STRam[0xfffa01],bl
		ret
	}
}

// INTERCEPT_ACTIVE_EDGE(0xfffa03 byte)
NAKED void Intercept_ActiveEdge_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_AER]
		mov		STRam[0xfffa03],bl
		ret
	}
}

// INTERCEPT_DATA_DIRECTION(0xfffa05 byte)
NAKED void Intercept_DataDirection_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_DDR]
		mov		STRam[0xfffa05],bl
		ret
	}
}

// INTERCEPT_ENABLE_A(0xfffa07 byte)
NAKED void Intercept_EnableA_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_IERA]
		mov		STRam[0xfffa07],bl
		ret
	}
}

// INTERCEPT_ENABLE_B(0xfffa09 byte)
NAKED void Intercept_EnableB_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_IERB]
		mov		STRam[0xfffa09],bl
		ret
	}
}

// INTERCEPT_PENDING_A(0xfffa0b byte)
NAKED void Intercept_PendingA_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_IPRA]
		mov		STRam[0xfffa0b],bl
		ret
	}
}

// INTERCEPT_PENDING_B(0xfffa0d byte)
NAKED void Intercept_PendingB_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_IPRB]
		mov		STRam[0xfffa0d],bl
		ret
	}
}

// INTERCEPT_INSERVICE_A(0xfffa0f byte)
NAKED void Intercept_InServiceA_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_ISRA]
		mov		STRam[0xfffa0f],bl
		ret
	}
}

// INTERCEPT_INSERVICE_B(0xfffa11 byte)
NAKED void Intercept_InServiceB_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_ISRB]
		mov		STRam[0xfffa11],bl
		ret
	}
}

// INTERCEPT_MASK_A(0xfffa13 byte)
NAKED void Intercept_MaskA_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_IMRA]
		mov		STRam[0xfffa13],bl
		ret
	}
}

// INTERCEPT_MASK_B(0xfffa15 byte)
NAKED void Intercept_MaskB_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_IMRB]
		mov		STRam[0xfffa15],bl
		ret
	}
}

// INTERCEPT_VECTOR_REG(0xfffa17 byte)
NAKED void Intercept_VectorReg_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_VR]
		mov		STRam[0xfffa17],bl
		ret
	}
}

// INTERCEPT_TIMERA_CTRL(0xfffa19 byte)
NAKED void Intercept_TimerACtrl_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_TACR]
		mov		STRam[0xfffa19],bl
		ret
	}
}

// INTERCEPT_TIMERB_CTRL(0xfffa1b byte)
NAKED void Intercept_TimerBCtrl_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_TBCR]
		mov		STRam[0xfffa1b],bl
		ret
	}
}

// INTERCEPT_TIMERCD_CTRL(0xfffa1d byte)
NAKED void Intercept_TimerCDCtrl_ReadByte(void)
{
	__asm {
		mov		bl,[MFP_TCDCR]
		mov		STRam[0xfffa1d],bl
		ret
	}
}

// INTERCEPT_TIMERA_DATA(0xfffa1f byte)
NAKED void Intercept_TimerAData_ReadByte(void)
{
	__asm {
		// Is event count? Need to re-calculate counter
		cmp		[MFP_TACR],0x8
		je		in_event_mode
		call	MFP_ReadTimerA							// Stores result in 'MFP_TA_MAINCOUNTER'
in_event_mode:
		mov		bl,[MFP_TA_MAINCOUNTER]

		mov		STRam[0xfffa1f],bl
		ret
	}
}

// INTERCEPT_TIMERB_DATA(0xfffa21 byte)
NAKED void Intercept_TimerBData_ReadByte(void)
{
	__asm {
		// Is event count? Need to re-calculate counter
		cmp		[MFP_TBCR],0x8
		je		in_event_mode
		call	MFP_ReadTimerB							// Stores result in 'MFP_TB_MAINCOUNTER'
in_event_mode:
		mov		bl,[MFP_TB_MAINCOUNTER]

		mov		STRam[0xfffa21],bl
		ret
	}
}

// INTERCEPT_TIMERC_DATA(0xfffa23 byte)
NAKED void Intercept_TimerCData_ReadByte(void)
{
	__asm {
		call	MFP_ReadTimerC							// Stores result in 'MFP_TC_MAINCOUNTER'
		mov		bl,[MFP_TC_MAINCOUNTER]

		mov		STRam[0xfffa23],bl
		ret
	}
}

// INTERCEPT_TIMERD_DATA(0xfffa25 byte)
NAKED void Intercept_TimerDData_ReadByte(void)
{
	__asm {
		call	MFP_ReadTimerD							// Stores result in 'MFP_TD_MAINCOUNTER'
		mov		bl,[MFP_TD_MAINCOUNTER]

		mov		STRam[0xfffa25],bl
		ret
	}
}

// INTERCEPT_KEYBOARDCONTROL(0xfffc00 byte)
NAKED void Intercept_KeyboardControl_ReadByte(void)
{
	__asm {
		mov		bl,[ACIAStatusRegister]
		or		bl,ACIA_STATUS_REGISTER__TX_BUFFER_EMPTY	// For our emulation send is immediate so acknowledge buffer is empty

		mov		STRam[0xfffc00],bl
		ret
	}
}

// INTERCEPT_KEYBOARDDATA(0xfffc02 byte)
NAKED void Intercept_KeyboardData_ReadByte(void)
{
	__asm {
		call	IKBD_GetByteFromACIA						// Return our byte 'bl' from keyboard processor
		mov		STRam[0xfffc02],bl
		ret
	}
}

// INTERCEPT_MIDICONTROL(0xfffc04 byte)
NAKED void Intercept_MidiControl_ReadByte(void)
{
	__asm {
		mov		bl,0x02									// Should be this?
		mov		STRam[0xfffc04],bl
		ret
	}
}

// INTERCEPT_MIDIDATA(0xfffc06 byte)
NAKED void Intercept_MidiData_ReadByte(void)
{
	__asm {
		mov		bl,0x01									// Should be this?
		mov		STRam[0xfffc06],bl
		ret
	}
}


//-----------------------------------------------------------------------
/*
	Write to Hardware(0x00ff8000 to 0xffffff)
*/

// INTERCEPT_VIDEOHIGH(0xff8205 byte)
NAKED void Intercept_VideoHigh_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_VIDEOMED(0xff8207 byte)
NAKED void Intercept_VideoMed_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_VIDEOLOW(0xff8209 byte)
NAKED void Intercept_VideoLow_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_VIDEOSYNC(0xff820a byte)
NAKED void Intercept_VideoSync_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xff820a]
		and		bl,0x3									// We're only interested in lower 2 bits(50/60Hz)
		mov		[VideoSyncByte],bl

		SAVE_ASSEM_REGS
		push	ebx
		call	Video_WriteToSync
		pop		ebx
		RESTORE_ASSEM_REGS

		ret												// Nothing...
	}
}

// INTERCEPT_VIDEOBASELOW(0xff820d byte)
NAKED void Intercept_VideoBaseLow_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_LINEWIDTH(0xff820f byte)
NAKED void Intercept_LineWidth_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

NAKED void Intercept_Colour_WriteWord(void)
{
	__asm {
		cmp		[bUseHighRes],TRUE						// Don't store if hi-res
		je		no_write

		push	ebp
		push	ecx
		push	edx

		call	Video_SetHBLPaletteMaskPointers			// Set 'pHBLPalettes' etc.. according cycles into frame

		mov		ecx,ebp									// Colour 0xff8240
		and		WORD PTR STRam[ebp],0x7707				// Mask off to 512 palette(some games write 0xFFFF and read back to see if STe)
		mov		bx,WORD PTR STRam[ebp]					// (as 0x0777)
		SWAP_ENDIAN_WORD_BX
		call	Spec512_StoreCyclePalette				// Store colour 'bx' into CyclePalettes[]
		sub		ecx,0xff8240							// 0,1,2,3...15

		mov		ebp,[pHBLPalettes]						// Set colour x
		mov		WORD PTR [ebp+ecx],bx

		shr		ecx,1
		mov		edx,0x0001
		shl		edx,cl
		mov		ebp,[pHBLPaletteMasks]					// And mask
		or		WORD PTR [ebp],dx
		
		pop		edx
		pop		ecx
		pop		ebp
no_write:
		ret
	}
}

// INTERCEPT_COLOUR0(0xff8240 word)
NAKED void Intercept_Colour0_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8240
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR1(0xff8242 word)
NAKED void Intercept_Colour1_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8242
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR2(0xff8244 word)
NAKED void Intercept_Colour2_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8244
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR3(0xff8246 word)
NAKED void Intercept_Colour3_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8246
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR4(0xff8248 word)
NAKED void Intercept_Colour4_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8248
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR5(0xff824a word)
NAKED void Intercept_Colour5_WriteWord(void)
{
	__asm {
		mov		ebp,0xff824a
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR6(0xff824c word)
NAKED void Intercept_Colour6_WriteWord(void)
{
	__asm {
		mov		ebp,0xff824c
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR7(0xff824e word)
NAKED void Intercept_Colour7_WriteWord(void)
{
	__asm {
		mov		ebp,0xff824e
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR8(0xff8250 word)
NAKED void Intercept_Colour8_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8250
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR9(0xff8252 word)
NAKED void Intercept_Colour9_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8252
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR10(0xff8254 word)
NAKED void Intercept_Colour10_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8254
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR11(0xff8256 word)
NAKED void Intercept_Colour11_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8256
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR12(0xff8258 word)
NAKED void Intercept_Colour12_WriteWord(void)
{
	__asm {
		mov		ebp,0xff8258
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR13(0xff825a word)
NAKED void Intercept_Colour13_WriteWord(void)
{
	__asm {
		mov		ebp,0xff825a
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR14(0xff825c word)
NAKED void Intercept_Colour14_WriteWord(void)
{
	__asm {
		mov		ebp,0xff825c
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_COLOUR15(0xff825e word)
NAKED void Intercept_Colour15_WriteWord(void)
{
	__asm {
		mov		ebp,0xff825e
		call	Intercept_Colour_WriteWord
		ret
	}
}

// INTERCEPT_SHIFTERMODE(0xff8260 byte)
NAKED void Intercept_ShifterMode_WriteByte(void)
{
	__asm {
		cmp		[bUseHighRes],TRUE						// Don't store if hi-res
		je		no_write
		cmp		[bUseVDIRes],TRUE						// Don't store if VDI resolution
		je		no_write

		SAVE_ASSEM_REGS
		mov		bl,STRam[0xff8260]						// Read byte
		and		bl,0x3									// We only care for lower 2-bits
		mov		[VideoShifterByte],bl

		call	Video_WriteToShifter
		call	Video_SetHBLPaletteMaskPointers

		mov		ebp,[pHBLPaletteMasks]					// Store resolution
		mov		bl,[VideoShifterByte]
		mov		BYTE PTR [ebp+2],bl						// after palette mask
		or		BYTE PTR [ebp+2],0x04					// and set resolution write bit
		RESTORE_ASSEM_REGS

no_write:
		ret
	}
}

// INTERCEPT_DISKCONTROL(0xff8604 word)
NAKED void Intercept_DiskControl_WriteWord(void)
{
	__asm {
		mov		bx,WORD PTR STRam[0xff8604]
		SWAP_ENDIAN_WORD_BX
		call	FDC_WriteDiscController
		ret
	}
}

// INTERCEPT_DMASTATUS(0xff8606 word)
NAKED void Intercept_DmaStatus_WriteWord(void)
{
	__asm {
		mov		bx,WORD PTR STRam[0xff8606]
		SWAP_ENDIAN_WORD_BX
		call	FDC_WriteDMAModeControl
		ret
	}
}

// INTERCEPT_PSG_REGISTER(0xff8800 byte)
NAKED void Intercept_PSGRegister_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xff8800]
		call	PSG_WriteSelectRegister
		ret
	}
}

// INTERCEPT_PSG_DATA(0xff8802 byte)
NAKED void Intercept_PSGData_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xff8802]
		call	PSG_WriteDataRegister
		ret
	}
}

// INTERCEPT_MICROWIREDATA(0xff8922 word)
NAKED void Intercept_MicrowireData_WriteWord(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_MONITOR(0xfffa01 byte)
NAKED void Intercept_Monitor_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_ACTIVE_EDGE(0xfffa03 byte)
NAKED void Intercept_ActiveEdge_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa03]
		mov		[MFP_AER],bl
		ret
	}
}

// INTERCEPT_DATA_DIRECTION(0xfffa05 byte)
NAKED void Intercept_DataDirection_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa05]
		mov		[MFP_DDR],bl
		ret
	}
}


// INTERCEPT_ENABLE_A(0xfffa07 byte)
NAKED void Intercept_EnableA_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa07]
		mov		[MFP_IERA],bl							// Store register
		and		[MFP_IPRA],bl							// Clear pending bits for disabled channels
		call	MFP_UpdateFlags							// Check if any interrupts pending

		// We may have enabled Timer A or B, check
		call	MFP_StartTimerA
		call	MFP_StartTimerB
		ret
	}
}

// INTERCEPT_ENABLE_B(0xfffa09 byte)
NAKED void Intercept_EnableB_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa09]
		mov		[MFP_IERB],bl							// Store register
		and		[MFP_IPRB],bl							// Clear pending bits for disabled channels
		call	MFP_UpdateFlags							// Check if any interrupts pending

		// We may have enabled Timer C or D, check
		call	MFP_StartTimerC
		call	MFP_StartTimerD
		ret
	}
}

// INTERCEPT_PENDING_A(0xfffa0b byte)
NAKED void Intercept_PendingA_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa0b]
		and		[MFP_IPRA],bl							// Cannot set pending bits - only clear via software
		call	MFP_UpdateFlags							// Check if any interrupts pending
		ret
	}
}

// INTERCEPT_PENDING_B(0xfffa0d byte)
NAKED void Intercept_PendingB_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa0d]
		and		[MFP_IPRB],bl
		call	MFP_UpdateFlags							// Check if any interrupts pending
		ret
	}
}

// INTERCEPT_INSERVICE_A(0xfffa0f byte)
NAKED void Intercept_InServiceA_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa0f]
		and		[MFP_ISRA],bl							// Cannot set in-service bits - only clear via software
		ret
	}
}

// INTERCEPT_INSERVICE_B(0xfffa11 byte)
NAKED void Intercept_InServiceB_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa11]
		and		[MFP_ISRB],bl							// Cannot set in-service bits - only clear via software
		ret
	}
}

// INTERCEPT_MASK_A(0xfffa13 byte)
NAKED void Intercept_MaskA_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa13]
		mov		[MFP_IMRA],bl
		ret
	}
}

// INTERCEPT_MASK_B(0xfffa15 byte)
NAKED void Intercept_MaskB_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa15]
		mov		[MFP_IMRB],bl
		ret
	}
}

// INTERCEPT_VECTOR_REG(0xfffa17 byte)
NAKED void Intercept_VectorReg_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa17]

		push	ecx

		mov		cl,[MFP_VR]								// Copy for checking if set mode
		mov		[MFP_VR],bl

		// OK, now have we set automatic end-of-interrupt mode?
		xor		cl,bl
		test	cl,0x08									// Test change in end-of-interrupt mode
		je		no_mode_change
		// Mode did change but was it to automatic mode? (ie bit is a zero)
		test	bl,0x08
		je		no_mode_change
		// We are now in automatic mode, so clear all in-service bits!
		mov		[MFP_ISRA],0
		mov		[MFP_ISRB],0
no_mode_change:

		pop		ecx
		ret
	}
}

// INTERCEPT_TIMERA_CTRL(0xfffa19 byte)
NAKED void Intercept_TimerACtrl_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa19]

		push	ecx

		mov		cl,[MFP_TACR]							// Remember old control state
		and		bl,0x0f									// Mask, Fish(auto160) writes into top nibble!
		mov		[MFP_TACR],bl
		
		// Check if Timer A control changed
		mov		bl,cl									// Get old state
		xor		bl,[MFP_TACR]							// Check against new
		test	bl,0x0f
		je		no_timer_a_change
		call	MFP_StartTimerA							// Reset timers if need to
no_timer_a_change:

		pop		ecx
		ret
	}
}

// INTERCEPT_TIMERB_CTRL(0xfffa1b byte)
NAKED void Intercept_TimerBCtrl_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa1b]

		push	ecx

		mov		cl,[MFP_TBCR]							// Remember old control state
		and		bl,0x0f									// Mask, Fish(auto160) writes into top nibble!
		mov		[MFP_TBCR],bl
		
		// Check if Timer B control changed
		mov		bl,cl									// Get old state
		xor		bl,[MFP_TBCR]							// Check against new
		test	bl,0x0f
		je		no_timer_b_change
		call	MFP_StartTimerB							// Reset timers if need to
no_timer_b_change:

		pop		ecx
		ret
	}
}

// INTERCEPT_TIMERCD_CTRL(0xfffa1d byte)
NAKED void Intercept_TimerCDCtrl_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa1d]

		push	ecx

		mov		cl,[MFP_TCDCR]							// Remember old control state
		mov		[MFP_TCDCR],bl							// Store new one
		
		// Check if Timer C control changed
		mov		bl,cl									// Get old state
		xor		bl,[MFP_TCDCR]							// Check against new
		test	bl,0x70
		je		no_timer_c_change
		call	MFP_StartTimerC							// Reset timers if need to
no_timer_c_change:

		// Check if Timer D control changed
		mov		bl,cl									// Get old state
		xor		bl,[MFP_TCDCR]							// Check against new
		test	bl,0x07
		je		no_timer_d_change
		call	MFP_StartTimerD							// Reset timers if need to
no_timer_d_change:

		pop		ecx
		ret
	}
}

// INTERCEPT_TIMERA_DATA(0xfffa1f byte)
NAKED void Intercept_TimerAData_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa1f]

		mov		[MFP_TADR],bl							// Store into data register
		// Now check if timer is running - if so do not set
		cmp		[MFP_TACR],0							// Is control register zero?
		jne		cannot_set
		mov		[MFP_TA_MAINCOUNTER],bl					// Timer is off, store to main counter
		call	MFP_StartTimerA							// Add our interrupt
cannot_set:
		ret
	}
}

// INTERCEPT_TIMERB_DATA(0xfffa21 byte)
NAKED void Intercept_TimerBData_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa21]

		mov		[MFP_TBDR],bl							// Store into data register
		// Now check if timer is running - if so do not set
		cmp		[MFP_TBCR],0							// Is control register zero?
		jne		cannot_set
		mov		[MFP_TB_MAINCOUNTER],bl					// Timer is off, store to main counter
		call	MFP_StartTimerB							// Add our interrupt
cannot_set:
		ret
	}
}

// INTERCEPT_TIMERC_DATA(0xfffa23 byte)
NAKED void Intercept_TimerCData_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa23]

		mov		[MFP_TCDR],bl							// Store into data register
		// Now check if timer is running - if so do not set
		test	[MFP_TCDCR],0x70						// Is control register zero?
		jne		cannot_set
		call	MFP_StartTimerC							// Add our interrupt
cannot_set:
		ret
	}
}

// INTERCEPT_TIMERD_DATA(0xfffa25 byte)
NAKED void Intercept_TimerDData_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffa25]

		mov		[MFP_TDDR],bl							// Store into data register
		// Now check if timer is running - if so do not set
		test	[MFP_TCDCR],0x07						// Is control register zero?
		jne		cannot_set
		call	MFP_StartTimerD							// Add our interrupt
cannot_set:
		ret
	}
}

// INTERCEPT_KEYBOARDCONTROL(0xfffc00 byte)
NAKED void Intercept_KeyboardControl_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_KEYBOARDDATA(0xfffc02 byte)
NAKED void Intercept_KeyboardData_WriteByte(void)
{
	__asm {
		mov		bl,STRam[0xfffc02]

		call	IKBD_SendByteToKeyboardProcessor		// Pass our byte 'bl' to the keyboard processor
		ret
	}
}

// INTERCEPT_MIDICONTROL(0xfffc04 byte)
NAKED void Intercept_MidiControl_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// INTERCEPT_MIDIDATA(0xfffc06 byte)
NAKED void Intercept_MidiData_WriteByte(void)
{
	__asm {
		ret												// Nothing...
	}
}

// Address space for Bus Error in hardware mapping
INTERCEPT_ADDRESSRANGE InterceptBusErrors[] = {
	0xff8a00,0xff8a3e,									// Blitter
	0xff8900,0xff8960,									// DMA Sound/MicroWire

	0,0	//term
};

//-----------------------------------------------------------------------
/*
	Modify 'intercept' tables to cause Bus Errors on addres to un-mapped hardware space(Wing Of Death addresses Blitter space which causes BusError on STfm)
*/
void Intercept_ModifyTablesForBusErrors(void)
{
	unsigned long *pInterceptList;
	unsigned int Address;
	int i=0;

	// Set routine list
	pInterceptList = pCurrentInterceptWorkspace;
	*pCurrentInterceptWorkspace++ = (unsigned long)M68000_BusError;
	*pCurrentInterceptWorkspace++ = NULL;

	// Set all 'no-mans-land' entries
	while(InterceptBusErrors[i].Start_Address!=0) {
		// Set 'no-mans-land' table
		for(Address=InterceptBusErrors[i].Start_Address; Address<InterceptBusErrors[i].End_Address; Address++) {
			// For 'read'
			pInterceptReadByteTable[Address-0xff8000] = pInterceptList;
			pInterceptReadWordTable[Address-0xff8000] = pInterceptList;
			pInterceptReadLongTable[Address-0xff8000] = pInterceptList;
			// and 'write'
			pInterceptWriteByteTable[Address-0xff8000] = pInterceptList;
			pInterceptWriteWordTable[Address-0xff8000] = pInterceptList;
			pInterceptWriteLongTable[Address-0xff8000] = pInterceptList;
		}

		i++;
	}
}

#ifdef CHECK_FOR_NO_MANS_LAND

// List of hardware addresses which are 'no-man's-land', ie not connected on ST
// NOTE PSG is mirror'd in range 0xff8004 to 0xff8900
INTERCEPT_ADDRESSRANGE InterceptNoMansLand[] = {
	0xff8002,0xff8200,									// All these are illegal addresses on standard STfm
	0xff8210,0xff8240,
	0xff8262,0xff8600,
	0xff860e,0xff8800,
	0xff8900,0xfffa00,
	0xfffa30,0xfffc00,
	0xfffc20,0x1000000,

	0,0	//term
};

//-----------------------------------------------------------------------
/*
	Intercept function used on all non-documented hardware registers. Used to help debugging
*/
NAKED void Intercept_NoMansLand_ReadWrite(void)
{
	SAVE_ASSEM_REGS
	__asm {
		// 'ebp' will hold address we tried to access!
		// 'esi' is ST program counter!
		int	3											// Cause Windows GPF, so can see register list
	}
}

//-----------------------------------------------------------------------
/*
	Modify 'intercept' tables to check for access into 'no-mans-land', ie unknown hardware locations
*/
void Intercept_ModifyTablesForNoMansLand(void)
{
	unsigned long *pInterceptList;
	unsigned int Address;
	int i=0;

	// Set routine list
	pInterceptList = pCurrentInterceptWorkspace;
	*pCurrentInterceptWorkspace++ = (unsigned long)Intercept_NoMansLand_ReadWrite;
	*pCurrentInterceptWorkspace++ = NULL;

	// Set all 'no-mans-land' entries
	while(InterceptNoMansLand[i].Start_Address!=0) {
		// Set 'no-mans-land' table
		for(Address=InterceptNoMansLand[i].Start_Address; Address<InterceptNoMansLand[i].End_Address; Address++) {
			// For 'read'
			pInterceptReadByteTable[Address-0xff8000] = pInterceptList;
			pInterceptReadWordTable[Address-0xff8000] = pInterceptList;
			pInterceptReadLongTable[Address-0xff8000] = pInterceptList;
			// and 'write'
			pInterceptWriteByteTable[Address-0xff8000] = pInterceptList;
			pInterceptWriteWordTable[Address-0xff8000] = pInterceptList;
			pInterceptWriteLongTable[Address-0xff8000] = pInterceptList;
		}

		i++;
	}
}

#endif	//CHECK_FOR_NO_MANS_LAND

