#ifndef _BURNINT_H
#define _BURNINT_H

// Burn - Arcade emulator library - internal code

// Standard headers
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "tchar.h"

#include "burn.h"
#include "burn_sound.h"
#include "joyprocess.h"

#ifdef LSB_FIRST
typedef union
{
	struct { UINT8 l,h,h2,h3; } b;
	struct { UINT16 l,h; } w;
	UINT32 d;
} PAIR;

#define BURN_ENDIAN_SWAP_INT8(x)				x
#define BURN_ENDIAN_SWAP_INT16(x)				x
#define BURN_ENDIAN_SWAP_INT32(x)				x
#define BURN_ENDIAN_SWAP_INT64(x)				x
#else

// Xbox 360 and NetBSD have byteswap 64-bit intrinsic
#if !definded(_XBOX) && !defined(__NetBSD__)
#define NO_64BIT_BYTESWAP
#endif

// psl1ght uses those intrinsics too
#if defined(__PSL1GHT__) && !defined(__CELLOS_LV2__)
#define __CELLOS_LV2__
#endif

typedef union {
  struct { UINT8 h3,h2,h,l; } b;
  struct { UINT16 h,l; } w;
  UINT32 d;
} PAIR;

#ifdef NO_64BIT_BYTESWAP
typedef union {
   UINT64 ll;
   struct { UINT32 l, h; } l;
} BYTESWAP_INT64;
#endif

// Libogc doesn't have intrinsics or ASM macros defined for this
#if defined(HW_RVL) || defined(GEKKO)
#define __sthbrx(base,index,value)      \
        __asm__ volatile ("sthbrx       %0,%1,%2" : : "r"(value), "b%"(index), "r"(base) : "memory")

#define __stwbrx(base,index,value)      \
        __asm__ volatile ("stwbrx       %0,%1,%2" : : "r"(value), "b%"(index), "r"(base) : "memory")
#endif

// Xbox 360
#if defined(_XBOX)
#define BURN_ENDIAN_SWAP_INT8(x)				(x^1)
#define BURN_ENDIAN_SWAP_INT16(x)				(_byteswap_ushort(x))
#define BURN_ENDIAN_SWAP_INT32(x)				(_byteswap_ulong(x))
#define BURN_ENDIAN_SWAP_INT64(x)				(_byteswap_uint64(x))
// PS3
#elif defined(__CELLOS_LV2__)
#include <ppu_intrinsics.h>
#include <math.h>
#define BURN_ENDIAN_SWAP_INT8(x)				(x^1)
#define BURN_ENDIAN_SWAP_INT16(x)				({uint16_t tt; __sthbrx(&tt, x); tt;})
#define BURN_ENDIAN_SWAP_INT32(x)				({uint32_t tt; __stwbrx(&tt, x); tt;})
// GC/Wii/WiiU
#elif defined(HW_RVL)
#define BURN_ENDIAN_SWAP_INT8(x)				(x^1)
#define BURN_ENDIAN_SWAP_INT16(x)				({uint16_t tt; __sthbrx(&tt, 0, x); tt;})
#define BURN_ENDIAN_SWAP_INT32(x)				({uint32_t tt; __stwbrx(&tt, 0, x); tt;})
// NetBSD
#elif defined(__NetBSD__)
#include <machine/endian.h>
#define BURN_ENDIAN_SWAP_INT8(x)				(x^1)
#define BURN_ENDIAN_SWAP_INT16(x)				(bswap16(x))
#define BURN_ENDIAN_SWAP_INT32(x)				(bswap32(x))
#define BURN_ENDIAN_SWAP_INT64(x)				(bswap64(x))
// gcc/clang
#elif defined(__GNUC__)
#define BURN_ENDIAN_SWAP_INT8(x)				(x^1)
#define BURN_ENDIAN_SWAP_INT16(x)				(__builtin_bswap16(x))
#define BURN_ENDIAN_SWAP_INT32(x)				(__builtin_bswap32(x))
// Anything else (swap16 & swap32 are kinda broken though)
#else
#define BURN_ENDIAN_SWAP_INT8(x)				(x^1)
#define BURN_ENDIAN_SWAP_INT16(x)				((((x) << 8) & 0xff00) | (((x) >> 8) & 0x00ff))
#define BURN_ENDIAN_SWAP_INT32(x)				((((x) << 24) & 0xff000000) | (((x) << 8) & 0x00ff0000) | (((x) >> 8) & 0x0000ff00) | (((x) >> 24) & 0x000000ff))
#endif

#ifdef NO_64BIT_BYTESWAP
static inline UINT64 BURN_ENDIAN_SWAP_INT64(UINT64 x)
{
	BYTESWAP_INT64 r = {0};
	r.l.l = BURN_ENDIAN_SWAP_INT32(x);
	r.l.h = BURN_ENDIAN_SWAP_INT32(x >> 32);
	return r.ll;
}
#endif

#endif

// ---------------------------------------------------------------------------
// Driver information

struct BurnDriver {
	char* szShortName;			// The filename of the zip file (without extension)
	char* szParent;				// The filename of the parent (without extension, NULL if not applicable)
	char* szBoardROM;			// The filename of the board ROMs (without extension, NULL if not applicable)
	char* szSampleName;			// The filename of the samples zip file (without extension, NULL if not applicable)
	char* szDate;

	// szFullNameA, szCommentA, szManufacturerA and szSystemA should always contain valid info
	// szFullNameW, szCommentW, szManufacturerW and szSystemW should be used only if characters or scripts are needed that ASCII can't handle
	char*    szFullNameA; char*    szCommentA; char*    szManufacturerA; char*    szSystemA;
	wchar_t* szFullNameW; wchar_t* szCommentW; wchar_t* szManufacturerW; wchar_t* szSystemW;

	INT32 Flags;			// See burn.h
	INT32 Players;		// Max number of players a game supports (so we can remove single player games from netplay)
	INT32 Hardware;		// Which type of hardware the game runs on
	INT32 Genre;
	INT32 Family;
	INT32 (*GetZipName)(char** pszName, UINT32 i);				// Function to get possible zip names
	INT32 (*GetRomInfo)(struct BurnRomInfo* pri, UINT32 i);		// Function to get the length and crc of each rom
	INT32 (*GetRomName)(char** pszName, UINT32 i, INT32 nAka);	// Function to get the possible names for each rom
	INT32 (*GetHDDInfo)(struct BurnHDDInfo* pri, UINT32 i);			// Function to get hdd info
	INT32 (*GetHDDName)(char** pszName, UINT32 i, INT32 nAka);		// Function to get the possible names for each hdd
	INT32 (*GetSampleInfo)(struct BurnSampleInfo* pri, UINT32 i);		// Function to get the sample flags
	INT32 (*GetSampleName)(char** pszName, UINT32 i, INT32 nAka);	// Function to get the possible names for each sample
	INT32 (*GetInputInfo)(struct BurnInputInfo* pii, UINT32 i);	// Function to get the input info for the game
	INT32 (*GetDIPInfo)(struct BurnDIPInfo* pdi, UINT32 i);		// Function to get the input info for the game
	INT32 (*Init)(); INT32 (*Exit)(); INT32 (*Frame)(); INT32 (*Redraw)(); INT32 (*AreaScan)(INT32 nAction, INT32* pnMin);
	UINT8* pRecalcPal; UINT32 nPaletteEntries;										// Set to 1 if the palette needs to be fully re-calculated
	INT32 nWidth, nHeight; INT32 nXAspect, nYAspect;					// Screen width, height, x/y aspect
};

#define BurnDriverD BurnDriver		// Debug status
#define BurnDriverX BurnDriver		// Exclude from build

// Standard functions for dealing with ROM and input info structures
#include "stdfunc.h"

// ---------------------------------------------------------------------------

// burn.cpp
INT32 BurnSetRefreshRate(double dRefreshRate);
INT32 BurnByteswap(UINT8* pMem, INT32 nLen);
void BurnNibbleExpand(UINT8 *source, UINT8 *dst, INT32 length, INT32 swap, UINT8 nxor);
INT32 BurnClearScreen();


// recording / netgame helper
#ifndef __LIBRETRO__
INT32 is_netgame_or_recording();
#else
inline static INT32 is_netgame_or_recording()
{
	return kNetGame;
}
#endif


// load.cpp

/*
	Flags for use with BurnLoadRomExt

	GROUP(x)		load this many bytes, then skip by nGap from start position (flag is optional)
	REVERSE			load the bytes in a group in reverse order (0,1,2,3 -> 3,2,1,0)
	INVERT			Src ^= 0xff
	BYTESWAP		change order of bytes from 0,1,2,3 to 1,0,3,2
	NIBBLES			Dest[0] = (byte & & 0xf); Dest[1] = (byte >> 4) & 0xf;
	XOR			Dest ^= Src
*/

#define LD_GROUP(x)	((x) & 0xff) // 256 - plenty
#define LD_REVERSE	(1<<8)
#define LD_INVERT	(1<<9)
#define LD_BYTESWAP	(1<<10)
#define LD_NIBBLES	(1<<11)
#define LD_XOR		(1<<12)

INT32 BurnLoadRomExt(UINT8 *Dest, INT32 i, INT32 nGap, INT32 nFlags);
INT32 BurnLoadRom(UINT8* Dest, INT32 i, INT32 nGap);
INT32 BurnXorRom(UINT8 *Dest, INT32 i, INT32 nGap);
INT32 BurnLoadBitField(UINT8* pDest, UINT8* pSrc, INT32 nField, INT32 nSrcLen);

// ---------------------------------------------------------------------------
// Plotting pixels

inline static void PutPix(UINT8* pPix, UINT32 c)
{
	if (nBurnBpp >= 4) {
		*((UINT32*)pPix) = c;
	} else {
		if (nBurnBpp == 2) {
			*((UINT16*)pPix) = (UINT16)c;
		} else {
			pPix[0] = (UINT8)(c >>  0);
			pPix[1] = (UINT8)(c >>  8);
			pPix[2] = (UINT8)(c >> 16);
		}
	}
}

// ---------------------------------------------------------------------------
// Setting up cpus for cheats

struct cpu_core_config {
	char cpu_name[32];
	void (*open)(INT32);		// cpu open
	void (*close)();		// cpu close

	UINT8 (*read)(UINT32);		// read
	void (*write)(UINT32, UINT8);	// write
	INT32 (*active)();		// active cpu
	INT32 (*totalcycles)();		// total cycles
	void (*newframe)();		// new frame'
	INT32 (*idle)(INT32);	// idle cycles

	void (*irq)(INT32, INT32, INT32);	// set irq, cpu, line, status

	INT32 (*run)(INT32);		// execute cycles
	void (*runend)();		// end run
	void (*reset)();		// reset cpu

	UINT64 nMemorySize;		// how large is our memory range?
	UINT32 nAddressXor;		// fix endianness for some cpus
};

void CpuCheatRegister(INT32 type, cpu_core_config *config);

// burn_memory.cpp
void BurnInitMemoryManager();
UINT8 *_BurnMalloc(INT32 size, char *file, INT32 line); // internal use only :)
UINT8 *BurnRealloc(void *ptr, INT32 size);
void _BurnFree(void *ptr); // internal use only :)
#define BurnFree(x) do {_BurnFree(x); x = NULL; } while (0)
#define BurnMalloc(x) _BurnMalloc(x, __FILE__, __LINE__)
void BurnExitMemoryManager();

// ---------------------------------------------------------------------------
// Sound clipping macro
#define BURN_SND_CLIP(A) ((A) < -0x8000 ? -0x8000 : (A) > 0x7fff ? 0x7fff : (A))

// sound routes
#define BURN_SND_ROUTE_LEFT			1
#define BURN_SND_ROUTE_RIGHT		2
#define BURN_SND_ROUTE_BOTH			(BURN_SND_ROUTE_LEFT | BURN_SND_ROUTE_RIGHT)
// the following 2 are only supported in ay8910 and flt_rc
#define BURN_SND_ROUTE_PANLEFT      4
#define BURN_SND_ROUTE_PANRIGHT     8

// ---------------------------------------------------------------------------
// Debug Tracker

extern UINT8 Debug_BurnTransferInitted;
extern UINT8 Debug_BurnGunInitted;
extern UINT8 Debug_BurnLedInitted;
extern UINT8 Debug_BurnShiftInitted;
extern UINT8 Debug_HiscoreInitted;
extern UINT8 Debug_GenericTilesInitted;

extern UINT8 DebugDev_8255PPIInitted;
extern UINT8 DebugDev_8257DMAInitted;
extern UINT8 DebugDev_EEPROMInitted;
extern UINT8 DebugDev_PandoraInitted;
extern UINT8 DebugDev_SeibuSndInitted;
extern UINT8 DebugDev_SknsSprInitted;
extern UINT8 DebugDev_SlapsticInitted;
extern UINT8 DebugDev_T5182Initted;
extern UINT8 DebugDev_TimeKprInitted;
extern UINT8 DebugDev_Tms34061Initted;
extern UINT8 DebugDev_V3021Initted;
extern UINT8 DebugDev_VDCInitted;

extern UINT8 DebugSnd_AY8910Initted;
extern UINT8 DebugSnd_Y8950Initted;
extern UINT8 DebugSnd_YM2151Initted;
extern UINT8 DebugSnd_YM2203Initted;
extern UINT8 DebugSnd_YM2413Initted;
extern UINT8 DebugSnd_YM2608Initted;
extern UINT8 DebugSnd_YM2610Initted;
extern UINT8 DebugSnd_YM2612Initted;
extern UINT8 DebugSnd_YM3526Initted;
extern UINT8 DebugSnd_YM3812Initted;
extern UINT8 DebugSnd_YMF278BInitted;
extern UINT8 DebugSnd_YMF262Initted;
extern UINT8 DebugSnd_YMF271Initted;
extern UINT8 DebugSnd_C6280Initted;
extern UINT8 DebugSnd_DACInitted;
extern UINT8 DebugSnd_ES5506Initted;
extern UINT8 DebugSnd_ES8712Initted;
extern UINT8 DebugSnd_FilterRCInitted;
extern UINT8 DebugSnd_ICS2115Initted;
extern UINT8 DebugSnd_IremGA20Initted;
extern UINT8 DebugSnd_K005289Initted;
extern UINT8 DebugSnd_K007232Initted;
extern UINT8 DebugSnd_K051649Initted;
extern UINT8 DebugSnd_K053260Initted;
extern UINT8 DebugSnd_K054539Initted;
extern UINT8 DebugSnd_MSM5205Initted;
extern UINT8 DebugSnd_MSM5232Initted;
extern UINT8 DebugSnd_MSM6295Initted;
extern UINT8 DebugSnd_NamcoSndInitted;
extern UINT8 DebugSnd_NESAPUSndInitted;
extern UINT8 DebugSnd_RF5C68Initted;
extern UINT8 DebugSnd_SAA1099Initted;
extern UINT8 DebugSnd_SamplesInitted;
extern UINT8 DebugSnd_SegaPCMInitted;
extern UINT8 DebugSnd_SN76496Initted;
extern UINT8 DebugSnd_UPD7759Initted;
extern UINT8 DebugSnd_VLM5030Initted;
extern UINT8 DebugSnd_X1010Initted;
extern UINT8 DebugSnd_YMZ280BInitted;

extern UINT8 DebugCPU_ARM7Initted;
extern UINT8 DebugCPU_ARMInitted;
extern UINT8 DebugCPU_H6280Initted;
extern UINT8 DebugCPU_HD6309Initted;
extern UINT8 DebugCPU_KonamiInitted;
extern UINT8 DebugCPU_M6502Initted;
extern UINT8 DebugCPU_M6800Initted;
extern UINT8 DebugCPU_M6805Initted;
extern UINT8 DebugCPU_M6809Initted;
extern UINT8 DebugCPU_S2650Initted;
extern UINT8 DebugCPU_SekInitted;
extern UINT8 DebugCPU_VezInitted;
extern UINT8 DebugCPU_ZetInitted;
extern UINT8 DebugCPU_PIC16C5XInitted;
extern UINT8 DebugCPU_I8039Initted;
extern UINT8 DebugCPU_SH2Initted;

void DebugTrackerExit();

#endif
