#ifndef STATE_HPP__
#define STATE_HPP__

#include "rsp_common.h"

#define DMEM_SIZE (4 * 1024)
#define IMEM_SIZE (4 * 1024)
#define DMEM_WORDS (DMEM_SIZE / 4)
#define IMEM_WORDS (IMEM_SIZE / 4)
#define CODE_BLOCK_SIZE (256)
#define CODE_BLOCK_WORDS (CODE_BLOCK_SIZE / 4)
#define CODE_BLOCK_SIZE_LOG2 (8)
#define CODE_BLOCKS (IMEM_SIZE / CODE_BLOCK_SIZE)

namespace RSP
{
enum RSPFlags
{
	RSP_VCO = 0,
	RSP_VCC = 1,
	RSP_VCE = 2
};

enum RSPAccumulator
{
	RSP_ACC_LO = 16,
	RSP_ACC_MD = 8,
	RSP_ACC_HI = 0
};

enum CP0Registers
{
	CP0_REGISTER_DMA_CACHE = 0,
	CP0_REGISTER_DMA_DRAM = 1,
	CP0_REGISTER_DMA_READ_LENGTH = 2,
	CP0_REGISTER_DMA_WRITE_LENGTH = 3,
	CP0_REGISTER_SP_STATUS = 4,
	CP0_REGISTER_DMA_FULL = 5,
	CP0_REGISTER_DMA_BUSY = 6,
	CP0_REGISTER_SP_SEMAPHORE = 7,
	CP0_REGISTER_CMD_START = 8,
	CP0_REGISTER_CMD_END = 9,
	CP0_REGISTER_CMD_CURRENT = 10,
	CP0_REGISTER_CMD_STATUS = 11,
	CP0_REGISTER_CMD_CLOCK = 12,
	CP0_REGISTER_CMD_BUSY = 13,
	CP0_REGISTER_CMD_PIPE_BUSY = 14,
	CP0_REGISTER_CMD_TMEM_BUSY = 15,
};

// CMD_STATUS read bits.
#define DPC_STATUS_XBUS_DMEM_DMA 0x001
#define DPC_STATUS_FREEZE 0x002
#define DPC_STATUS_FLUSH 0x004
#define DPC_STATUS_START_GCLK 0x008
#define DPC_STATUS_TMEM_BUSY 0x010
#define DPC_STATUS_PIPE_BUSY 0x020
#define DPC_STATUS_CMD_BUSY 0x040
#define DPC_STATUS_CBUF_READY 0x080
#define DPC_STATUS_DMA_BUSY 0x100
#define DPC_STATUS_END_VALID 0x200
#define DPC_STATUS_START_VALID 0x400

// CMD_STATUS write bits.
#define DPC_CLR_XBUS_DMEM_DMA 0x001
#define DPC_SET_XBUS_DMEM_DMA 0x002
#define DPC_CLR_FREEZE 0x004
#define DPC_SET_FREEZE 0x008
#define DPC_CLR_FLUSH 0x010
#define DPC_SET_FLUSH 0x020
#define DPC_CLR_TMEM_CTR 0x040
#define DPC_CLR_PIPE_CTR 0x080
#define DPC_CLR_CMD_CTR 0x100
#define DPC_CLR_CLOCK_CTR 0x200

// SP_STATUS read bits.
#define SP_STATUS_HALT 0x0001
#define SP_STATUS_BROKE 0x0002
#define SP_STATUS_DMA_BUSY 0x0004
#define SP_STATUS_DMA_FULL 0x0008
#define SP_STATUS_IO_FULL 0x0010
#define SP_STATUS_SSTEP 0x0020
#define SP_STATUS_INTR_BREAK 0x0040
#define SP_STATUS_SIG0 0x0080
#define SP_STATUS_SIG1 0x0100
#define SP_STATUS_SIG2 0x0200
#define SP_STATUS_SIG3 0x0400
#define SP_STATUS_SIG4 0x0800
#define SP_STATUS_SIG5 0x1000
#define SP_STATUS_SIG6 0x2000
#define SP_STATUS_SIG7 0x4000

// SP_STATUS write bits.
#define SP_CLR_HALT 0x00000001
#define SP_SET_HALT 0x00000002
#define SP_CLR_BROKE 0x00000004
#define SP_CLR_INTR 0x00000008
#define SP_SET_INTR 0x00000010
#define SP_CLR_SSTEP 0x00000020
#define SP_SET_SSTEP 0x00000040
#define SP_CLR_INTR_BREAK 0x00000080
#define SP_SET_INTR_BREAK 0x00000100
#define SP_CLR_SIG0 0x00000200
#define SP_SET_SIG0 0x00000400
#define SP_CLR_SIG1 0x00000800
#define SP_SET_SIG1 0x00001000
#define SP_CLR_SIG2 0x00002000
#define SP_SET_SIG2 0x00004000
#define SP_CLR_SIG3 0x00008000
#define SP_SET_SIG3 0x00010000
#define SP_CLR_SIG4 0x00020000
#define SP_SET_SIG4 0x00040000
#define SP_CLR_SIG5 0x00080000
#define SP_SET_SIG5 0x00100000
#define SP_CLR_SIG6 0x00200000
#define SP_SET_SIG6 0x00400000
#define SP_CLR_SIG7 0x00800000
#define SP_SET_SIG7 0x01000000

template <int N>
struct alignas(rsp_vect_t) AlignedRSPVector
{
	uint16_t e[8 * N];
};

struct CP0
{
	uint32_t *cr[16] = {};
	uint32_t *irq = nullptr;
};

struct alignas(64) CP2
{
	AlignedRSPVector<1> regs[32];
	AlignedRSPVector<2> flags[3];
	AlignedRSPVector<3> acc;
	int16_t div_out;
	int16_t div_in;
	int8_t dp_flag;
};

struct CPUState
{
	uint32_t pc = 0;
	uint32_t instruction_count = 0;
	uint32_t last_instruction_type = 0;
	uint32_t instruction_pipeline = 0;
	uint32_t dirty_blocks = 0;
	static_assert(CODE_BLOCKS <= 32, "Code blocks must fit in 32-bit register.");

	uint32_t has_delay_slot = 0;
	uint32_t branch_target = 0;

	uint32_t sr[33] = {}; // r32 is used as a scratch register.
	uint32_t *dmem = nullptr;
	uint32_t *imem = nullptr;
	uint32_t *rdram = nullptr;

	CP2 cp2 = {};
	CP0 cp0;
};

enum ReturnMode
{
	MODE_ENTER = 0,
	MODE_CONTINUE = 1,
	MODE_BREAK = 2,
	MODE_DMA_READ = 3,
	MODE_CHECK_FLAGS = 4,
	MODE_EXIT = 5
};

enum InstructionType
{
	VU_INSTRUCTION = 0,
	SU_INSTRUCTION = 1
};

} // namespace RSP

#endif
