#pragma once

namespace Latte
{
	// common enums
	enum class E_DIM : uint32 // shared between Latte backend and GX2 code
	{
		DIM_1D = 0,
		DIM_2D = 1,
		DIM_3D = 2,
		DIM_CUBEMAP = 3,
		DIM_1D_ARRAY = 4,
		DIM_2D_ARRAY = 5,
		DIM_2D_MSAA = 6,
		DIM_2D_ARRAY_MSAA = 7
	};

	enum class E_AAMODE : uint32 // shared between Latte backend and GX2 code
	{
		AA_1X = 0,
		AA_2X = 1,
		AA_4X = 2,
		AA_8X = 3,
	};

	enum class E_HWTILEMODE
	{
		TM_LINEAR_GENERAL = 0,  // linear (pitch must be aligned to 8?)
		TM_LINEAR_ALIGNED = 1,  // pitch must be multiple of 64 pixels?

		TM_1D_TILED_THIN1 = 2,  // no macro tiling, 8x8 micro tiles
		TM_1D_TILED_THICK = 3,  // no macro tiling, 8x8x4 micro tiles

		TM_2D_TILED_THIN1 = 4,  // 4x2
		TM_2D_TILED_THIN2 = 5,  // 2x4
		TM_2D_TILED_THIN4 = 6,  // 1x8
		TM_2D_TILED_THICK = 7,  // 4x2x1

		TM_2B_TILED_THIN1 = 8,  // 4x2
		TM_2B_TILED_THIN2 = 9,  // 2x4
		TM_2B_TILED_THIN4 = 10, // 1x8
		TM_2B_TILED_THICK = 11, // 4x2x1

		TM_3D_TILED_THIN1 = 12, // 4x2
		TM_3D_TILED_THICK = 13, // 4x2x1

		TM_3B_TILED_THIN1 = 14, // 4x2
		TM_3B_TILED_THICK = 15, // 4x2x1
	};

	enum class E_GX2TILEMODE : uint32
	{
		// same as E_TILEMODE but contains additional options with special meaning
		TM_LINEAR_GENERAL = 0,
		TM_LINEAR_ALIGNED = 1, 

		// micro-tiled
		TM_1D_TILED_THIN1 = 2, 
		TM_1D_TILED_THICK = 3, 

		// macro-tiled
		TM_2D_TILED_THIN1 = 4, 
		TM_2D_TILED_THIN2 = 5, 
		TM_2D_TILED_THIN4 = 6, 
		TM_2D_TILED_THICK = 7, 

		TM_2B_TILED_THIN1 = 8, 
		TM_2B_TILED_THIN2 = 9, 
		TM_2B_TILED_THIN4 = 10,
		TM_2B_TILED_THICK = 11,

		TM_3D_TILED_THIN1 = 12,
		TM_3D_TILED_THICK = 13,

		TM_3B_TILED_THIN1 = 14,
		TM_3B_TILED_THICK = 15,

		// special
		TM_LINEAR_SPECIAL = 16,
		TM_32_SPECIAL	  = 32,
	};

	inline E_HWTILEMODE MakeHWTileMode(const E_GX2TILEMODE gx2Tilemode)
	{
		return (E_HWTILEMODE)((uint32)gx2Tilemode & 0xF);
	}

	inline E_GX2TILEMODE MakeGX2TileMode(const E_HWTILEMODE hwTilemode)
	{
		return (E_GX2TILEMODE)hwTilemode;
	}

	inline bool TM_IsMacroTiled(const E_HWTILEMODE tm)
	{
		return (uint32)tm >= 4;
	}

	inline bool TM_IsMacroTiled(const E_GX2TILEMODE tm)
	{
		return (uint32)tm >= 4 && (uint32)tm != 16;
	}

	inline bool TM_IsBankSwapped(const E_HWTILEMODE tileMode)
	{
		return
			tileMode == E_HWTILEMODE::TM_2B_TILED_THIN1 ||
			tileMode == E_HWTILEMODE::TM_2B_TILED_THIN2 ||
			tileMode == E_HWTILEMODE::TM_2B_TILED_THIN4 ||
			tileMode == E_HWTILEMODE::TM_2B_TILED_THICK ||
			tileMode == E_HWTILEMODE::TM_3B_TILED_THIN1 ||
			tileMode == E_HWTILEMODE::TM_3B_TILED_THICK;
	}

	enum class E_HWSURFFMT
	{
		INVALID_FORMAT = 0,
		// hardware formats only
		HWFMT_8 = 0x1,
		HWFMT_4_4 = 0x2,
		HWFMT_3_3_2 = 0x3,
		HWFMT_16 = 0x5,
		HWFMT_16_FLOAT = 0x6,
		HWFMT_8_8 = 0x7,
		HWFMT_5_6_5 = 0x8,
		HWFMT_6_5_5 = 0x9,
		HWFMT_1_5_5_5 = 0xA,
		HWFMT_4_4_4_4 = 0xB,
		HWFMT_5_5_5_1 = 0xC,
		HWFMT_32 = 0xD,
		HWFMT_32_FLOAT = 0xE,
		HWFMT_16_16 = 0xF,
		HWFMT_16_16_FLOAT = 0x10,
		HWFMT_8_24 = 0x11,
		HWFMT_8_24_FLOAT = 0x12,
		HWFMT_24_8 = 0x13,
		HWFMT_24_8_FLOAT = 0x14,
		HWFMT_10_11_11 = 0x15,
		HWFMT_10_11_11_FLOAT = 0x16,
		HWFMT_11_11_10 = 0x17,
		HWFMT_11_11_10_FLOAT = 0x18,
		HWFMT_2_10_10_10 = 0x19,
		HWFMT_8_8_8_8 = 0x1A,
		HWFMT_10_10_10_2 = 0x1B,
		HWFMT_X24_8_32_FLOAT = 0x1C,
		HWFMT_32_32 = 0x1D,
		HWFMT_32_32_FLOAT = 0x1E,
		HWFMT_16_16_16_16 = 0x1F,
		HWFMT_16_16_16_16_FLOAT = 0x20,
		HWFMT_32_32_32_32 = 0x22,
		HWFMT_32_32_32_32_FLOAT = 0x23,
		HWFMT_BC1 = 0x31,
		HWFMT_BC2 = 0x32,
		HWFMT_BC3 = 0x33,
		HWFMT_BC4 = 0x34,
		HWFMT_BC5 = 0x35,

		// these formats exist in R600/R700 documentation, but GX2 doesn't seem to handle them. Are they supported?
		U_HWFMT_BC6 = 0x36,
		U_HWFMT_BC7 = 0x37,
		U_HWFMT_32_32_32 = 0x2F,
		U_HWFMT_32_32_32_FLOAT = 0x30,


	};

	enum class E_GX2SURFFMT // GX2 surface format
	{
		INVALID_FORMAT = 0,
		// base hardware formats (shared with E_HWSURFFMT)
		HWFMT_8 = 0x1,
		HWFMT_4_4 = 0x2,
		HWFMT_3_3_2 = 0x3,
		HWFMT_16 = 0x5,
		HWFMT_16_FLOAT = 0x6,
		HWFMT_8_8 = 0x7,
		HWFMT_5_6_5 = 0x8,
		HWFMT_6_5_5 = 0x9,
		HWFMT_1_5_5_5 = 0xA,
		HWFMT_4_4_4_4 = 0xB,
		HWFMT_5_5_5_1 = 0xC,
		HWFMT_32 = 0xD,
		HWFMT_32_FLOAT = 0xE,		
		HWFMT_16_16 = 0xF,
		HWFMT_16_16_FLOAT = 0x10,
		HWFMT_8_24 = 0x11,
		HWFMT_8_24_FLOAT = 0x12,
		HWFMT_24_8 = 0x13,
		HWFMT_24_8_FLOAT = 0x14,
		HWFMT_10_11_11 = 0x15,
		HWFMT_10_11_11_FLOAT = 0x16,
		HWFMT_11_11_10 = 0x17,
		HWFMT_11_11_10_FLOAT = 0x18,
		HWFMT_2_10_10_10 = 0x19,
		HWFMT_8_8_8_8 = 0x1A,
		HWFMT_10_10_10_2 = 0x1B,
		HWFMT_X24_8_32_FLOAT = 0x1C,
		HWFMT_32_32 = 0x1D,
		HWFMT_32_32_FLOAT = 0x1E,
		HWFMT_16_16_16_16 = 0x1F,
		HWFMT_16_16_16_16_FLOAT = 0x20,
		HWFMT_32_32_32_32 = 0x22,
		HWFMT_32_32_32_32_FLOAT = 0x23,
		HWFMT_BC1 = 0x31,
		HWFMT_BC2 = 0x32,
		HWFMT_BC3 = 0x33,
		HWFMT_BC4 = 0x34,
		HWFMT_BC5 = 0x35,

		// GX2 extra format bits
		FMT_BIT_INT = 0x100,
		FMT_BIT_SIGNED = 0x200,
		FMT_BIT_SRGB = 0x400,
		FMT_BIT_FLOAT = 0x800,

		// GX2 formats
		R4_G4_UNORM = HWFMT_4_4,

		R5_G6_B5_UNORM = HWFMT_5_6_5,
		R5_G5_B5_A1_UNORM = HWFMT_1_5_5_5,
		R4_G4_B4_A4_UNORM = HWFMT_4_4_4_4,
		A1_B5_G5_R5_UNORM = HWFMT_5_5_5_1,

		R8_UNORM = HWFMT_8,
		R8_SNORM = (HWFMT_8 | FMT_BIT_SIGNED),
		R8_UINT = (HWFMT_8 | FMT_BIT_INT),
		R8_SINT = (HWFMT_8 | FMT_BIT_INT | FMT_BIT_SIGNED),

		R8_G8_UNORM = HWFMT_8_8,
		R8_G8_SNORM = (HWFMT_8_8 | FMT_BIT_SIGNED),
		R8_G8_UINT = (HWFMT_8_8 | FMT_BIT_INT),
		R8_G8_SINT = (HWFMT_8_8 | FMT_BIT_INT | FMT_BIT_SIGNED),

		R8_G8_B8_A8_UNORM = HWFMT_8_8_8_8,
		R8_G8_B8_A8_SNORM = (HWFMT_8_8_8_8 | FMT_BIT_SIGNED),
		R8_G8_B8_A8_UINT = (HWFMT_8_8_8_8 | FMT_BIT_INT),
		R8_G8_B8_A8_SINT = (HWFMT_8_8_8_8 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R8_G8_B8_A8_SRGB = (HWFMT_8_8_8_8 | FMT_BIT_SRGB),

		R10_G10_B10_A2_UNORM = HWFMT_2_10_10_10,
		R10_G10_B10_A2_SNORM = (HWFMT_2_10_10_10 | FMT_BIT_SIGNED),
		R10_G10_B10_A2_UINT = (HWFMT_2_10_10_10 | FMT_BIT_INT),
		R10_G10_B10_A2_SINT = (HWFMT_2_10_10_10 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R10_G10_B10_A2_SRGB = (HWFMT_2_10_10_10 | FMT_BIT_SRGB),

		A2_B10_G10_R10_UNORM = HWFMT_10_10_10_2,
		A2_B10_G10_R10_UINT = (HWFMT_10_10_10_2 | FMT_BIT_INT),

		R16_UNORM = HWFMT_16,
		R16_SNORM = (HWFMT_16 | FMT_BIT_SIGNED),
		R16_UINT = (HWFMT_16 | FMT_BIT_INT),
		R16_SINT = (HWFMT_16 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R16_FLOAT = (HWFMT_16_FLOAT | FMT_BIT_FLOAT),

		R16_G16_UNORM = HWFMT_16_16,
		R16_G16_SNORM = (HWFMT_16_16 | FMT_BIT_SIGNED),
		R16_G16_UINT = (HWFMT_16_16 | FMT_BIT_INT),
		R16_G16_SINT = (HWFMT_16_16 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R16_G16_FLOAT = (HWFMT_16_16_FLOAT | FMT_BIT_FLOAT),

		R16_G16_B16_A16_UNORM = HWFMT_16_16_16_16,
		R16_G16_B16_A16_SNORM = (HWFMT_16_16_16_16 | FMT_BIT_SIGNED),
		R16_G16_B16_A16_UINT = (HWFMT_16_16_16_16 | FMT_BIT_INT),
		R16_G16_B16_A16_SINT = (HWFMT_16_16_16_16 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R16_G16_B16_A16_FLOAT = (HWFMT_16_16_16_16_FLOAT | FMT_BIT_FLOAT),


		R24_X8_UNORM = (HWFMT_8_24),
		R24_X8_FLOAT = (HWFMT_8_24 | FMT_BIT_FLOAT),
		X24_G8_UINT = (HWFMT_8_24 | FMT_BIT_INT),
		R32_X8_FLOAT = (HWFMT_X24_8_32_FLOAT | FMT_BIT_FLOAT), // R32_X8_FLOAT
		X32_G8_UINT_X24 = (HWFMT_X24_8_32_FLOAT | FMT_BIT_INT), // X32_G8_UINT

		R11_G11_B10_FLOAT = (HWFMT_10_11_11_FLOAT | FMT_BIT_FLOAT),

		// 32bit component formats do not support SNORM/UNORM (at least GX2 doesnt expose it)
		R32_UINT = (HWFMT_32 | FMT_BIT_INT),
		R32_SINT = (HWFMT_32 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R32_FLOAT = (HWFMT_32_FLOAT | FMT_BIT_FLOAT),

		R32_G32_UINT = (HWFMT_32_32 | FMT_BIT_INT),
		R32_G32_SINT = (HWFMT_32_32 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R32_G32_FLOAT = (HWFMT_32_32_FLOAT | FMT_BIT_FLOAT),

		R32_G32_B32_A32_UINT = (HWFMT_32_32_32_32 | FMT_BIT_INT),
		R32_G32_B32_A32_SINT = (HWFMT_32_32_32_32 | FMT_BIT_INT | FMT_BIT_SIGNED),
		R32_G32_B32_A32_FLOAT = (HWFMT_32_32_32_32_FLOAT | FMT_BIT_FLOAT),
	
		// depth
		D24_S8_UNORM = (HWFMT_8_24),
		D24_S8_FLOAT = (HWFMT_8_24 | FMT_BIT_FLOAT),
		D32_S8_FLOAT = (HWFMT_X24_8_32_FLOAT | FMT_BIT_FLOAT),
		D16_UNORM = HWFMT_16,
		D32_FLOAT = (HWFMT_32_FLOAT | FMT_BIT_FLOAT),

		// compressed formats
		BC1_UNORM = (HWFMT_BC1),
		BC1_SRGB = (HWFMT_BC1 | FMT_BIT_SRGB),
		BC2_UNORM = (HWFMT_BC2),
		BC2_SRGB = (HWFMT_BC2 | FMT_BIT_SRGB),
		BC3_UNORM = (HWFMT_BC3),
		BC3_SRGB = (HWFMT_BC3 | FMT_BIT_SRGB),
		BC4_UNORM = (HWFMT_BC4),
		BC4_SNORM = (HWFMT_BC4 | FMT_BIT_SIGNED),
		BC5_UNORM = (HWFMT_BC5),
		BC5_SNORM = (HWFMT_BC5 | FMT_BIT_SIGNED),

		// special
		NV12_UNORM = 0x81,
	};
	DEFINE_ENUM_FLAG_OPERATORS(E_GX2SURFFMT);

	inline uint32 GetFormatBits(const Latte::E_HWSURFFMT hwFmt)
	{
		const uint8 sBitsTable[0x40] = {
		0x00,0x08,0x08,0x00,0x00,0x10,0x10,0x10,
		0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x20,
		0x20,0x20,0x00,0x20,0x00,0x00,0x20,0x00,
		0x00,0x20,0x20,0x20,0x40,0x40,0x40,0x40,
		0x40,0x00,0x80,0x80,0x00,0x00,0x00,0x10,
		0x10,0x20,0x20,0x20,0x00,0x00,0x00,0x60,
		0x60,0x40,0x80,0x80,0x40,0x80,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
		};
		cemu_assert((uint32)hwFmt < 0x40);
		return sBitsTable[(uint32)hwFmt];
	}

	inline uint32 GetFormatBits(const Latte::E_GX2SURFFMT gx2Fmt)
	{
		return GetFormatBits((Latte::E_HWSURFFMT)((uint32)gx2Fmt & 0x3F));
	}

	inline E_HWSURFFMT GetHWFormat(E_GX2SURFFMT format)
	{
		return (E_HWSURFFMT)((uint32)format & 0x3F);
	}

	inline bool IsCompressedFormat(Latte::E_HWSURFFMT format)
	{
		return (uint32)format >= 0x31 && (uint32)format <= 0x35;
	}

	inline bool IsCompressedFormat(Latte::E_GX2SURFFMT format)
	{
		return IsCompressedFormat((Latte::E_HWSURFFMT)((uint32)format & 0x3F));
	}

	inline bool IsMSAA(Latte::E_DIM dim)
	{
		return dim == E_DIM::DIM_2D_MSAA || dim == E_DIM::DIM_2D_ARRAY_MSAA;
	}

	enum GPU_LIMITS
	{
		NUM_VERTEX_BUFFERS = 16,
		NUM_TEXTURES_PER_STAGE = 18, 
		NUM_SAMPLERS_PER_STAGE = 18, // is this 16 or 18?
		NUM_COLOR_ATTACHMENTS = 8,
	};

	enum REGADDR
	{
		VGT_PRIMITIVE_TYPE					= 0x2256,

		// each stage has 12 sets of 4 border color registers
		TD_PS_SAMPLER0_BORDER_RED			= 0x2900,
		TD_PS_SAMPLER0_BORDER_GREEN			= 0x2901,
		TD_PS_SAMPLER0_BORDER_BLUE			= 0x2902,
		TD_PS_SAMPLER0_BORDER_ALPHA			= 0x2903,

		TD_VS_SAMPLER0_BORDER_RED			= 0x2980,
		TD_VS_SAMPLER0_BORDER_GREEN			= 0x2981,
		TD_VS_SAMPLER0_BORDER_BLUE			= 0x2982,
		TD_VS_SAMPLER0_BORDER_ALPHA			= 0x2983,

		TD_GS_SAMPLER0_BORDER_RED			= 0x2A00,
		TD_GS_SAMPLER0_BORDER_GREEN			= 0x2A01,
		TD_GS_SAMPLER0_BORDER_BLUE			= 0x2A02,
		TD_GS_SAMPLER0_BORDER_ALPHA			= 0x2A03,

		DB_STENCIL_CLEAR					= 0xA00A,
		DB_DEPTH_CLEAR						= 0xA00B,

		CB_TARGET_MASK                      = 0xA08E,

		PA_SC_GENERIC_SCISSOR_TL			= 0xA090,
		PA_SC_GENERIC_SCISSOR_BR			= 0xA091,

		SQ_VTX_SEMANTIC_0					= 0xA0E0,
		SQ_VTX_SEMANTIC_31					= 0xA0FF,

		VGT_MULTI_PRIM_IB_RESET_INDX		= 0xA103,
		SX_ALPHA_TEST_CONTROL				= 0xA104,
		CB_BLEND_RED						= 0xA105,
		CB_BLEND_GREEN						= 0xA106,
		CB_BLEND_BLUE						= 0xA107,
		CB_BLEND_ALPHA						= 0xA108,

		DB_STENCILREFMASK					= 0xA10C,
		DB_STENCILREFMASK_BF				= 0xA10D,
		SX_ALPHA_REF						= 0xA10E,
		PA_CL_VPORT_XSCALE					= 0xA10F,
		PA_CL_VPORT_XOFFSET					= 0xA110,
		PA_CL_VPORT_YSCALE					= 0xA111,
		PA_CL_VPORT_YOFFSET					= 0xA112,
		PA_CL_VPORT_ZSCALE					= 0xA113,
		PA_CL_VPORT_ZOFFSET					= 0xA114,

		SPI_VS_OUT_ID_0						= 0xA185,

		SPI_VS_OUT_CONFIG					= 0xA1B1,

		CB_BLEND0_CONTROL					= 0xA1E0, // first
		CB_BLEND7_CONTROL					= 0xA1E7, // last

		DB_DEPTH_CONTROL					= 0xA200,

		CB_COLOR_CONTROL					= 0xA202,

		PA_CL_CLIP_CNTL						= 0xA204,
		PA_SU_SC_MODE_CNTL					= 0xA205,
		PA_CL_VTE_CNTL						= 0xA206,
		PA_CL_VS_OUT_CNTL					= 0xA207,

		// shader program descriptors:
		SQ_PGM_START_PS						= 0xA210,
		SQ_PGM_RESOURCES_PS					= 0xA214,
		SQ_PGM_EXPORTS_PS					= 0xA215,
		SQ_PGM_START_VS						= 0xA216,
		SQ_PGM_RESOURCES_VS					= 0xA21A,
		SQ_PGM_START_GS 					= 0xA21B,
		SQ_PGM_RESOURCES_GS					= 0xA21F,
		SQ_PGM_START_ES						= 0xA220,
		SQ_PGM_RESOURCES_ES					= 0xA224,
		SQ_PGM_START_FS						= 0xA225,
		SQ_PGM_RESOURCES_FS					= 0xA229,

		SQ_VTX_SEMANTIC_CLEAR				= 0xA238,

		PA_SU_POINT_SIZE					= 0xA280,
		PA_SU_POINT_MINMAX					= 0xA281,

		VGT_GS_MODE							= 0xA290,

		VGT_DMA_INDEX_TYPE					= 0xA29F, // todo - verify offset

		VGT_PRIMITIVEID_EN					= 0xA2A1,

		VGT_MULTI_PRIM_IB_RESET_EN 			= 0xA2A5,

		VGT_INSTANCE_STEP_RATE_0			= 0xA2A8,
		VGT_INSTANCE_STEP_RATE_1			= 0xA2A9,

		VGT_STRMOUT_BUFFER_SIZE_0			= 0xA2B4,
		VGT_STRMOUT_VTX_STRIDE_0			= 0xA2B5,
		VGT_STRMOUT_BUFFER_BASE_0			= 0xA2B6,
		VGT_STRMOUT_BUFFER_OFFSET_0			= 0xA2B7,
		VGT_STRMOUT_BUFFER_SIZE_1			= 0xA2B8,
		VGT_STRMOUT_VTX_STRIDE_1			= 0xA2B9,
		VGT_STRMOUT_BUFFER_BASE_1			= 0xA2BA,
		VGT_STRMOUT_BUFFER_OFFSET_1			= 0xA2BB,
		VGT_STRMOUT_BUFFER_SIZE_2			= 0xA2BC,
		VGT_STRMOUT_VTX_STRIDE_2			= 0xA2BD,
		VGT_STRMOUT_BUFFER_BASE_2			= 0xA2BE,
		VGT_STRMOUT_BUFFER_OFFSET_2			= 0xA2BF,
		VGT_STRMOUT_BUFFER_SIZE_3			= 0xA2C0,
		VGT_STRMOUT_VTX_STRIDE_3			= 0xA2C1,
		VGT_STRMOUT_BUFFER_BASE_3			= 0xA2C2,
		VGT_STRMOUT_BUFFER_OFFSET_3			= 0xA2C3,
		VGT_STRMOUT_BASE_OFFSET_0			= 0xA2C4,
		VGT_STRMOUT_BASE_OFFSET_1			= 0xA2C5,
		VGT_STRMOUT_BASE_OFFSET_2			= 0xA2C6,
		VGT_STRMOUT_BASE_OFFSET_3			= 0xA2C7,
		VGT_STRMOUT_BUFFER_EN				= 0xA2C8,

		// HiZ early stencil test?
		DB_SRESULTS_COMPARE_STATE0			= 0xA34A,
		DB_SRESULTS_COMPARE_STATE1			= 0xA34B,

		PA_SU_POLY_OFFSET_CLAMP				= 0xA37F,
		PA_SU_POLY_OFFSET_FRONT_SCALE		= 0xA380,
		PA_SU_POLY_OFFSET_FRONT_OFFSET		= 0xA381,
		PA_SU_POLY_OFFSET_BACK_SCALE		= 0xA382,
		PA_SU_POLY_OFFSET_BACK_OFFSET		= 0xA383,

		// texture units
		SQ_TEX_RESOURCE_WORD0_N_PS			= 0xE000,
		SQ_TEX_RESOURCE_WORD0_N_VS			= 0xE460,
		SQ_TEX_RESOURCE_WORD0_N_GS			= 0xE930,
		SQ_TEX_RESOURCE_WORD_FIRST			= SQ_TEX_RESOURCE_WORD0_N_PS,
		SQ_TEX_RESOURCE_WORD_LAST			= (SQ_TEX_RESOURCE_WORD0_N_GS + GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7 - 1),
		// there are 54 samplers with 3 registers each. 18 (actually only 16?) per stage. For stage indices see SAMPLER_BASE_INDEX_*
		SQ_TEX_SAMPLER_WORD0_0				= 0xF000,
		SQ_TEX_SAMPLER_WORD1_0				= 0xF001,
		SQ_TEX_SAMPLER_WORD2_0				= 0xF002,


	};

	inline constexpr int SAMPLER_BASE_INDEX_PIXEL = 0;
	inline constexpr int SAMPLER_BASE_INDEX_VERTEX = 18;
	inline constexpr int SAMPLER_BASE_INDEX_GEOMETRY = 36;

#define LATTE_BITFIELD(__regname, __bitIndex, __bitWidth) \
auto& set_##__regname(uint32 newValue) \
{	\
	cemu_assert_debug(newValue < (1u << (__bitWidth))); \
	v &= ~((((1u << (__bitWidth)) - 1u) << (__bitIndex))); \
	v |= (newValue << (__bitIndex)); \
    return *this; \
} \
uint32 get_##__regname() const \
{	\
	return (v >> (__bitIndex))&((1u << (__bitWidth)) - 1u); \
}

#define LATTE_BITFIELD_SIGNED(__regname, __bitIndex, __bitWidth) \
auto& set_##__regname(sint32 newValue) \
{	\
	cemu_assert_debug(newValue < (1 << ((__bitWidth)-1))); \
	cemu_assert_debug(newValue >= -(1 << ((__bitWidth)-1))); \
	v &= ~((((1u << (__bitWidth)) - 1u) << (__bitIndex))); \
	v |= (((uint32)newValue & ((1u << (__bitWidth)) - 1u)) << (__bitIndex)); \
    return *this; \
} \
sint32 get_##__regname() const \
{	\
    sint32 r = (v >> (__bitIndex))&((1u << (__bitWidth)) - 1u); \
    r = (r << (32 - (__bitWidth))); \
    r = (r >> (32 - (__bitWidth))); \
	return r; \
}

#define LATTE_BITFIELD_BOOL(__regname, __bitIndex) \
auto& set_##__regname(bool newValue) \
{	\
	if(newValue) \
	v |= (1u << (__bitIndex)); \
	else \
	v &= ~(1u << (__bitIndex)); \
    return *this; \
} \
bool get_##__regname() const \
{	\
	return (v&(1u << (__bitIndex))) != 0; \
}

#define LATTE_BITFIELD_TYPED(__regname, __bitIndex, __bitWidth, __typename) \
auto& set_##__regname(__typename newValue) \
{	\
	cemu_assert_debug(static_cast<uint32>(newValue) < (1u << (__bitWidth))); \
	v &= ~((((1u << (__bitWidth)) - 1u) << (__bitIndex))); \
	v |= (static_cast<uint32>(newValue) << (__bitIndex)); \
    return *this; \
} \
__typename get_##__regname() const \
{	\
	return static_cast<__typename>((v >> (__bitIndex))&((1u << (__bitWidth)) - 1u)); \
}

#define LATTE_BITFIELD_FULL_TYPED(__regname, __typename) \
auto& set_##__regname(__typename newValue) \
{	\
	v = (static_cast<uint32>(newValue)); \
    return *this; \
} \
__typename get_##__regname() const \
{	\
	return static_cast<__typename>(v); \
}

#define LATTE_BITFIELD_FLOAT(__regname) \
auto& set_##__regname(float newValue) \
{	\
	*(float*)&v = newValue; \
    return *this; \
} \
float get_##__regname() const \
{	\
	return *(float*)&v; \
}

	class LATTEREG
	{
	public:
		uint32 getRawValue() const
		{
			return v;
		}

		uint32 getRawValueBE() const
		{
			return _swapEndianU32(v);
		}

	protected:
		uint32 v{};
	};

	// shared enums
	enum class E_COMPAREFUNC // used by depth test func and alpha test func
	{
		NEVER,
		LESS,
		EQUAL,
		LEQUAL,
		GREATER,
		NOTEQUAL,
		GEQUAL,
		ALWAYS
	};

	enum class E_ENDIAN_SWAP
	{
		SWAP_NONE = 0,
	};

	struct LATTE_VGT_PRIMITIVE_TYPE : LATTEREG // 0x2256
	{
		enum class E_PRIMITIVE_TYPE
		{
			NONE = 0x0,
			POINTS = 0x1,
			LINES = 0x2,
			LINE_STRIP = 0x3,
			TRIANGLES = 0x4,
			TRIANGLE_FAN = 0x5,
			TRIANGLE_STRIP = 0x6,

			LINES_ADJACENT = 0xA,
			LINE_STRIP_ADJACENT = 0xB,
			TRIANGLES_ADJACENT = 0xC,
			TRIANGLE_STRIP_ADJACENT = 0xD,

			RECTS = 0x11,
			LINE_LOOP = 0x12,
			QUADS = 0x13,
			QUAD_STRIP = 0x14,
		};

		LATTE_BITFIELD_FULL_TYPED(PRIMITIVE_MODE, E_PRIMITIVE_TYPE);
	};

	struct LATTE_TD_BORDER_COLOR : LATTEREG // 0x2900 - 0x2A47
	{
		LATTE_BITFIELD_FLOAT(channelValue);
	};

	struct LATTE_DB_STENCIL_CLEAR : LATTEREG // 0xA00A
	{
		LATTE_BITFIELD(clearValue, 0, 8);
	};

	struct LATTE_DB_DEPTH_CLEAR : LATTEREG // 0xA00B
	{
		LATTE_BITFIELD_FLOAT(clearValue);
	};

	struct LATTE_CB_TARGET_MASK : LATTEREG // 0xA08E
	{
		LATTE_BITFIELD_FULL_TYPED(MASK, uint32);
	};

	struct LATTE_PA_SC_GENERIC_SCISSOR_TL : LATTEREG // 0xA090
	{
		LATTE_BITFIELD(TL_X, 0, 15);
		LATTE_BITFIELD(TL_Y, 16, 15);
		LATTE_BITFIELD_BOOL(WINDOW_OFFSET_DISABLE, 31);
	};

	struct LATTE_PA_SC_GENERIC_SCISSOR_BR : LATTEREG // 0xA091
	{
		LATTE_BITFIELD(BR_X, 0, 15);
		LATTE_BITFIELD(BR_Y, 16, 15);
	};

	struct LATTE_VGT_MULTI_PRIM_IB_RESET_INDX : LATTEREG // 0xA103
	{
		LATTE_BITFIELD_FULL_TYPED(RESTART_INDEX, uint32);
	};

	struct LATTE_SX_ALPHA_TEST_CONTROL : LATTEREG // 0xA104
	{
		using E_ALPHA_FUNC = E_COMPAREFUNC;

		LATTE_BITFIELD_TYPED(ALPHA_FUNC, 0, 3, E_ALPHA_FUNC);
		LATTE_BITFIELD_BOOL(ALPHA_TEST_ENABLE, 3);
		LATTE_BITFIELD_BOOL(ALPHA_TEST_BYPASS, 8);
	};

	struct LATTE_CB_BLEND_RED : LATTEREG // 0xA105
	{
		LATTE_BITFIELD_FLOAT(RED);
	};

	struct LATTE_CB_BLEND_GREEN : LATTEREG // 0xA106
	{
		LATTE_BITFIELD_FLOAT(GREEN);
	};

	struct LATTE_CB_BLEND_BLUE : LATTEREG // 0xA107
	{
		LATTE_BITFIELD_FLOAT(BLUE);
	};

	struct LATTE_CB_BLEND_ALPHA : LATTEREG // 0xA108
	{
		LATTE_BITFIELD_FLOAT(ALPHA);
	};

	struct LATTE_DB_STENCILREFMASK : LATTEREG // 0xA10C
	{
		LATTE_BITFIELD(STENCILREF_F, 0, 8);
		LATTE_BITFIELD(STENCILMASK_F, 8, 8);
		LATTE_BITFIELD(STENCILWRITEMASK_F, 16, 8);
	};

	struct LATTE_DB_STENCILREFMASK_BF : LATTEREG // 0xA10D
	{
		LATTE_BITFIELD(STENCILREF_B, 0, 8);
		LATTE_BITFIELD(STENCILMASK_B, 8, 8);
		LATTE_BITFIELD(STENCILWRITEMASK_B, 16, 8);
	};

	struct LATTE_SX_ALPHA_REF : LATTEREG // 0xA10E
	{
		LATTE_BITFIELD_FLOAT(ALPHA_TEST_REF);
	};

	struct LATTE_PA_CL_VPORT_XSCALE : LATTEREG // 0xA10F
	{
		LATTE_BITFIELD_FLOAT(SCALE);
	};

	struct LATTE_PA_CL_VPORT_XOFFSET : LATTEREG // 0xA110
	{
		LATTE_BITFIELD_FLOAT(OFFSET);
	};

	struct LATTE_PA_CL_VPORT_YSCALE : LATTEREG // 0xA111
	{
		LATTE_BITFIELD_FLOAT(SCALE);
	};

	struct LATTE_PA_CL_VPORT_YOFFSET : LATTEREG // 0xA112
	{
		LATTE_BITFIELD_FLOAT(OFFSET);
	};

	struct LATTE_PA_CL_VPORT_ZSCALE : LATTEREG // 0xA113
	{
		LATTE_BITFIELD_FLOAT(SCALE);
	};

	struct LATTE_PA_CL_VPORT_ZOFFSET : LATTEREG // 0xA114
	{
		LATTE_BITFIELD_FLOAT(OFFSET);
	};

	struct LATTE_CB_BLENDN_CONTROL : LATTEREG // 0xA1E0 - 0xA1E7
	{
		enum class E_BLENDFACTOR
		{
			BLEND_ZERO						= 0x00,
			BLEND_ONE						= 0x01,
			BLEND_SRC_COLOR					= 0x02,
			BLEND_ONE_MINUS_SRC_COLOR		= 0x03,
			BLEND_SRC_ALPHA					= 0x04,
			BLEND_ONE_MINUS_SRC_ALPHA		= 0x05,
			BLEND_DST_ALPHA					= 0x06,
			BLEND_ONE_MINUS_DST_ALPHA		= 0x07,
			BLEND_DST_COLOR					= 0x08,
			BLEND_ONE_MINUS_DST_COLOR		= 0x09,
			BLEND_SRC_ALPHA_SATURATE		= 0x0A,
			BLEND_BOTH_SRC_ALPHA			= 0x0B,
			BLEND_BOTH_INV_SRC_ALPHA		= 0x0C,
			BLEND_CONST_COLOR				= 0x0D,
			BLEND_ONE_MINUS_CONST_COLOR		= 0x0E,
			BLEND_SRC1_COLOR				= 0x0F,
			BLEND_INV_SRC1_COLOR			= 0x10,
			BLEND_SRC1_ALPHA				= 0x11,
			BLEND_INV_SRC1_ALPHA			= 0x12,
			BLEND_CONST_ALPHA				= 0x13,
			BLEND_ONE_MINUS_CONST_ALPHA		= 0x14
		};

		enum class E_COMBINEFUNC
		{
			DST_PLUS_SRC					= 0,
			SRC_MINUS_DST					= 1,
			MIN_DST_SRC						= 2,
			MAX_DST_SRC						= 3,
			DST_MINUS_SRC					= 4
		};

		LATTE_BITFIELD_TYPED(COLOR_SRCBLEND, 0, 5, E_BLENDFACTOR);
		LATTE_BITFIELD_TYPED(COLOR_COMB_FCN, 5, 3, E_COMBINEFUNC);
		LATTE_BITFIELD_TYPED(COLOR_DSTBLEND, 8, 5, E_BLENDFACTOR);
		LATTE_BITFIELD_BOOL(OPACITY_WEIGHT, 13);
		LATTE_BITFIELD_TYPED(ALPHA_SRCBLEND, 16, 5, E_BLENDFACTOR);
		LATTE_BITFIELD_TYPED(ALPHA_COMB_FCN, 21, 3, E_COMBINEFUNC);
		LATTE_BITFIELD_TYPED(ALPHA_DSTBLEND, 24, 5, E_BLENDFACTOR);
		LATTE_BITFIELD_BOOL(SEPARATE_ALPHA_BLEND, 29);
	};

	struct LATTE_DB_DEPTH_CONTROL : LATTEREG // 0xA200
	{
		using E_ZFUNC = E_COMPAREFUNC;
		using E_STENCILFUNC = E_COMPAREFUNC;

		enum class E_STENCILACTION
		{
			KEEP,
			ZERO,
			REPLACE,
			INCR,
			DECR,
			INVERT,
			INCR_WRAP,
			DECR_WRAP,
		};

		LATTE_BITFIELD_BOOL(STENCIL_ENABLE, 0);
		LATTE_BITFIELD_BOOL(Z_ENABLE, 1);
		LATTE_BITFIELD_BOOL(Z_WRITE_ENABLE, 2);
		// bit 3 is unused?
		LATTE_BITFIELD_TYPED(Z_FUNC, 4, 3, E_ZFUNC);
		LATTE_BITFIELD_BOOL(BACK_STENCIL_ENABLE, 7);

		LATTE_BITFIELD_TYPED(STENCIL_FUNC_F, 8, 3, E_STENCILFUNC);
		LATTE_BITFIELD_TYPED(STENCIL_FAIL_F, 11, 3, E_STENCILACTION);
		LATTE_BITFIELD_TYPED(STENCIL_ZPASS_F, 14, 3, E_STENCILACTION);
		LATTE_BITFIELD_TYPED(STENCIL_ZFAIL_F, 17, 3, E_STENCILACTION);

		LATTE_BITFIELD_TYPED(STENCIL_FUNC_B, 20, 3, E_STENCILFUNC);
		LATTE_BITFIELD_TYPED(STENCIL_FAIL_B, 23, 3, E_STENCILACTION);
		LATTE_BITFIELD_TYPED(STENCIL_ZPASS_B, 26, 3, E_STENCILACTION);
		LATTE_BITFIELD_TYPED(STENCIL_ZFAIL_B, 29, 3, E_STENCILACTION);
	};

	struct LATTE_CB_COLOR_CONTROL : LATTEREG // 0xA202
	{
		enum class E_SPECIALOP
		{
			NORMAL = 0, // use state to render
			DISABLE = 1, // dont write color results
		};

		enum class E_LOGICOP
		{
			CLEAR = 0x00,
			COPY = 0xCC,
			OR = 0xEE,
			SET = 0xFF,
		};

		LATTE_BITFIELD_BOOL(FOG_ENABLE, 0);
		LATTE_BITFIELD_BOOL(MULTIWRITE_ENABLE, 1);
		LATTE_BITFIELD_BOOL(DITHER_ENABLE, 2);
		LATTE_BITFIELD_BOOL(DEGAMMA_ENABLE, 3);
		LATTE_BITFIELD_TYPED(SPECIAL_OP, 4, 3, E_SPECIALOP);
		LATTE_BITFIELD_BOOL(PER_MRT_BLEND, 7);
		LATTE_BITFIELD(BLEND_MASK, 8, 8);
		LATTE_BITFIELD_TYPED(ROP, 16, 8, E_LOGICOP); // aka logic op
	};

	struct LATTE_PA_CL_CLIP_CNTL : LATTEREG // 0xA204
	{
		// todo - other fields
		// see R6xx_3D_Registers.pdf

		LATTE_BITFIELD_BOOL(CLIP_DISABLE, 16);

		LATTE_BITFIELD_BOOL(DX_CLIP_SPACE_DEF, 19); // GX2 calls this flag HalfZ


		LATTE_BITFIELD_BOOL(DX_RASTERIZATION_KILL, 22);

		LATTE_BITFIELD_BOOL(DX_LINEAR_ATTR_CLIP_ENA, 24); // what does this do?

		LATTE_BITFIELD_BOOL(ZCLIP_NEAR_DISABLE, 26);
		LATTE_BITFIELD_BOOL(ZCLIP_FAR_DISABLE, 27);

	};

	struct LATTE_PA_CL_VTE_CNTL : LATTEREG // 0xA206
	{
		LATTE_BITFIELD_BOOL(VPORT_X_SCALE_ENA, 0);
		LATTE_BITFIELD_BOOL(VPORT_X_OFFSET_ENA, 1);

		LATTE_BITFIELD_BOOL(VPORT_Y_SCALE_ENA, 2);
		LATTE_BITFIELD_BOOL(VPORT_Y_OFFSET_ENA, 3);

		LATTE_BITFIELD_BOOL(VPORT_Z_SCALE_ENA, 4);
		LATTE_BITFIELD_BOOL(VPORT_Z_OFFSET_ENA, 5);

		LATTE_BITFIELD_BOOL(VTX_XY_FMT, 8);
		LATTE_BITFIELD_BOOL(VTX_Z_FMT, 9);
		LATTE_BITFIELD_BOOL(VTX_W0_FMT, 10);
	};

	struct LATTE_PA_CL_VS_OUT_CNTL : LATTEREG // 0xA207
	{
		LATTE_BITFIELD(CLIP_DIST_ENA_MASK, 0, 8);
		LATTE_BITFIELD(CULL_DIST_ENA_MASK, 8, 8);
	};

	struct LATTE_PA_SU_POINT_SIZE : LATTEREG // 0xA280
	{
		LATTE_BITFIELD(HEIGHT, 0, 16);
		LATTE_BITFIELD(WIDTH, 16, 16);
	};

	struct LATTE_PA_SU_POINT_MINMAX : LATTEREG // 0xA281
	{
		LATTE_BITFIELD(MIN_SIZE, 0, 16);
		LATTE_BITFIELD(MAX_SIZE, 16, 16);
	};

	struct LATTE_VGT_GS_MODE : LATTEREG // 0xA290
	{
		enum class E_MODE
		{
			OFF = 0,
			SCENARIO_A = 1,
			SCENARIO_B = 2,
			SCENARIO_G = 3
		};

		enum class E_CUT_MODE
		{
			CUT_1024 = 0,
			CUT_512 = 1,
			CUT_256 = 2,
			CUT_128 = 3,
		};

		enum class E_COMPUTE_MODE
		{
			OFF = 0,
			UKN_1 = 1,
			UKN_2 = 2,
			ON = 3,
		};

		LATTE_BITFIELD_TYPED(MODE, 0, 2, E_MODE);
		LATTE_BITFIELD_BOOL(ES_PASSTHRU, 2);
		LATTE_BITFIELD_TYPED(CUT_MODE, 3, 2, E_CUT_MODE);
		LATTE_BITFIELD_TYPED(COMPUTE_MODE, 14, 2, E_COMPUTE_MODE);
		LATTE_BITFIELD_BOOL(PARTIAL_THD_AT_EOI, 17);
	};

	struct LATTE_VGT_DMA_INDEX_TYPE : LATTEREG // 0xA29F
	{
		enum class E_INDEX_TYPE
		{
			U16_LE = 0, // U16
			U32_LE = 1, // U32

			U16_BE = 4, // U16 + SwapU16
			U32_BE = 9, // U32 + SwapU32

			// index 0 -> U16
			// index 1 -> U32
			// bit 0x2 -> swap U16
			// bit 0x4 -> swap U32
			// bit 0x8 -> swap U32 (machine word?)

			AUTO = 0xFFFF, // helper value for tracking auto-generated indices. Not part of the actual enum
		};

		LATTE_BITFIELD_FULL_TYPED(INDEX_TYPE, E_INDEX_TYPE);
	};

	struct LATTE_VGT_PRIMITIVEID_EN : LATTEREG // 0xA2A1
	{
		LATTE_BITFIELD_BOOL(PRIMITIVEID_EN, 0);
	};

	struct LATTE_VGT_MULTI_PRIM_IB_RESET_EN : LATTEREG // 0xA2A5
	{
		LATTE_BITFIELD_BOOL(RESET_EN, 0);
	};

	struct LATTE_VGT_INSTANCE_STEP_RATE_X : LATTEREG // 0xA2A8-0xA2A9
	{
		LATTE_BITFIELD_FULL_TYPED(STEP_RATE, uint32);
	};

	struct LATTE_VGT_STRMOUT_BUFFER_SIZE_X : LATTEREG // 0xA2B4 + index * 4
	{
		LATTE_BITFIELD_FULL_TYPED(SIZE, uint32);
	};

	struct LATTE_VGT_STRMOUT_STRIDE_X : LATTEREG // 0xA2B5 + index * 4
	{
		LATTE_BITFIELD_FULL_TYPED(STRIDE, uint32);
	};

	struct LATTE_VGT_STRMOUT_BUFFER_BASE_X : LATTEREG // 0xA2B6 + index * 4
	{
		LATTE_BITFIELD_FULL_TYPED(BASE, uint32);
	};

	struct LATTE_VGT_STRMOUT_BUFFER_OFFSET_X : LATTEREG // 0xA2B7 + index * 4
	{
		LATTE_BITFIELD_FULL_TYPED(BUFFER_OFFSET, uint32);
	};

	struct LATTE_VGT_STRMOUT_BASE_OFFSET_X : LATTEREG // 0xA2C4-0xA2C7
	{
		LATTE_BITFIELD_FULL_TYPED(BASE_OFFSET, uint32);
	};

	struct LATTE_VGT_STRMOUT_BUFFER_EN : LATTEREG // 0xA2C8
	{
		LATTE_BITFIELD_BOOL(BUFFER_ENABLE_0, 0);
		LATTE_BITFIELD_BOOL(BUFFER_ENABLE_1, 1);
		LATTE_BITFIELD_BOOL(BUFFER_ENABLE_2, 2);
		LATTE_BITFIELD_BOOL(BUFFER_ENABLE_3, 3);
	};

	struct LATTE_PA_SU_POLY_OFFSET_CLAMP : LATTEREG // 0xA37F
	{
		LATTE_BITFIELD_FLOAT(CLAMP);
	};

	struct LATTE_PA_SU_POLY_OFFSET_FRONT_SCALE : LATTEREG // 0xA380
	{
		LATTE_BITFIELD_FLOAT(SCALE);
	};

	struct LATTE_PA_SU_POLY_OFFSET_FRONT_OFFSET : LATTEREG // 0xA381
	{
		LATTE_BITFIELD_FLOAT(OFFSET);
	};

	struct LATTE_PA_SU_POLY_OFFSET_BACK_SCALE : LATTEREG // 0xA382
	{
		LATTE_BITFIELD_FLOAT(SCALE);
	};

	struct LATTE_PA_SU_POLY_OFFSET_BACK_OFFSET : LATTEREG // 0xA383
	{
		LATTE_BITFIELD_FLOAT(OFFSET);
	};

	struct LATTE_SQ_VTX_SEMANTIC_CLEAR : LATTEREG // 0xA238
	{
		LATTE_BITFIELD_FULL_TYPED(CLEAR_MASK, uint32); // probably a bitmask
	};

	struct LATTE_SQ_VTX_SEMANTIC_X : LATTEREG // 0xA0E0 - 0xA0FF
	{
		LATTE_BITFIELD(SEMANTIC_ID, 0, 8);
	};

	struct LATTE_SQ_TEX_RESOURCE_WORD0_N : LATTEREG // 0xE000 + index * 7
	{
		LATTE_BITFIELD_TYPED(DIM, 0, 3, E_DIM);
		LATTE_BITFIELD_TYPED(TILE_MODE, 3, 4, E_HWTILEMODE);

		LATTE_BITFIELD_BOOL(TILE_TYPE, 7);

		LATTE_BITFIELD(PITCH, 8, 11);
		LATTE_BITFIELD(WIDTH, 19, 13);
	};

	struct LATTE_SQ_TEX_RESOURCE_WORD1_N : LATTEREG // 0xE001 + index * 7
	{
		LATTE_BITFIELD(HEIGHT, 0, 13);
		LATTE_BITFIELD(DEPTH, 13, 13);
		LATTE_BITFIELD_TYPED(DATA_FORMAT, 26, 6, E_HWSURFFMT);
	};

	struct LATTE_SQ_TEX_RESOURCE_WORD2_N : LATTEREG // 0xE002 + index * 7
	{
		LATTE_BITFIELD_FULL_TYPED(BASE_ADDRESS, uint32);
	};

	struct LATTE_SQ_TEX_RESOURCE_WORD3_N : LATTEREG // 0xE003 + index * 7
	{
		LATTE_BITFIELD_FULL_TYPED(MIP_ADDRESS, uint32);
	};

	struct LATTE_SQ_TEX_RESOURCE_WORD4_N : LATTEREG // 0xE004 + index * 7
	{
		enum class E_FORMAT_COMP
		{
			COMP_UNSIGNED = 0,
			COMP_SIGNED = 1,
			COMP_UNSIGNED_BIASED = 2,
		};

		enum class E_NUM_FORMAT_ALL
		{
			NUM_FORMAT_NORM = 0,
			NUM_FORMAT_INT = 1,
			NUM_FORMAT_SCALED = 2,
		};

		enum class E_SRF_MODE_ALL
		{
			SRF_MODE_ZERO_CLAMP_MINUS_ONE = 0,
			SRF_MODE_NO_ZERO = 1,
		};

		// using E_ENDIAN_SWAP = E_ENDIAN_SWAP;

		enum class E_SEL
		{
			SEL_X = 0,
			SEL_Y = 1,
			SEL_Z = 2,
			SEL_W = 3,
			SEL_0 = 4,
			SEL_1 = 5
		};

		LATTE_BITFIELD_TYPED(FORMAT_COMP_X, 0, 2, E_FORMAT_COMP);
		LATTE_BITFIELD_TYPED(FORMAT_COMP_Y, 2, 2, E_FORMAT_COMP);
		LATTE_BITFIELD_TYPED(FORMAT_COMP_Z, 4, 2, E_FORMAT_COMP);
		LATTE_BITFIELD_TYPED(FORMAT_COMP_W, 6, 2, E_FORMAT_COMP);
		LATTE_BITFIELD_TYPED(NUM_FORM_ALL, 8, 2, E_NUM_FORMAT_ALL);
		LATTE_BITFIELD_TYPED(SRF_MODE_ALL, 10, 1, E_SRF_MODE_ALL);
		LATTE_BITFIELD_BOOL(FORCE_DEGAMMA, 11);
		LATTE_BITFIELD_TYPED(ENDIAN_SWAP, 12, 2, E_ENDIAN_SWAP);
		LATTE_BITFIELD(REQUEST_SIZE, 14, 2);
		LATTE_BITFIELD_TYPED(DST_SEL_X, 16, 3, E_SEL);
		LATTE_BITFIELD_TYPED(DST_SEL_Y, 19, 3, E_SEL);
		LATTE_BITFIELD_TYPED(DST_SEL_Z, 22, 3, E_SEL);
		LATTE_BITFIELD_TYPED(DST_SEL_W, 25, 3, E_SEL);
		LATTE_BITFIELD(BASE_LEVEL, 28, 4);
	};

	struct LATTE_SQ_TEX_RESOURCE_WORD5_N : LATTEREG // 0xE005 + index * 7
	{
		LATTE_BITFIELD(LAST_LEVEL, 0, 4); // for MSAA textures, this stores the AA level
		LATTE_BITFIELD(BASE_ARRAY, 4, 13);
		LATTE_BITFIELD(LAST_ARRAY, 17, 13);
		LATTE_BITFIELD_BOOL(UKN_BIT_30, 30); // may be a 2 bit value?
	};

	struct LATTE_SQ_TEX_RESOURCE_WORD6_N : LATTEREG // 0xE006 + index * 7
	{
		enum class E_MPEG_CLAMP
		{
			UKN = 0,
		};

		enum class E_TYPE
		{
			VTX_INVALID_TEXTURE = 0,
			VTX_INVALID_BUFFER = 1,
			VTX_VALID_TEXTURE = 2,
			VTX_VALID_BUFFER = 3,
		};

		// unsure if these are correct

		LATTE_BITFIELD_TYPED(MPEG_CLAMP, 0, 2, E_MPEG_CLAMP);
		LATTE_BITFIELD(MAX_ANISO, 2, 3);
		LATTE_BITFIELD(PERF_MODULATION, 5, 3);
		LATTE_BITFIELD_BOOL(INTERLACED, 8);
		LATTE_BITFIELD_TYPED(TYPE, 30, 2, E_TYPE);
	};

	struct LATTE_SQ_TEX_SAMPLER_WORD0_0 : LATTEREG // 0xF000+n*3 - 0xF???
	{
		enum class E_CLAMP
		{
			WRAP = 0,
			MIRROR = 1,
			CLAMP_LAST_TEXEL = 2,
			MIRROR_ONCE_LAST_TEXEL = 3,
			CLAMP_HALF_BORDER = 4,
			MIRROR_ONCE_HALF_BORDER = 5,
			CLAMP_BORDER = 6,
			MIRROR_ONCE_BORDER = 7,
		};

		enum class E_XY_FILTER
		{
			POINT = 0,
			BILINEAR = 1,
			BICUBIC = 2,
			// 3 unused ?
			ANISO_POINT = 4,
			ANISO_BILINEAR = 5,
			// 6, 7 unused ?
		};

		enum class E_Z_FILTER
		{
			NONE = 0,
			POINT = 1,
			LINEAR = 2,
			// 3 is unused ?
		};

		enum class E_BORDER_COLOR_TYPE
		{
			TRANSPARENT_BLACK = 0,
			OPAQUE_BLACK = 1,
			OPAQUE_WHITE = 2,
			REGISTER = 3,
		};

		enum class E_DEPTH_COMPARE
		{
			NEVER = 0,
			LESS = 1,
			EQUAL = 2,
			LEQUAL = 3,
			GREATER = 4,
			NOTEQUAL = 5,
			GEQUAL = 6,
			ALWAYS = 7
		};


		enum class E_CHROMA_KEY
		{
			DISABLE = 0,
			KILL = 1,
			BLEND = 2,
			// 3 is unused
		};

		LATTE_BITFIELD_TYPED(CLAMP_X, 0, 3, E_CLAMP);
		LATTE_BITFIELD_TYPED(CLAMP_Y, 3, 3, E_CLAMP);
		LATTE_BITFIELD_TYPED(CLAMP_Z, 6, 3, E_CLAMP);
		LATTE_BITFIELD_TYPED(XY_MAG_FILTER, 9, 3, E_XY_FILTER);
		LATTE_BITFIELD_TYPED(XY_MIN_FILTER, 12, 3, E_XY_FILTER);
		LATTE_BITFIELD_TYPED(Z_FILTER, 15, 2, E_Z_FILTER);
		LATTE_BITFIELD_TYPED(MIP_FILTER, 17, 2, E_Z_FILTER);
		LATTE_BITFIELD(MAX_ANISO_RATIO, 19, 3);
		LATTE_BITFIELD_TYPED(BORDER_COLOR_TYPE, 22, 2, E_BORDER_COLOR_TYPE);
		LATTE_BITFIELD_BOOL(POINT_SAMPLING_CLAMP, 24);
		LATTE_BITFIELD_BOOL(TEX_ARRAY_OVERRIDE, 25);
		LATTE_BITFIELD_TYPED(DEPTH_COMPARE_FUNCTION, 26, 3, E_DEPTH_COMPARE);
		LATTE_BITFIELD_TYPED(CHROMA_KEY, 28, 2, E_CHROMA_KEY);
		LATTE_BITFIELD_BOOL(LOD_USES_MINOR_AXIS, 25);
	};

	struct LATTE_SQ_TEX_SAMPLER_WORD1_0 : LATTEREG // 0xF001+n*3 - 0xF???
	{
		LATTE_BITFIELD(MIN_LOD, 0, 10);
		LATTE_BITFIELD(MAX_LOD, 10, 10);
		LATTE_BITFIELD_SIGNED(LOD_BIAS, 20, 12);
	};

	struct LATTE_SQ_TEX_SAMPLER_WORD2_0 : LATTEREG // 0xF002+n*3 - 0xF???
	{
		enum E_SAMPLER_TYPE
		{
			UKN0 = 0,
			UKN1 = 1,
		};

		LATTE_BITFIELD(LOD_BIAS_SECONDARY, 0, 12);
		LATTE_BITFIELD_BOOL(MC_COORD_TRUNCATE, 12);
		LATTE_BITFIELD_BOOL(FORCE_DEGAMMA, 13);
		LATTE_BITFIELD_BOOL(HIGH_PRECISION_FILTER, 14);

		// PERF_MIP at 15, 3 bits
		// PERF_Z at 18, 3 bits
		// bit 21 is unused?

		LATTE_BITFIELD(ANISO_BIAS, 22, 6); // is size correct?

		//LATTE_BITFIELD_BOOL(FETCH_4, 26); overlaps with ANISO_BIAS
		//LATTE_BITFIELD_BOOL(SAMPLE_IS_PCF, 27); overlaps with ANISO_BIAS

		LATTE_BITFIELD_TYPED(TYPE, 31, 1, E_SAMPLER_TYPE);
	};

	struct LATTE_SQ_PGM_START_X : LATTEREG // 0xA210 / 0xA216 / 0xA21B / 0xA220 / 0xA225
	{
		LATTE_BITFIELD_FULL_TYPED(PGM_START, uint32);
	};

	struct LATTE_SQ_PGM_RESOURCES_PS : LATTEREG // 0xA214
	{
		LATTE_BITFIELD(NUM_GPRS, 0, 8);
		LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8);
		LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN
		LATTE_BITFIELD(FETCH_CACHE_LINES, 24, 3);
		LATTE_BITFIELD_BOOL(UNCACHED_FIRST_INST, 28);
		LATTE_BITFIELD_BOOL(CLAMP_CONSTS, 31);
	};

	struct LATTE_SQ_PGM_RESOURCES_VS : LATTEREG // 0xA21A
	{
		LATTE_BITFIELD(NUM_GPRS, 0, 8);
		LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8);
		LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN
		LATTE_BITFIELD(FETCH_CACHE_LINES, 24, 3);
		LATTE_BITFIELD_BOOL(UNCACHED_FIRST_INST, 28);
	};

	struct LATTE_SQ_PGM_RESOURCES_GS : LATTEREG // 0xA21F
	{
		LATTE_BITFIELD(NUM_GPRS, 0, 8);
		LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8);
		LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN
	};

	struct LATTE_SQ_PGM_RESOURCES_ES : LATTEREG // 0xA224
	{
		LATTE_BITFIELD(NUM_GPRS, 0, 8);
		LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8);
		LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN
	};

	struct LATTE_SQ_PGM_RESOURCES_FS : LATTEREG // 0xA229
	{
		LATTE_BITFIELD(NUM_GPRS, 0, 8);
		LATTE_BITFIELD(NUM_STACK_ENTRIES, 8, 8);
		LATTE_BITFIELD_BOOL(DX10_CLAMP, 21); // if true, CLAMP modifier in shaders will return 0 for NaN
	};

	struct LATTE_SQ_XX_ITEMSIZE : LATTEREG // 0xA227 - 0xA2XX
	{
		// used by:
		// SQ_ESGS_RING_ITEMSIZE
		// SQ_GSVS_RING_ITEMSIZE
		// SQ_ESTMP_RING_ITEMSIZE
		// SQ_GSTMP_RING_ITEMSIZE
		// SQ_VSTMP_RING_ITEMSIZE
		// SQ_PSTMP_RING_ITEMSIZE
		// SQ_FBUF_RING_ITEMSIZE
		// SQ_REDUC_RING_ITEMSIZE
		LATTE_BITFIELD(ITEMSIZE, 0, 15);
	};

	struct LATTE_PA_SU_SC_MODE_CNTL : LATTEREG // 0xA205
	{
		enum class E_FRONTFACE
		{
			CCW = 0,
			CW = 1
		};

		enum class E_POLYGONMODE
		{
			UKN0 = 0, // default - render triangles
		};

		enum class E_PTYPE
		{
			POINTS = 0,
			LINES = 1,
			TRIANGLES = 2,
		};

		LATTE_BITFIELD_BOOL(CULL_FRONT, 0);
		LATTE_BITFIELD_BOOL(CULL_BACK, 1);
		LATTE_BITFIELD_TYPED(FRONT_FACE, 2, 1, E_FRONTFACE);
		LATTE_BITFIELD_TYPED(POLYGON_MODE, 3, 2, E_POLYGONMODE);
		LATTE_BITFIELD_TYPED(FRONT_POLY_MODE, 5, 3, E_PTYPE);
		LATTE_BITFIELD_TYPED(BACK_POLY_MODE, 8, 3, E_PTYPE);
		LATTE_BITFIELD_BOOL(OFFSET_FRONT_ENABLED, 11);
		LATTE_BITFIELD_BOOL(OFFSET_BACK_ENABLED, 12);
		LATTE_BITFIELD_BOOL(OFFSET_PARA_ENABLED, 13); // offset enable for lines and points?
		// additional fields?
	};

	struct LATTE_SPI_VS_OUT_CONFIG : LATTEREG // 0xA1B1
	{
		LATTE_BITFIELD_BOOL(VS_PER_COMPONENT, 0);
		LATTE_BITFIELD(VS_EXPORT_COUNT, 1, 5);
		LATTE_BITFIELD_BOOL(EXPORTS_FOG, 8);
		LATTE_BITFIELD(VS_OUT_FOG_VEC_ADDR, 9, 5);
	};

	struct LATTE_SPI_VS_OUT_ID_N : LATTEREG // 0xA185 - 0xA18E(?) - 0xA1B2 - 0xA1B3
	{
		uint8 get_SEMANTIC(sint32 index)
		{
			cemu_assert_debug(index < 4);
			return (uint8)((v >> (index * 8)) & 0xFF);
		}

		void set_SEMANTIC(sint32 index, uint8 value)
		{
			cemu_assert_debug(index < 4);
			v &= ~(0xFF << (index * 8));
			v |= (value & 0xFF) << (index * 8);
		}
	};

};

struct _LatteRegisterSetTextureUnit
{
	Latte::LATTE_SQ_TEX_RESOURCE_WORD0_N word0;
	Latte::LATTE_SQ_TEX_RESOURCE_WORD1_N word1;
	Latte::LATTE_SQ_TEX_RESOURCE_WORD2_N word2;
	Latte::LATTE_SQ_TEX_RESOURCE_WORD3_N word3;
	Latte::LATTE_SQ_TEX_RESOURCE_WORD4_N word4;
	Latte::LATTE_SQ_TEX_RESOURCE_WORD5_N word5;
	Latte::LATTE_SQ_TEX_RESOURCE_WORD6_N word6;
};

static_assert(sizeof(_LatteRegisterSetTextureUnit) == 28);

struct _LatteRegisterSetSampler
{
	Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0 WORD0;
	Latte::LATTE_SQ_TEX_SAMPLER_WORD1_0 WORD1;
	Latte::LATTE_SQ_TEX_SAMPLER_WORD2_0 WORD2;
};

static_assert(sizeof(_LatteRegisterSetSampler) == 12);

struct _LatteRegisterSetSamplerBorderColor
{
	Latte::LATTE_TD_BORDER_COLOR red;
	Latte::LATTE_TD_BORDER_COLOR green;
	Latte::LATTE_TD_BORDER_COLOR blue;
	Latte::LATTE_TD_BORDER_COLOR alpha;
};

static_assert(sizeof(_LatteRegisterSetSamplerBorderColor) == 16);

struct _LatteRegisterSetStreamoutBuffer
{
	Latte::LATTE_VGT_STRMOUT_BUFFER_SIZE_X SIZE;
	Latte::LATTE_VGT_STRMOUT_STRIDE_X STRIDE;
	Latte::LATTE_VGT_STRMOUT_BUFFER_BASE_X BASE;
	Latte::LATTE_VGT_STRMOUT_BUFFER_OFFSET_X BUFFER_OFFSET;
};

static_assert(sizeof(_LatteRegisterSetStreamoutBuffer) == 16);

struct LatteContextRegister
{
	uint8 padding0[0x08958];

	/* +0x08958 */ Latte::LATTE_VGT_PRIMITIVE_TYPE VGT_PRIMITIVE_TYPE;
	uint8 padding5[0x0A400 - 0x0895C];
	/* +0x0A400 */ _LatteRegisterSetSamplerBorderColor TD_PS_SAMPLER_BORDER_COLOR[Latte::GPU_LIMITS::NUM_SAMPLERS_PER_STAGE];
	uint8 padding6[0x0A600 - 0x0A520];
	/* +0x0A600 */ _LatteRegisterSetSamplerBorderColor TD_VS_SAMPLER_BORDER_COLOR[Latte::GPU_LIMITS::NUM_SAMPLERS_PER_STAGE];
	uint8 padding7[0x0A800 - 0x0A720];
	/* +0x0A800 */ _LatteRegisterSetSamplerBorderColor TD_GS_SAMPLER_BORDER_COLOR[Latte::GPU_LIMITS::NUM_SAMPLERS_PER_STAGE];
	uint8 padding8[0x28238 - 0x0A920];
	/* +0x28238 */ Latte::LATTE_CB_TARGET_MASK CB_TARGET_MASK;
	uint8 padding_2823C[4];
	/* +0x28240 */ Latte::LATTE_PA_SC_GENERIC_SCISSOR_TL PA_SC_GENERIC_SCISSOR_TL;
	/* +0x28244 */ Latte::LATTE_PA_SC_GENERIC_SCISSOR_BR PA_SC_GENERIC_SCISSOR_BR;
	uint8 padding_28248[0x28380 - 0x28248];
	/* +0x28380 */ Latte::LATTE_SQ_VTX_SEMANTIC_X SQ_VTX_SEMANTIC_X[32];
	/* +0x28400 */ uint8 padding_28400[0x2840C - 0x28400];
	/* +0x2840C */ Latte::LATTE_VGT_MULTI_PRIM_IB_RESET_INDX VGT_MULTI_PRIM_IB_RESET_INDX;
	/* +0x28410 */ Latte::LATTE_SX_ALPHA_TEST_CONTROL SX_ALPHA_TEST_CONTROL;
	/* +0x28414 */ Latte::LATTE_CB_BLEND_RED CB_BLEND_RED;
	/* +0x28418 */ Latte::LATTE_CB_BLEND_GREEN CB_BLEND_GREEN;
	/* +0x2841C */ Latte::LATTE_CB_BLEND_BLUE CB_BLEND_BLUE;
	/* +0x28420 */ Latte::LATTE_CB_BLEND_ALPHA CB_BLEND_ALPHA;
	uint8 padding_28424[0x28430 - 0x28424];
	/* +0x28430 */ Latte::LATTE_DB_STENCILREFMASK DB_STENCILREFMASK;
	/* +0x28434 */ Latte::LATTE_DB_STENCILREFMASK_BF DB_STENCILREFMASK_BF;
	/* +0x28438 */ Latte::LATTE_SX_ALPHA_REF SX_ALPHA_REF;
	/* +0x2843C */ Latte::LATTE_PA_CL_VPORT_XSCALE	PA_CL_VPORT_XSCALE;
	/* +0x28440 */ Latte::LATTE_PA_CL_VPORT_XOFFSET PA_CL_VPORT_XOFFSET;
	/* +0x28444 */ Latte::LATTE_PA_CL_VPORT_YSCALE	PA_CL_VPORT_YSCALE;
	/* +0x28448 */ Latte::LATTE_PA_CL_VPORT_YOFFSET PA_CL_VPORT_YOFFSET;
	/* +0x2844C */ Latte::LATTE_PA_CL_VPORT_ZSCALE	PA_CL_VPORT_ZSCALE;
	/* +0x28450 */ Latte::LATTE_PA_CL_VPORT_ZOFFSET PA_CL_VPORT_ZOFFSET;

	uint8 padding_28450[0x28614 - 0x28454];

	/* +0x28614 */ Latte::LATTE_SPI_VS_OUT_ID_N LATTE_SPI_VS_OUT_ID_N[10];

	uint8 padding_2863C[0x286C4 - 0x2863C];

	/* +0x286C4 */ Latte::LATTE_SPI_VS_OUT_CONFIG SPI_VS_OUT_CONFIG;

	uint8 padding_286C8[0x28780 - 0x286C8];

	/* +0x28780 */ Latte::LATTE_CB_BLENDN_CONTROL CB_BLENDN_CONTROL[8];

	uint8 padding_287A0[0x28800 - 0x287A0];

	/* +0x28800 */ Latte::LATTE_DB_DEPTH_CONTROL DB_DEPTH_CONTROL;
	uint8 padding_28804[4];
	/* +0x28808 */ Latte::LATTE_CB_COLOR_CONTROL CB_COLOR_CONTROL;
	uint8 padding_2880C[4];
	/* +0x28810 */ Latte::LATTE_PA_CL_CLIP_CNTL PA_CL_CLIP_CNTL;
	/* +0x28814 */ Latte::LATTE_PA_SU_SC_MODE_CNTL PA_SU_SC_MODE_CNTL;
	/* +0x28818 */ Latte::LATTE_PA_CL_VTE_CNTL PA_CL_VTE_CNTL;
	/* +0x2881C */ Latte::LATTE_PA_CL_VS_OUT_CNTL PA_CL_VS_OUT_CNTL;

	uint8 padding_2881C[0x28840 - 0x28820];

	/* +0x28840 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_PS;
	/* +0x28844 */ uint32 ukn28844; // PS size
	/* +0x28848 */ uint32 ukn28848;
	/* +0x2884C */ uint32 ukn2884C;
	/* +0x28850 */ Latte::LATTE_SQ_PGM_RESOURCES_PS SQ_PGM_RESOURCES_PS;
	/* +0x28854 */ uint32 ukn28854; // SQ_PGM_EXPORTS_PS
	/* +0x28858 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_VS;
	/* +0x2885C */ uint32 ukn2885C; // VS size
	/* +0x28860 */ uint32 ukn28860;
	/* +0x28864 */ uint32 ukn28864;
	/* +0x28868 */ Latte::LATTE_SQ_PGM_RESOURCES_VS SQ_PGM_RESOURCES_VS;
	/* +0x2886C */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_GS;
	/* +0x28870 */ uint32 ukn28870; // GS size
	/* +0x28874 */ uint32 ukn28874;
	/* +0x28878 */ uint32 ukn28878;
	/* +0x2887C */ Latte::LATTE_SQ_PGM_RESOURCES_GS SQ_PGM_RESOURCES_GS;
	/* +0x28880 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_ES;
	/* +0x28884 */ uint32 ukn28884; // ES size
	/* +0x28888 */ uint32 ukn28888;
	/* +0x2888C */ uint32 ukn2888C;
	/* +0x28890 */ Latte::LATTE_SQ_PGM_RESOURCES_ES SQ_PGM_RESOURCES_ES;
	/* +0x28894 */ Latte::LATTE_SQ_PGM_START_X SQ_PGM_START_FS;
	/* +0x28898 */ uint32 ukn28898; // FS size
	/* +0x2889C */ uint32 ukn2889C;
	/* +0x288A0 */ uint32 ukn288A0;
	/* +0x288A4 */ Latte::LATTE_SQ_PGM_RESOURCES_FS SQ_PGM_RESOURCES_FS;
	/* +0x288A8 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_ESGS_RING_ITEMSIZE;
	/* +0x288AC */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_GSVS_RING_ITEMSIZE;
	/* +0x288B0 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_ESTMP_RING_ITEMSIZE;
	/* +0x288B4 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_GSTMP_RING_ITEMSIZE;
	/* +0x288B8 */ Latte::LATTE_SQ_XX_ITEMSIZE SQ_VSTMP_RING_ITEMSIZE;
	uint8 padding_288BC[0x288E0 - 0x288BC];
	/* +0x288E0 */ Latte::LATTE_SQ_VTX_SEMANTIC_CLEAR SQ_VTX_SEMANTIC_CLEAR;
	uint8 padding_288E4[0x28A00 - 0x288E4];
	/* +0x28A00 */ Latte::LATTE_PA_SU_POINT_SIZE PA_SU_POINT_SIZE;
	/* +0x28A04 */ Latte::LATTE_PA_SU_POINT_MINMAX PA_SU_POINT_MINMAX;

	uint8 padding_28A08[0x28A40 - 0x28A08];

	/* +0x28A40 */ Latte::LATTE_VGT_GS_MODE VGT_GS_MODE;

	uint8 padding_28A44[0x28A7C - 0x28A44];

	/* +0x28A7C */ Latte::LATTE_VGT_DMA_INDEX_TYPE VGT_DMA_INDEX_TYPE;
	/* +0x28A80 */ uint32 ukn28A80;
	/* +0x28A84 */ Latte::LATTE_VGT_PRIMITIVEID_EN VGT_PRIMITIVEID_EN;
	/* +0x28A88 */ uint32 ukn28A88;
	/* +0x28A8C */ uint32 ukn28A8C;
	/* +0x28A90 */ uint32 ukn28A90;
	/* +0x28A94 */ Latte::LATTE_VGT_MULTI_PRIM_IB_RESET_EN VGT_MULTI_PRIM_IB_RESET_EN;
	/* +0x28A98 */ uint32 ukn28A98;
	/* +0x28A9C */ uint32 ukn28A9C;
	/* +0x28AA0 */ Latte::LATTE_VGT_INSTANCE_STEP_RATE_X VGT_INSTANCE_STEP_RATE_0;
	/* +0x28AA4 */ Latte::LATTE_VGT_INSTANCE_STEP_RATE_X VGT_INSTANCE_STEP_RATE_1;

	uint8 padding_28AA8[0x28AD0 - 0x28AA8];

	/* +0x28AD0 */ _LatteRegisterSetStreamoutBuffer VGT_STRMOUT_BUFFER_X[4];
	/* +0x28B10 */ Latte::LATTE_VGT_STRMOUT_BASE_OFFSET_X VGT_STRMOUT_BASE_OFFSET_X[4];
	/* +0x28B20 */ Latte::LATTE_VGT_STRMOUT_BUFFER_EN VGT_STRMOUT_BUFFER_EN;

	uint8 padding_28B24[0x28DFC - 0x28B24];

	/* +0x28DFC */ Latte::LATTE_PA_SU_POLY_OFFSET_CLAMP PA_SU_POLY_OFFSET_CLAMP;
	/* +0x28E00 */ Latte::LATTE_PA_SU_POLY_OFFSET_FRONT_SCALE PA_SU_POLY_OFFSET_FRONT_SCALE;
	/* +0x28E04 */ Latte::LATTE_PA_SU_POLY_OFFSET_FRONT_OFFSET PA_SU_POLY_OFFSET_FRONT_OFFSET;
	/* +0x28E08 */ Latte::LATTE_PA_SU_POLY_OFFSET_BACK_SCALE PA_SU_POLY_OFFSET_BACK_SCALE;
	/* +0x28E0C */ Latte::LATTE_PA_SU_POLY_OFFSET_BACK_OFFSET PA_SU_POLY_OFFSET_BACK_OFFSET;

	uint8 padding_28E10[0x38000 - 0x28E10];

	// texture units are mapped as following (18 sets each):
	// 0xE000 0x38000 -> pixel shader
	// 0xE460 0x39180 -> vertex shader / compute shader
	// 0xE930 0x3A4C0 -> geometry shader
	// there is register space for exactly 160 ps tex units and 176 vs tex units. It's unknown how many GS units there are.

	/* +0x38000 */ _LatteRegisterSetTextureUnit SQ_TEX_START_PS[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE];
	_LatteRegisterSetTextureUnit _DUMMY_TEX_UNITS_PS[160 - Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE];
	/* +0x39180 */ _LatteRegisterSetTextureUnit SQ_TEX_START_VS[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE];
	_LatteRegisterSetTextureUnit _DUMMY_TEX_UNITS_VS[176 - Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE];
	/* +0x3A4C0 */ _LatteRegisterSetTextureUnit SQ_TEX_START_GS[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE];

	uint8 padding_3A6B8[0x3C000 - 0x3A6B8];
	
	/* +0x3C000 */ _LatteRegisterSetSampler SQ_TEX_SAMPLER[18 * 3];

	/* +0x3C288 */
	uint8 padding_3C288[0x40000 - 0x3C288];

	/* +0x40000 */
	// special state registers
	uint32 hleSpecialState[9]; // deprecated

	uint32* GetRawView() const
	{
		return (uint32*)padding0;
	}

	uint32* GetSpecialStateValues() const
	{
		return (uint32*)hleSpecialState;
	}
};

static_assert(sizeof(LatteContextRegister) == 0x10000 * 4 + 9 * 4);

static_assert(offsetof(LatteContextRegister, VGT_PRIMITIVE_TYPE) == Latte::REGADDR::VGT_PRIMITIVE_TYPE * 4);
static_assert(offsetof(LatteContextRegister, TD_PS_SAMPLER_BORDER_COLOR) == Latte::REGADDR::TD_PS_SAMPLER0_BORDER_RED * 4);
static_assert(offsetof(LatteContextRegister, TD_VS_SAMPLER_BORDER_COLOR) == Latte::REGADDR::TD_VS_SAMPLER0_BORDER_RED * 4);
static_assert(offsetof(LatteContextRegister, TD_GS_SAMPLER_BORDER_COLOR) == Latte::REGADDR::TD_GS_SAMPLER0_BORDER_RED * 4);
static_assert(offsetof(LatteContextRegister, CB_TARGET_MASK) == Latte::REGADDR::CB_TARGET_MASK * 4);
static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_TL) == Latte::REGADDR::PA_SC_GENERIC_SCISSOR_TL * 4);
static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_BR) == Latte::REGADDR::PA_SC_GENERIC_SCISSOR_BR * 4);
static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_INDX) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_INDX * 4);
static_assert(offsetof(LatteContextRegister, VGT_PRIMITIVEID_EN) == Latte::REGADDR::VGT_PRIMITIVEID_EN * 4);
static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_EN) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_EN * 4);
static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_0) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_0 * 4);
static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_1) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_1 * 4);
static_assert(offsetof(LatteContextRegister, VGT_STRMOUT_BUFFER_X) == Latte::REGADDR::VGT_STRMOUT_BUFFER_SIZE_0 * 4);
static_assert(offsetof(LatteContextRegister, VGT_STRMOUT_BASE_OFFSET_X) == Latte::REGADDR::VGT_STRMOUT_BASE_OFFSET_0 * 4);
static_assert(offsetof(LatteContextRegister, VGT_STRMOUT_BUFFER_EN) == Latte::REGADDR::VGT_STRMOUT_BUFFER_EN * 4);
static_assert(offsetof(LatteContextRegister, SX_ALPHA_TEST_CONTROL) == Latte::REGADDR::SX_ALPHA_TEST_CONTROL * 4);
static_assert(offsetof(LatteContextRegister, DB_STENCILREFMASK) == Latte::REGADDR::DB_STENCILREFMASK * 4);
static_assert(offsetof(LatteContextRegister, DB_STENCILREFMASK_BF) == Latte::REGADDR::DB_STENCILREFMASK_BF * 4);
static_assert(offsetof(LatteContextRegister, SX_ALPHA_REF) == Latte::REGADDR::SX_ALPHA_REF * 4);
static_assert(offsetof(LatteContextRegister, CB_BLEND_RED) == Latte::REGADDR::CB_BLEND_RED * 4);
static_assert(offsetof(LatteContextRegister, CB_BLEND_GREEN) == Latte::REGADDR::CB_BLEND_GREEN * 4);
static_assert(offsetof(LatteContextRegister, CB_BLEND_BLUE) == Latte::REGADDR::CB_BLEND_BLUE * 4);
static_assert(offsetof(LatteContextRegister, CB_BLEND_ALPHA) == Latte::REGADDR::CB_BLEND_ALPHA * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VPORT_XSCALE) == Latte::REGADDR::PA_CL_VPORT_XSCALE * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VPORT_XOFFSET) == Latte::REGADDR::PA_CL_VPORT_XOFFSET * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VPORT_YSCALE) == Latte::REGADDR::PA_CL_VPORT_YSCALE * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VPORT_YOFFSET) == Latte::REGADDR::PA_CL_VPORT_YOFFSET * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VPORT_ZSCALE) == Latte::REGADDR::PA_CL_VPORT_ZSCALE * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VPORT_ZOFFSET) == Latte::REGADDR::PA_CL_VPORT_ZOFFSET * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_CLIP_CNTL) == Latte::REGADDR::PA_CL_CLIP_CNTL * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_SC_MODE_CNTL) == Latte::REGADDR::PA_SU_SC_MODE_CNTL * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VTE_CNTL) == Latte::REGADDR::PA_CL_VTE_CNTL * 4);
static_assert(offsetof(LatteContextRegister, PA_CL_VS_OUT_CNTL) == Latte::REGADDR::PA_CL_VS_OUT_CNTL * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_POINT_SIZE) == Latte::REGADDR::PA_SU_POINT_SIZE * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_POINT_MINMAX) == Latte::REGADDR::PA_SU_POINT_MINMAX * 4);
static_assert(offsetof(LatteContextRegister, CB_BLENDN_CONTROL) == Latte::REGADDR::CB_BLEND0_CONTROL * 4);
static_assert(offsetof(LatteContextRegister, DB_DEPTH_CONTROL) == Latte::REGADDR::DB_DEPTH_CONTROL * 4);
static_assert(offsetof(LatteContextRegister, CB_COLOR_CONTROL) == Latte::REGADDR::CB_COLOR_CONTROL * 4);
static_assert(offsetof(LatteContextRegister, VGT_GS_MODE) == Latte::REGADDR::VGT_GS_MODE * 4);
static_assert(offsetof(LatteContextRegister, VGT_DMA_INDEX_TYPE) == Latte::REGADDR::VGT_DMA_INDEX_TYPE * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_CLAMP) == Latte::REGADDR::PA_SU_POLY_OFFSET_CLAMP * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_FRONT_SCALE) == Latte::REGADDR::PA_SU_POLY_OFFSET_FRONT_SCALE * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_FRONT_OFFSET) == Latte::REGADDR::PA_SU_POLY_OFFSET_FRONT_OFFSET * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_BACK_SCALE) == Latte::REGADDR::PA_SU_POLY_OFFSET_BACK_SCALE * 4);
static_assert(offsetof(LatteContextRegister, PA_SU_POLY_OFFSET_BACK_OFFSET) == Latte::REGADDR::PA_SU_POLY_OFFSET_BACK_OFFSET * 4);
static_assert(offsetof(LatteContextRegister, SQ_VTX_SEMANTIC_X) == Latte::REGADDR::SQ_VTX_SEMANTIC_0 * 4);
static_assert(offsetof(LatteContextRegister, SQ_VTX_SEMANTIC_CLEAR) == Latte::REGADDR::SQ_VTX_SEMANTIC_CLEAR * 4);
static_assert(offsetof(LatteContextRegister, SQ_TEX_START_PS) == Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS * 4);
static_assert(offsetof(LatteContextRegister, SQ_TEX_START_VS) == Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS * 4);
static_assert(offsetof(LatteContextRegister, SQ_TEX_START_GS) == Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS * 4);
static_assert(offsetof(LatteContextRegister, SQ_TEX_SAMPLER) == Latte::REGADDR::SQ_TEX_SAMPLER_WORD0_0 * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_START_PS) == Latte::REGADDR::SQ_PGM_START_PS * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_PS) == Latte::REGADDR::SQ_PGM_RESOURCES_PS * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_START_VS) == Latte::REGADDR::SQ_PGM_START_VS * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_VS) == Latte::REGADDR::SQ_PGM_RESOURCES_VS * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_START_FS) == Latte::REGADDR::SQ_PGM_START_FS * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_FS) == Latte::REGADDR::SQ_PGM_RESOURCES_FS * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_START_ES) == Latte::REGADDR::SQ_PGM_START_ES * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_ES) == Latte::REGADDR::SQ_PGM_RESOURCES_ES * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_START_GS) == Latte::REGADDR::SQ_PGM_START_GS * 4);
static_assert(offsetof(LatteContextRegister, SQ_PGM_RESOURCES_GS) == Latte::REGADDR::SQ_PGM_RESOURCES_GS * 4);
static_assert(offsetof(LatteContextRegister, SPI_VS_OUT_CONFIG) == Latte::REGADDR::SPI_VS_OUT_CONFIG * 4);
static_assert(offsetof(LatteContextRegister, LATTE_SPI_VS_OUT_ID_N) == Latte::REGADDR::SPI_VS_OUT_ID_0 * 4);