/*  Pcsx2 - Pc Ps2 Emulator
 *  Copyright (C) 2002-2009  Pcsx2 Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#ifndef __HW_H__
#define __HW_H__

extern u8  *psH; // hw mem

#define psHs8(mem)	(*(s8 *)&PS2MEM_HW[(mem) & 0xffff])
#define psHs16(mem)	(*(s16*)&PS2MEM_HW[(mem) & 0xffff])
#define psHs32(mem)	(*(s32*)&PS2MEM_HW[(mem) & 0xffff])
#define psHs64(mem)	(*(s64*)&PS2MEM_HW[(mem) & 0xffff])
#define psHu8(mem)	(*(u8 *)&PS2MEM_HW[(mem) & 0xffff])
#define psHu16(mem)	(*(u16*)&PS2MEM_HW[(mem) & 0xffff])
#define psHu32(mem)	(*(u32*)&PS2MEM_HW[(mem) & 0xffff])
#define psHu64(mem)	(*(u64*)&PS2MEM_HW[(mem) & 0xffff])

extern void CPU_INT( u32 n, s32 ecycle );

//////////////////////////////////////////////////////////////////////////
// Hardware FIFOs (128 bit access only!)
//
// VIF0   -- 0x10004000 -- psH[0x4000]
// VIF1   -- 0x10005000 -- psH[0x5000]
// GIF    -- 0x10006000 -- psH[0x6000]
// IPUout -- 0x10007000 -- psH[0x7000]
// IPUin  -- 0x10007010 -- psH[0x7010]

void __fastcall ReadFIFO_page_4(u32 mem, mem128_t *out);
void __fastcall ReadFIFO_page_5(u32 mem, mem128_t *out);
void __fastcall ReadFIFO_page_6(u32 mem, mem128_t *out);
void __fastcall ReadFIFO_page_7(u32 mem, mem128_t *out);

void __fastcall WriteFIFO_page_4(u32 mem, const mem128_t *value);
void __fastcall WriteFIFO_page_5(u32 mem, const mem128_t *value);
void __fastcall WriteFIFO_page_6(u32 mem, const mem128_t *value);
void __fastcall WriteFIFO_page_7(u32 mem, const mem128_t *value);


//
// --- DMA ---
//

struct DMACh {
	u32 chcr;
	u32 null0[3];
	u32 madr;
	u32 null1[3];
	u16 qwc; u16 pad;
	u32 null2[3];
	u32 tadr;
	u32 null3[3];
	u32 asr0;
	u32 null4[3];
	u32 asr1;
	u32 null5[11];
	u32 sadr;
};

// HW defines

#define RCNT0_COUNT		0x10000000
#define RCNT0_MODE		0x10000010
#define RCNT0_TARGET	0x10000020
#define RCNT0_HOLD		0x10000030

#define RCNT1_COUNT		0x10000800
#define RCNT1_MODE		0x10000810
#define RCNT1_TARGET	0x10000820
#define RCNT1_HOLD		0x10000830

#define RCNT2_COUNT		0x10001000
#define RCNT2_MODE		0x10001010
#define RCNT2_TARGET	0x10001020

#define RCNT3_COUNT		0x10001800
#define RCNT3_MODE		0x10001810
#define RCNT3_TARGET	0x10001820

#define IPU_CMD			0x10002000
#define IPU_CTRL		0x10002010
#define IPU_BP			0x10002020
#define IPU_TOP			0x10002030

#define GIF_CTRL		0x10003000
#define GIF_MODE		0x10003010
#define GIF_STAT		0x10003020
#define GIF_TAG0		0x10003040
#define GIF_TAG1		0x10003050
#define GIF_TAG2		0x10003060
#define GIF_TAG3		0x10003070
#define GIF_CNT			0x10003080
#define GIF_P3CNT		0x10003090
#define GIF_P3TAG		0x100030A0

#define GIF_FIFO		0x10006000

#define IPUout_FIFO		0x10007000
#define IPUin_FIFO		0x10007010

//VIF0
#define D0_CHCR			0x10008000
#define D0_MADR			0x10008010
#define D0_QWC			0x10008020

//VIF1
#define D1_CHCR			0x10009000
#define D1_MADR			0x10009010
#define D1_QWC			0x10009020
#define D1_TADR			0x10009030
#define D1_ASR0			0x10009040
#define D1_ASR1			0x10009050
#define D1_SADR			0x10009080

//GS
#define D2_CHCR			0x1000A000
#define D2_MADR			0x1000A010
#define D2_QWC			0x1000A020
#define D2_TADR			0x1000A030
#define D2_ASR0			0x1000A040
#define D2_ASR1			0x1000A050
#define D2_SADR			0x1000A080

//fromIPU
#define D3_CHCR			0x1000B000
#define D3_MADR			0x1000B010
#define D3_QWC			0x1000B020
#define D3_TADR			0x1000B030
#define D3_SADR			0x1000B080

//toIPU
#define D4_CHCR			0x1000B400
#define D4_MADR			0x1000B410
#define D4_QWC			0x1000B420
#define D4_TADR			0x1000B430
#define D4_SADR			0x1000B480

//SIF0
#define D5_CHCR			0x1000C000
#define D5_MADR			0x1000C010
#define D5_QWC			0x1000C020

//SIF1
#define D6_CHCR			0x1000C400
#define D6_MADR			0x1000C410
#define D6_QWC			0x1000C420

//SIF2
#define D7_CHCR			0x1000C800
#define D7_MADR			0x1000C810
#define D7_QWC			0x1000C820

//fromSPR
#define D8_CHCR			0x1000D000
#define D8_MADR			0x1000D010
#define D8_QWC			0x1000D020
#define D8_SADR			0x1000D080


#define DMAC_CTRL		0x1000E000
#define DMAC_STAT		0x1000E010
#define DMAC_PCR		0x1000E020
#define DMAC_SQWC		0x1000E030
#define DMAC_RBSR		0x1000E040
#define DMAC_RBOR		0x1000E050
#define DMAC_STADR		0x1000E060

#define INTC_STAT		0x1000F000
#define INTC_MASK		0x1000F010

#define SBUS_F220		0x1000F220
#define SBUS_SMFLG		0x1000F230
#define SBUS_F240		0x1000F240

#define DMAC_ENABLER	0x1000F520
#define DMAC_ENABLEW	0x1000F590

#define SBFLG_IOPALIVE	0x10000
#define SBFLG_IOPSYNC	0x40000

#define GS_PMODE		0x12000000
#define GS_SMODE1		0x12000010
#define GS_SMODE2		0x12000020
#define GS_SRFSH		0x12000030
#define GS_SYNCH1		0x12000040
#define GS_SYNCH2		0x12000050
#define GS_SYNCV		0x12000060
#define GS_DISPFB1		0x12000070
#define GS_DISPLAY1		0x12000080
#define GS_DISPFB2		0x12000090
#define GS_DISPLAY2		0x120000A0
#define GS_EXTBUF		0x120000B0
#define GS_EXTDATA		0x120000C0
#define GS_EXTWRITE		0x120000D0
#define GS_BGCOLOR		0x120000E0
#define GS_CSR			0x12001000
#define GS_IMR			0x12001010
#define GS_BUSDIR		0x12001040
#define GS_SIGLBLID		0x12001080

#define INTC_GS  		0
#define INTC_SBUS  		1
#define	INTC_VBLANK_S	2
#define	INTC_VBLANK_E	3
#define INTC_VIF0  		4
#define INTC_VIF1  		5
#define INTC_VU0  		6
#define INTC_VU1  		7
#define INTC_IPU  		8
#define INTC_TIM0  		9
#define INTC_TIM1  		10
#define INTC_TIM2  		11
#define INTC_TIM3  		12

#define DMAC_STAT_SIS (1<<13) // stall condition
#define DMAC_STAT_MEIS (1<<14) // mfifo empty
#define DMAC_STAT_BEIS (1<<15) // bus error
#define DMAC_STAT_SIM (1<<29) // stall mask
#define DMAC_STAT_MEIM (1<<30) // mfifo mask

#define DMAC_VIF0		0
#define DMAC_VIF1		1
#define DMAC_GIF		2
#define DMAC_FROM_IPU	3
#define DMAC_TO_IPU		4
#define DMAC_SIF0		5
#define DMAC_SIF1		6
#define DMAC_SIF2		7
#define DMAC_FROM_SPR	8
#define DMAC_TO_SPR		9
#define DMAC_ERROR		15

#define VIF0_STAT_VPS_W (1)
#define VIF0_STAT_VPS_D (2)
#define VIF0_STAT_VPS_T (3)
#define VIF0_STAT_VPS	(3)
#define VIF0_STAT_VEW	(1<<2)
#define VIF0_STAT_MRK	(1<<6)
#define VIF0_STAT_DBF	(1<<7)
#define VIF0_STAT_VSS	(1<<8)
#define VIF0_STAT_VFS	(1<<9)
#define VIF0_STAT_VIS	(1<<10)
#define VIF0_STAT_INT	(1<<11)
#define VIF0_STAT_ER0	(1<<12)
#define VIF0_STAT_ER1	(1<<13)

#define VIF1_STAT_VPS_W (1)
#define VIF1_STAT_VPS_D (2)
#define VIF1_STAT_VPS_T (3)
#define VIF1_STAT_VPS	(3)
#define VIF1_STAT_VEW	(1<<2)
#define VIF1_STAT_VGW	(1<<3)
#define VIF1_STAT_MRK	(1<<6)
#define VIF1_STAT_DBF	(1<<7)
#define VIF1_STAT_VSS	(1<<8)
#define VIF1_STAT_VFS	(1<<9)
#define VIF1_STAT_VIS	(1<<10)
#define VIF1_STAT_INT	(1<<11)
#define VIF1_STAT_ER0	(1<<12)
#define VIF1_STAT_ER1	(1<<13)
#define VIF1_STAT_FDR	(1<<23)

//DMA interrupts & masks
#define BEISintr (0x8000)
#define VIF0intr (0x10001)
#define VIF1intr (0x20002)
#define GIFintr  (0x40004)
#define IPU0intr (0x80008)
#define IPU1intr (0x100010)
#define SIF0intr (0x200020)
#define SIF1intr (0x400040)
#define SIF2intr (0x800080)
#define SPR0intr (0x1000100)
#define SPR1intr (0x2000200)
#define SISintr  (0x20002000)
#define MEISintr (0x40004000)

#define DMAend(dma, num) { \
	dma->chcr &= ~0x100; \
	psHu32(DMAC_STAT)|= 1<<num; \
	return; \
}

#define DMAerror(dma, num) { \
	psHu32(DMAC_STAT)|= 1<<15; /* BUS error */ \
	DMAend(dma, num); \
}

#define _dmaGetAddr(dma, ptr, addr, num) \
	ptr = (u32*)dmaGetAddr(addr); \
	if (ptr == NULL) DMAerror(dma, num);

#ifdef PCSX2_VIRTUAL_MEM

#define dmaGetAddrBase(addr) (((addr) & 0x80000000) ? (void*)&PS2MEM_SCRATCH[(addr) & 0x3ff0] : (void*)(PS2MEM_BASE+TRANSFORM_ADDR(addr)))

#ifdef _WIN32
extern PSMEMORYMAP* memLUT;
#endif

// VM-version of dmaGetAddr -- Left in for references purposes for now (air)
static __forceinline u8* dmaGetAddr(u32 mem)
{
	u8* p, *pbase;
	mem &= ~0xf;

	if( (mem&0xffff0000) == 0x50000000 ) {// reserved scratch pad mem
		SysPrintf("dmaGetAddr: reserved scratch pad mem\n");
		return NULL;//(u8*)&PS2MEM_SCRATCH[(mem) & 0x3ff0];
	}

	p = (u8*)dmaGetAddrBase(mem);

#ifdef _WIN32	
	// do manual LUT since IPU/SPR seems to use addrs 0x3000xxxx quite often
    // linux doesn't suffer from this because it has better vm support
	if( memLUT[ (p-PS2MEM_BASE)>>12 ].aPFNs == NULL ) {
		SysPrintf("dmaGetAddr: memLUT PFN warning\n");
		return NULL;//p;
	}

	pbase = (u8*)memLUT[ (p-PS2MEM_BASE)>>12 ].aVFNs[0];
	if( pbase != NULL )
		p = pbase + ((u32)p&0xfff);
#endif

	return p;
}

#else

// Note: Dma addresses are guaranteed to be aligned to 16 bytes (128 bits)
static __forceinline void *dmaGetAddr(u32 addr) {
	u8 *ptr;

//	if (addr & 0xf) { DMA_LOG("*PCSX2*: DMA address not 128bit aligned: %8.8x\n", addr); }

	if (addr & 0x80000000) {	//  teh sux why the f00k 0xE0000000
		return (void*)&psS[addr & 0x3ff0];
	}

	ptr = (u8*)vtlb_GetPhyPtr(addr&0x1FFFFFF0);
	if (ptr == NULL) {
		Console::Error("*PCSX2*: DMA error: %8.8x", params addr);
		return NULL;
	}
	return ptr;
}

#endif

void hwInit();
void hwReset();
void hwShutdown();

// hw read functions
extern u8   hwRead8 (u32 mem);
extern u16  hwRead16(u32 mem);

extern mem32_t __fastcall hwRead32_page_00(u32 mem);
extern mem32_t __fastcall hwRead32_page_01(u32 mem);
extern mem32_t __fastcall hwRead32_page_02(u32 mem);
extern mem32_t __fastcall hwRead32_page_0F(u32 mem);
extern mem32_t __fastcall hwRead32_page_0F_INTC_HACK(u32 mem);
extern mem32_t __fastcall hwRead32_generic(u32 mem);

extern void __fastcall hwRead64_page_00(u32 mem, mem64_t* result );
extern void __fastcall hwRead64_page_01(u32 mem, mem64_t* result );
extern void __fastcall hwRead64_page_02(u32 mem, mem64_t* result );
extern void __fastcall hwRead64_generic_INTC_HACK(u32 mem, mem64_t *out);
extern void __fastcall hwRead64_generic(u32 mem, mem64_t* result );

extern void __fastcall hwRead128_page_00(u32 mem, mem128_t* result );
extern void __fastcall hwRead128_page_01(u32 mem, mem128_t* result );
extern void __fastcall hwRead128_page_02(u32 mem, mem128_t* result );
extern void __fastcall hwRead128_generic(u32 mem, mem128_t *out);

// hw write functions
extern void hwWrite8 (u32 mem, u8  value);
extern void hwWrite16(u32 mem, u16 value);

extern void __fastcall hwWrite32_page_00( u32 mem, u32 value );
extern void __fastcall hwWrite32_page_01( u32 mem, u32 value );
extern void __fastcall hwWrite32_page_02( u32 mem, u32 value );
extern void __fastcall hwWrite32_page_03( u32 mem, u32 value );
extern void __fastcall hwWrite32_page_0B( u32 mem, u32 value );
extern void __fastcall hwWrite32_page_0E( u32 mem, u32 value );
extern void __fastcall hwWrite32_page_0F( u32 mem, u32 value );
extern void __fastcall hwWrite32_generic( u32 mem, u32 value );

extern void __fastcall hwWrite64_page_02( u32 mem, const mem64_t* srcval );
extern void __fastcall hwWrite64_page_03( u32 mem, const mem64_t* srcval );
extern void __fastcall hwWrite64_page_0E( u32 mem, const mem64_t* srcval );
extern void __fastcall hwWrite64_generic( u32 mem, const mem64_t* srcval );

extern void __fastcall hwWrite128_generic(u32 mem, const mem128_t *srcval);

// legacy - used for debugging sometimes
//extern mem32_t __fastcall hwRead32(u32 mem);
//extern void __fastcall hwWrite32(u32 mem, u32 value);

//extern void hwWrite64(u32 mem, u64 value);
//extern void hwWrite128(u32 mem, const u64 *value);

void hwIntcIrq(int n);
void hwDmacIrq(int n);

int  hwMFIFORead(u32 addr, u8 *data, u32 size);
int  hwMFIFOWrite(u32 addr, u8 *data, u32 size);

int  hwDmacSrcChainWithStack(DMACh *dma, int id);
int  hwDmacSrcChain(DMACh *dma, int id);

int hwConstRead8 (u32 x86reg, u32 mem, u32 sign);
int hwConstRead16(u32 x86reg, u32 mem, u32 sign);
int hwConstRead32(u32 x86reg, u32 mem);
void hwConstRead64(u32 mem, int mmreg);
void hwConstRead128(u32 mem, int xmmreg);

void hwConstWrite8 (u32 mem, int mmreg);
void hwConstWrite16(u32 mem, int mmreg);
void hwConstWrite32(u32 mem, int mmreg);
void hwConstWrite64(u32 mem, int mmreg);
void hwConstWrite128(u32 mem, int xmmreg);

extern void  intcInterrupt();
extern void  dmacInterrupt();

extern int rdram_devices, rdram_sdevid;

#endif /* __HW_H__ */
