/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

#ifndef _FXINST_H_
#define _FXINST_H_

/*
 * FxChip(GSU) register space specification
 * (Register address space 3000-32ff)
 *
 * The 16 generic 16 bit registers:
 * (Some have a special function in special circumstances)
 * 3000 - R0    default source/destination register
 * 3002 - R1    pixel plot X position register
 * 3004 - R2    pixel plot Y position register
 * 3006 - R3
 * 3008 - R4    lower 16 bit result of lmult
 * 300a - R5
 * 300c - R6    multiplier for fmult and lmult
 * 300e - R7    fixed point texel X position for merge
 * 3010 - R8    fixed point texel Y position for merge
 * 3012 - R9
 * 3014 - R10
 * 3016 - R11   return address set by link
 * 3018 - R12   loop counter
 * 301a - R13   loop point address
 * 301c - R14   rom address for getb, getbh, getbl, getbs
 * 301e - R15   program counter
 *
 * 3020-302f -  unused
 *
 * Other internal registers
 * 3030 - SFR   status flag register  (16bit)
 * 3032 - unused
 * 3033 - BRAMR Backup RAM register    (8bit)
 * 3034 - PBR   program bank register  (8bit)
 * 3035 - unused
 * 3036 - ROMBR rom bank register      (8bit)
 * 3037 - CFGR  control flags register (8bit)
 * 3038 - SCBR  screen base register   (8bit)
 * 3039 - CLSR  clock speed register   (8bit)
 * 303a - SCMR  screen mode register   (8bit)
 * 303b - VCR   version code register  (8bit) (read only)
 * 303c - RAMBR ram bank register      (8bit)
 * 303d - unused
 * 303e - CBR   cache base register   (16bit)
 *
 * 3040-30ff -  unused
 *
 * 3100-32ff -  CACHERAM 512 bytes of GSU cache memory
 *
 * SFR status flag register bits:
 *  0   -
 *  1   Z     Zero flag
 *  2   CY    Carry flag
 *  3   S     Sign flag
 *  4   OV    Overflow flag
 *  5   G     Go flag (set to 1 when the GSU is running)
 *  6   R     Set to 1 when reading ROM using R14 address
 *  7   -
 *  8   ALT1  Mode set-up flag for the next instruction
 *  9   ALT2  Mode set-up flag for the next instruction
 * 10   IL    Immediate lower 8-bit flag
 * 11   IH    Immediate higher 8-bit flag
 * 12   B     Set to 1 when the WITH instruction is executed
 * 13   -
 * 14   -
 * 15   IRQ   Set to 1 when GSU caused an interrupt
 *            Set to 0 when read by 658c16
 *
 * BRAMR = 0, BackupRAM is disabled
 * BRAMR = 1, BackupRAM is enabled
 *
 * CFGR control flags register bits:
 *  0   -
 *  1   -
 *  2   -
 *  3   -
 *  4   -
 *  5   MS0   Multiplier speed, 0=standard, 1=high speed
 *  6   -
 *  7   IRQ   Set to 1 when GSU interrupt request is masked
 *
 * CLSR clock speed register bits:
 *  0   CLSR  clock speed, 0 = 10.7Mhz, 1 = 21.4Mhz
 *
 * SCMR screen mode register bits:
 *  0	MD0   color depth mode bit 0
 *  1	MD1   color depth mode bit 1
 *  2	HT0   screen height bit 1
 *  3	RAN   RAM access control
 *  4	RON   ROM access control
 *  5	HT1   screen height bit 2
 *  6	-
 *  7	-
 *
 * RON = 0    SNES CPU has ROM access
 * RON = 1    GSU has ROM access
 *
 * RAN = 0    SNES has game pak RAM access
 * RAN = 1    GSU has game pak RAM access
 *
 * HT1  HT0   Screen height mode
 *  0    0    128 pixels high
 *  0    1    160 pixels high
 *  1    0    192 pixels high
 *  1    1    OBJ mode
 *
 * MD1  MD0   Color depth mode
 *  0    0    4   color mode
 *  0    1    16  color mode
 *  1    0    not used
 *  1    1    256 color mode
 *
 * CBR cache base register bits:
 * 15-4       Specify base address for data to cache from ROM or RAM
 *  3-0       Are 0 when address is read
 *
 * Write access to the program counter (301e) from
 * the SNES-CPU will start the GSU, and it will not
 * stop until it reaches a stop instruction.
 *
 */

// Number of banks in GSU RAM
#define FX_RAM_BANKS	4

// Emulate proper R14 ROM access (slower, but safer)
#define FX_DO_ROMBUFFER

// Address checking (definately slow)
//#define FX_ADDRESS_CHECK

struct FxRegs_s
{
	// FxChip registers
	uint32	avReg[16];					// 16 Generic registers
	uint32	vColorReg;					// Internal color register
	uint32	vPlotOptionReg;				// Plot option register
	uint32	vStatusReg;					// Status register
	uint32	vPrgBankReg;				// Program bank index register
	uint32	vRomBankReg;				// Rom bank index register
	uint32	vRamBankReg;				// Ram bank index register
	uint32	vCacheBaseReg;				// Cache base address register
	uint32	vCacheFlags;				// Saying what parts of the cache was written to
	uint32	vLastRamAdr;				// Last RAM address accessed
	uint32	*pvDreg;					// Pointer to current destination register
	uint32	*pvSreg;					// Pointer to current source register
	uint8	vRomBuffer;					// Current byte read by R14
	uint8	vPipe;						// Instructionset pipe
	uint32	vPipeAdr;					// The address of where the pipe was read from

	// Status register optimization stuff
	uint32	vSign;						// v & 0x8000
	uint32	vZero;						// v == 0
	uint32	vCarry;						// a value of 1 or 0
	int32	vOverflow;					// (v >= 0x8000 || v < -0x8000)

	// Other emulator variables
	int32	vErrorCode;
	uint32	vIllegalAddress;

	uint8	bBreakPoint;
	uint32	vBreakPoint;
	uint32	vStepPoint;

	uint8	*pvRegisters;				// 768 bytes located in the memory at address 0x3000
	uint32	nRamBanks;					// Number of 64kb-banks in FxRam (Don't confuse it with SNES-Ram!!!)
	uint8	*pvRam;						// Pointer to FxRam
	uint32	nRomBanks;					// Number of 32kb-banks in Cart-ROM
	uint8	*pvRom;						// Pointer to Cart-ROM

	uint32	vMode;						// Color depth/mode
	uint32	vPrevMode;					// Previous depth
	uint8	*pvScreenBase;
	uint8	*apvScreen[32];				// Pointer to each of the 32 screen colums
	int32	x[32];
	uint32	vScreenHeight;				// 128, 160, 192 or 256 (could be overriden by cmode)
	uint32	vScreenRealHeight;			// 128, 160, 192 or 256
	uint32	vPrevScreenHeight;
	uint32	vScreenSize;
	void	(*pfPlot) (void);
	void	(*pfRpix) (void);

	uint8	*pvRamBank;					// Pointer to current RAM-bank
	uint8	*pvRomBank;					// Pointer to current ROM-bank
	uint8	*pvPrgBank;					// Pointer to current program ROM-bank

	uint8	*apvRamBank[FX_RAM_BANKS];	// Ram bank table (max 256kb)
	uint8	*apvRomBank[256];			// Rom bank table

	uint8	bCacheActive;
	uint8	*pvCache;					// Pointer to the GSU cache
	uint8	avCacheBackup[512];			// Backup of ROM when the cache has replaced it
	uint32	vCounter;
	uint32	vInstCount;
	uint32	vSCBRDirty;					// If SCBR is written, our cached screen pointers need updating
	
	uint8	*avRegAddr;					// To reference avReg in snapshot.cpp
};

extern struct FxRegs_s	GSU;

// GSU registers
#define GSU_R0			0x000
#define GSU_R1			0x002
#define GSU_R2			0x004
#define GSU_R3			0x006
#define GSU_R4			0x008
#define GSU_R5			0x00a
#define GSU_R6			0x00c
#define GSU_R7			0x00e
#define GSU_R8			0x010
#define GSU_R9			0x012
#define GSU_R10			0x014
#define GSU_R11			0x016
#define GSU_R12			0x018
#define GSU_R13			0x01a
#define GSU_R14			0x01c
#define GSU_R15			0x01e
#define GSU_SFR			0x030
#define GSU_BRAMR		0x033
#define GSU_PBR			0x034
#define GSU_ROMBR		0x036
#define GSU_CFGR		0x037
#define GSU_SCBR		0x038
#define GSU_CLSR		0x039
#define GSU_SCMR		0x03a
#define GSU_VCR			0x03b
#define GSU_RAMBR		0x03c
#define GSU_CBR			0x03e
#define GSU_CACHERAM	0x100

// SFR flags
#define FLG_Z			(1 <<  1)
#define FLG_CY			(1 <<  2)
#define FLG_S			(1 <<  3)
#define FLG_OV			(1 <<  4)
#define FLG_G			(1 <<  5)
#define FLG_R			(1 <<  6)
#define FLG_ALT1		(1 <<  8)
#define FLG_ALT2		(1 <<  9)
#define FLG_IL			(1 << 10)
#define FLG_IH			(1 << 11)
#define FLG_B			(1 << 12)
#define FLG_IRQ			(1 << 15)

// Test flag
#define TF(a)			(GSU.vStatusReg &   FLG_##a)
#define CF(a)			(GSU.vStatusReg &= ~FLG_##a)
#define SF(a)			(GSU.vStatusReg |=  FLG_##a)

// Test and set flag if condition, clear if not
#define TS(a, b)		GSU.vStatusReg = ((GSU.vStatusReg & (~FLG_##a)) | ((!!(##b)) * FLG_##a))

// Testing ALT1 & ALT2 bits
#define ALT0			(!TF(ALT1) && !TF(ALT2))
#define ALT1			( TF(ALT1) && !TF(ALT2))
#define ALT2			(!TF(ALT1) &&  TF(ALT2))
#define ALT3			( TF(ALT1) &&  TF(ALT2))

// Sign extend from 8/16 bit to 32 bit
#define SEX8(a)			((int32)  ((int8)   (a)))
#define SEX16(a)		((int32)  ((int16)  (a)))

// Unsign extend from 8/16 bit to 32 bit
#define USEX8(a)		((uint32) ((uint8)  (a)))
#define USEX16(a)		((uint32) ((uint16) (a)))
#define SUSEX16(a)		((int32)  ((uint16) (a)))

// Set/Clr Sign and Zero flag
#define TSZ(num)		TS(S, ((num) & 0x8000)); TS(Z, (!USEX16(num)))

// Clear flags
#define CLRFLAGS		GSU.vStatusReg &= ~(FLG_ALT1 | FLG_ALT2 | FLG_B); GSU.pvDreg = GSU.pvSreg = &R0

// Read current RAM-Bank
#define RAM(adr)		GSU.pvRamBank[USEX16(adr)]

// Read current ROM-Bank
#define ROM(idx)		GSU.pvRomBank[USEX16(idx)]

// Access the current value in the pipe
#define PIPE			GSU.vPipe

// Access data in the current program bank
#define PRGBANK(idx)	GSU.pvPrgBank[USEX16(idx)]

// Update pipe from ROM
#if 0
#define FETCHPIPE		{ PIPE = PRGBANK(R15); GSU.vPipeAdr = (GSU.vPrgBankReg << 16) + R15; }
#else
#define FETCHPIPE		{ PIPE = PRGBANK(R15); }
#endif

// ABS
#define ABS(x)			((x) < 0 ? -(x) : (x))

// Access source register
#define SREG			(*GSU.pvSreg)

// Access destination register
#define DREG			(*GSU.pvDreg)

#ifndef FX_DO_ROMBUFFER

// Don't read R14
#define READR14

// Don't test and/or read R14
#define TESTR14

#else

// Read R14
#define READR14			GSU.vRomBuffer = ROM(R14)

// Test and/or read R14
#define TESTR14			if (GSU.pvDreg == &R14) READR14

#endif

// Access to registers
#define R0				GSU.avReg[0]
#define R1				GSU.avReg[1]
#define R2				GSU.avReg[2]
#define R3				GSU.avReg[3]
#define R4				GSU.avReg[4]
#define R5				GSU.avReg[5]
#define R6				GSU.avReg[6]
#define R7				GSU.avReg[7]
#define R8				GSU.avReg[8]
#define R9				GSU.avReg[9]
#define R10				GSU.avReg[10]
#define R11				GSU.avReg[11]
#define R12				GSU.avReg[12]
#define R13				GSU.avReg[13]
#define R14				GSU.avReg[14]
#define R15				GSU.avReg[15]
#define SFR				GSU.vStatusReg
#define PBR				GSU.vPrgBankReg
#define ROMBR			GSU.vRomBankReg
#define RAMBR			GSU.vRamBankReg
#define CBR				GSU.vCacheBaseReg
#define SCBR			USEX8(GSU.pvRegisters[GSU_SCBR])
#define SCMR			USEX8(GSU.pvRegisters[GSU_SCMR])
#define COLR			GSU.vColorReg
#define POR				GSU.vPlotOptionReg
#define BRAMR			USEX8(GSU.pvRegisters[GSU_BRAMR])
#define VCR				USEX8(GSU.pvRegisters[GSU_VCR])
#define CFGR			USEX8(GSU.pvRegisters[GSU_CFGR])
#define CLSR			USEX8(GSU.pvRegisters[GSU_CLSR])

// Execute instruction from the pipe, and fetch next byte to the pipe
#define FX_STEP \
{ \
	uint32	vOpcode = (uint32) PIPE; \
	FETCHPIPE; \
	(*fx_OpcodeTable[(GSU.vStatusReg & 0x300) | vOpcode])(); \
}

extern void (*fx_PlotTable[]) (void);
extern void (*fx_OpcodeTable[]) (void);

// Set this define if branches are relative to the instruction in the delay slot (I think they are)
#define BRANCH_DELAY_RELATIVE

#endif