// Copyright 2008 Dolphin Emulator Project
// Copyright 2004 Duddie & Tratax
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <atomic>
#include <memory>
#include <string>

#include "Core/DSP/DSPBreakpoints.h"
#include "Core/DSP/DSPCaptureLogger.h"
#include "Core/DSP/DSPEmitter.h"

enum : u32
{
	DSP_IRAM_BYTE_SIZE = 0x2000,
	DSP_IRAM_SIZE      = 0x1000,
	DSP_IRAM_MASK      = 0x0fff
};

enum : u32
{
	DSP_IROM_BYTE_SIZE = 0x2000,
	DSP_IROM_SIZE      = 0x1000,
	DSP_IROM_MASK      = 0x0fff
};

enum : u32
{
	DSP_DRAM_BYTE_SIZE = 0x2000,
	DSP_DRAM_SIZE      = 0x1000,
	DSP_DRAM_MASK      = 0x0fff
};

enum : u32
{
	DSP_COEF_BYTE_SIZE = 0x1000,
	DSP_COEF_SIZE      = 0x800,
	DSP_COEF_MASK      = 0x7ff
};

enum : u16
{
	DSP_RESET_VECTOR = 0x8000
};

enum : u8
{
	DSP_STACK_DEPTH = 0x20,
	DSP_STACK_MASK  = 0x1f
};

enum : u32
{
	DSP_CR_IMEM     = 2,
	DSP_CR_DMEM     = 0,
	DSP_CR_TO_CPU   = 1,
	DSP_CR_FROM_CPU = 0
};

// Register table taken from libasnd
enum : int
{
	// Address registers
	DSP_REG_AR0    = 0x00,
	DSP_REG_AR1    = 0x01,
	DSP_REG_AR2    = 0x02,
	DSP_REG_AR3    = 0x03,

	// Indexing registers (actually, mostly used as increments)
	DSP_REG_IX0    = 0x04,
	DSP_REG_IX1    = 0x05,
	DSP_REG_IX2    = 0x06,
	DSP_REG_IX3    = 0x07,

	// Address wrapping registers. should be initialized to 0xFFFF if not used.
	DSP_REG_WR0    = 0x08,
	DSP_REG_WR1    = 0x09,
	DSP_REG_WR2    = 0x0a,
	DSP_REG_WR3    = 0x0b,

	// Stacks
	DSP_REG_ST0    = 0x0c,
	DSP_REG_ST1    = 0x0d,
	DSP_REG_ST2    = 0x0e,
	DSP_REG_ST3    = 0x0f,

	// Seems to be the top 8 bits of LRS/SRS.
	DSP_REG_CR     = 0x12,
	DSP_REG_SR     = 0x13,

	// Product
	DSP_REG_PRODL  = 0x14,
	DSP_REG_PRODM  = 0x15,
	DSP_REG_PRODH  = 0x16,
	DSP_REG_PRODM2 = 0x17,

	DSP_REG_AXL0   = 0x18,
	DSP_REG_AXL1   = 0x19,
	DSP_REG_AXH0   = 0x1a,
	DSP_REG_AXH1   = 0x1b,

	// Accumulator (global)
	DSP_REG_ACC0   = 0x1c,
	DSP_REG_ACC1   = 0x1d,

	DSP_REG_ACL0   = 0x1c, // Low accumulator
	DSP_REG_ACL1   = 0x1d,
	DSP_REG_ACM0   = 0x1e, // Mid accumulator
	DSP_REG_ACM1   = 0x1f,
	DSP_REG_ACH0   = 0x10, // Sign extended 8 bit register 0
	DSP_REG_ACH1   = 0x11  // Sign extended 8 bit register 1
};

// Hardware registers address
enum : u32
{
	DSP_COEF_A1_0   = 0xa0,

	DSP_DSCR        = 0xc9, // DSP DMA Control Reg
	DSP_DSPA        = 0xcd, // DSP DMA Address (DSP)
	DSP_DSBL        = 0xcb, // DSP DMA Block Length
	DSP_DSMAH       = 0xce, // DSP DMA Address High (External)
	DSP_DSMAL       = 0xcf, // DSP DMA Address Low (External)

	DSP_FORMAT      = 0xd1, // Sample format
	DSP_ACUNK       = 0xd2, // Set to 3 on my dumps
	DSP_ACDATA1     = 0xd3, // Used only by Zelda ucodes
	DSP_ACSAH       = 0xd4, // Start of loop
	DSP_ACSAL       = 0xd5,
	DSP_ACEAH       = 0xd6, // End of sample (and loop)
	DSP_ACEAL       = 0xd7,
	DSP_ACCAH       = 0xd8, // Current playback position
	DSP_ACCAL       = 0xd9,
	DSP_PRED_SCALE  = 0xda, // ADPCM predictor and scale
	DSP_YN1         = 0xdb,
	DSP_YN2         = 0xdc,
	DSP_ACCELERATOR = 0xdd, // ADPCM accelerator read. Used by AX.
	DSP_GAIN        = 0xde,
	DSP_ACUNK2      = 0xdf, // Set to 0xc on my dumps

	DSP_AMDM        = 0xef, // ARAM DMA Request Mask 0: DMA with ARAM unmasked 1: masked

	DSP_DIRQ        = 0xfb, // DSP Irq Rest
	DSP_DMBH        = 0xfc, // DSP Mailbox H
	DSP_DMBL        = 0xfd, // DSP Mailbox L
	DSP_CMBH        = 0xfe, // CPU Mailbox H
	DSP_CMBL        = 0xff  // CPU Mailbox L
};

// Stacks
enum : int
{
	DSP_STACK_C,
	DSP_STACK_D
};

// cr (Not g_dsp.r[CR]) bits
// See HW/DSP.cpp.
enum : u32
{
	CR_EXTERNAL_INT = 0x0002,
	CR_HALT         = 0x0004,
	CR_INIT         = 0x0400
};

// SR bits
enum : u16
{
	SR_CARRY           = 0x0001,
	SR_OVERFLOW        = 0x0002,
	SR_ARITH_ZERO      = 0x0004,
	SR_SIGN            = 0x0008,
	SR_OVER_S32        = 0x0010, // Set when there was mod/tst/cmp on accu and result is over s32
	SR_TOP2BITS        = 0x0020, // If the upper (ac?.m/ax?.h) 2 bits are equal
	SR_LOGIC_ZERO      = 0x0040,
	SR_OVERFLOW_STICKY = 0x0080, // Set at the same time as 0x2 (under same conditions) - but not cleared the same
	SR_100             = 0x0100, // Unknown
	SR_INT_ENABLE      = 0x0200, // Not 100% sure but duddie says so. This should replace the hack, if so.
	SR_400             = 0x0400, // Unknown
	SR_EXT_INT_ENABLE  = 0x0800, // Appears in zelda - seems to disable external interrupts
	SR_1000            = 0x1000, // Unknown
	SR_MUL_MODIFY      = 0x2000, // 1 = normal. 0 = x2   (M0, M2) (Free mul by 2)
	SR_40_MODE_BIT     = 0x4000, // 0 = "16", 1 = "40"  (SET16, SET40)  Controls sign extension when loading mid accums and data saturation for stores from mid accums.
	SR_MUL_UNSIGNED    = 0x8000, // 0 = normal. 1 = unsigned  (CLR15, SET15) If set, treats ax?.l as unsigned (MULX family only).

	// This should be the bits affected by CMP. Does not include logic zero.
	SR_CMP_MASK        = 0x3f
};

// Exception vectors
enum : int
{
	EXP_STOVF = 1, // 0x0002 stack under/over flow
	EXP_2     = 2, // 0x0004
	EXP_3     = 3, // 0x0006
	EXP_4     = 4, // 0x0008
	EXP_ACCOV = 5, // 0x000a accelerator address overflow
	EXP_6     = 6, // 0x000c
	EXP_INT   = 7  // 0x000e external int (message from CPU)
};

struct DSP_Regs
{
	u16 ar[4];
	u16 ix[4];
	u16 wr[4];
	u16 st[4];
	u16 cr;
	u16 sr;

	union
	{
		u64 val;
		struct
		{
			u16 l;
			u16 m;
			u16 h;
			u16 m2;//if this gets in the way, drop it.
		};
	} prod;

	union
	{
		u32 val;
		struct {
			u16 l;
			u16 h;
		};
	} ax[2];

	union
	{
		u64 val;
		struct
		{
			u16 l;
			u16 m;
			u16 h;
		};
	} ac[2];
};

// All the state of the DSP should be in this struct. Any DSP state that is not filled on init
// should be moved here.
struct SDSP
{
	DSP_Regs r;
	u16 pc;
#if PROFILE
	u16 err_pc;
#endif

	// This is NOT the same cr as r.cr.
	// This register is shared with the main emulation, see DSP.cpp
	// The engine has control over 0x0C07 of this reg.
	// Bits are defined in a struct in DSP.cpp.
	u16 cr;

	u8 reg_stack_ptr[4];
	u8 exceptions;   // pending exceptions
	volatile bool external_interrupt_waiting;
	bool reset_dspjit_codespace;

	// DSP hardware stacks. They're mapped to a bunch of registers, such that writes
	// to them push and reads pop.
	// Let's make stack depth 32 for now, which is way more than what's needed.
	// The real DSP has different depths for the different stacks, but it would
	// be strange if any ucode relied on stack overflows since on the DSP, when
	// the stack overflows, you're screwed.
	u16 reg_stack[4][DSP_STACK_DEPTH];

	// For debugging.
	u32 iram_crc;
	u64 step_counter;

	// Mailbox.
	std::atomic<u32> mbox[2];

	// Accelerator / DMA / other hardware registers. Not GPRs.
	std::array<u16, 256> ifx_regs;

	// When state saving, all of the above can just be memcpy'd into the save state.
	// The below needs special handling.
	u16 *iram;
	u16 *dram;
	u16 *irom;
	u16 *coef;

	// This one doesn't really belong here.
	u8  *cpu_ram;
};

extern SDSP g_dsp;
extern DSPBreakpoints g_dsp_breakpoints;
extern u16 g_cycles_left;
extern bool g_init_hax;
extern std::unique_ptr<DSPEmitter> g_dsp_jit;
extern std::unique_ptr<DSPCaptureLogger> g_dsp_cap;

struct DSPInitOptions
{
	// DSP IROM blob, which is where the DSP boots from. Embedded into the DSP.
	std::array<u16, DSP_IROM_SIZE> irom_contents;

	// DSP DROM blob, which contains resampling coefficients.
	std::array<u16, DSP_COEF_SIZE> coef_contents;

	// Core used to emulate the DSP.
	// Default: CORE_JIT.
	enum CoreType
	{
		CORE_INTERPRETER,
		CORE_JIT,
	};
	CoreType core_type;

	// Optional capture logger used to log internal DSP data transfers.
	// Default: dummy implementation, does nothing.
	DSPCaptureLogger* capture_logger;

	DSPInitOptions()
		: core_type(CORE_JIT),
		  capture_logger(new DefaultDSPCaptureLogger())
	{
	}
};

// Initializes the DSP emulator using the provided options. Takes ownership of
// all the pointers contained in the options structure.
bool DSPCore_Init(const DSPInitOptions& opts);

void DSPCore_Reset();
void DSPCore_Shutdown(); // Frees all allocated memory.

void DSPCore_CheckExternalInterrupt();
void DSPCore_CheckExceptions();
void DSPCore_SetExternalInterrupt(bool val);

// sets a flag in the pending exception register.
void DSPCore_SetException(u8 level);

void CompileCurrent();

enum DSPCoreState
{
	DSPCORE_STOP = 0,
	DSPCORE_RUNNING,
	DSPCORE_STEPPING,
};

int DSPCore_RunCycles(int cycles);

// These are meant to be called from the UI thread.
void DSPCore_SetState(DSPCoreState new_state);
DSPCoreState DSPCore_GetState();

void DSPCore_Step();

u16 DSPCore_ReadRegister(int reg);
void DSPCore_WriteRegister(int reg, u16 val);
