/***************************************************************************

    adsp2100.h

    ADSP-21xx series emulator.

****************************************************************************

    Copyright Aaron Giles
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in
          the documentation and/or other materials provided with the
          distribution.
        * Neither the name 'MAME' nor the names of its contributors may be
          used to endorse or promote products derived from this software
          without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.

***************************************************************************/

#pragma once

#ifndef __ADSP2100_H__
#define __ADSP2100_H__


//**************************************************************************
//  DEBUGGING
//**************************************************************************

#define ADSP_TRACK_HOTSPOTS		0



//**************************************************************************
//  CONSTANTS
//**************************************************************************

// ADSP-2100 IRQs
const int ADSP2100_IRQ0			= 0;		// IRQ0
const int ADSP2100_SPORT1_RX	= 0;		// SPORT1 receive IRQ
const int ADSP2100_IRQ1			= 1;		// IRQ1
const int ADSP2100_SPORT1_TX	= 1;		// SPORT1 transmit IRQ
const int ADSP2100_IRQ2			= 2;		// IRQ2
const int ADSP2100_IRQ3			= 3;		// IRQ3

// ADSP-2101 IRQs
const int ADSP2101_IRQ0			= 0;		// IRQ0
const int ADSP2101_SPORT1_RX	= 0;		// SPORT1 receive IRQ
const int ADSP2101_IRQ1			= 1;		// IRQ1
const int ADSP2101_SPORT1_TX	= 1;		// SPORT1 transmit IRQ
const int ADSP2101_IRQ2			= 2;		// IRQ2
const int ADSP2101_SPORT0_RX	= 3;		// SPORT0 receive IRQ
const int ADSP2101_SPORT0_TX	= 4;		// SPORT0 transmit IRQ
const int ADSP2101_TIMER		= 5;		// internal timer IRQ

// ADSP-2104 IRQs
const int ADSP2104_IRQ0			= 0;		// IRQ0
const int ADSP2104_SPORT1_RX	= 0;		// SPORT1 receive IRQ
const int ADSP2104_IRQ1			= 1;		// IRQ1
const int ADSP2104_SPORT1_TX	= 1;		// SPORT1 transmit IRQ
const int ADSP2104_IRQ2			= 2;		// IRQ2
const int ADSP2104_SPORT0_RX	= 3;		// SPORT0 receive IRQ
const int ADSP2104_SPORT0_TX	= 4;		// SPORT0 transmit IRQ
const int ADSP2104_TIMER		= 5;		// internal timer IRQ

// ADSP-2105 IRQs
const int ADSP2105_IRQ0			= 0;		// IRQ0
const int ADSP2105_SPORT1_RX	= 0;		// SPORT1 receive IRQ
const int ADSP2105_IRQ1			= 1;		// IRQ1
const int ADSP2105_SPORT1_TX	= 1;		// SPORT1 transmit IRQ
const int ADSP2105_IRQ2			= 2;		// IRQ2
const int ADSP2105_TIMER		= 5;		// internal timer IRQ

// ADSP-2115 IRQs
const int ADSP2115_IRQ0			= 0;		// IRQ0
const int ADSP2115_SPORT1_RX	= 0;		// SPORT1 receive IRQ
const int ADSP2115_IRQ1			= 1;		// IRQ1
const int ADSP2115_SPORT1_TX	= 1;		// SPORT1 transmit IRQ
const int ADSP2115_IRQ2			= 2;		// IRQ2
const int ADSP2115_SPORT0_RX	= 3;		// SPORT0 receive IRQ
const int ADSP2115_SPORT0_TX	= 4;		// SPORT0 transmit IRQ
const int ADSP2115_TIMER		= 5;		// internal timer IRQ

// ADSP-2181 IRQs
const int ADSP2181_IRQ0			= 0;		// IRQ0
const int ADSP2181_SPORT1_RX	= 0;		// SPORT1 receive IRQ
const int ADSP2181_IRQ1			= 1;		// IRQ1
const int ADSP2181_SPORT1_TX	= 1;		// SPORT1 transmit IRQ
const int ADSP2181_IRQ2			= 2;		// IRQ2
const int ADSP2181_SPORT0_RX	= 3;		// SPORT0 receive IRQ
const int ADSP2181_SPORT0_TX	= 4;		// SPORT0 transmit IRQ
const int ADSP2181_TIMER		= 5;		// internal timer IRQ
const int ADSP2181_IRQE			= 6;		// IRQE
const int ADSP2181_IRQL1		= 7;		// IRQL1
const int ADSP2181_IRQL2		= 8;		// IRQL2

// register enumeration
enum
{
	ADSP2100_PC,
	ADSP2100_AX0,
	ADSP2100_AX1,
	ADSP2100_AY0,
	ADSP2100_AY1,
	ADSP2100_AR,
	ADSP2100_AF,
	ADSP2100_MX0,
	ADSP2100_MX1,
	ADSP2100_MY0,
	ADSP2100_MY1,
	ADSP2100_MR0,
	ADSP2100_MR1,
	ADSP2100_MR2,
	ADSP2100_MF,
	ADSP2100_SI,
	ADSP2100_SE,
	ADSP2100_SB,
	ADSP2100_SR0,
	ADSP2100_SR1,
	ADSP2100_I0,
	ADSP2100_I1,
	ADSP2100_I2,
	ADSP2100_I3,
	ADSP2100_I4,
	ADSP2100_I5,
	ADSP2100_I6,
	ADSP2100_I7,
	ADSP2100_L0,
	ADSP2100_L1,
	ADSP2100_L2,
	ADSP2100_L3,
	ADSP2100_L4,
	ADSP2100_L5,
	ADSP2100_L6,
	ADSP2100_L7,
	ADSP2100_M0,
	ADSP2100_M1,
	ADSP2100_M2,
	ADSP2100_M3,
	ADSP2100_M4,
	ADSP2100_M5,
	ADSP2100_M6,
	ADSP2100_M7,
	ADSP2100_PX,
	ADSP2100_CNTR,
	ADSP2100_ASTAT,
	ADSP2100_SSTAT,
	ADSP2100_MSTAT,
	ADSP2100_PCSP,
	ADSP2100_CNTRSP,
	ADSP2100_STATSP,
	ADSP2100_LOOPSP,
	ADSP2100_IMASK,
	ADSP2100_ICNTL,
	ADSP2100_IRQSTATE0,
	ADSP2100_IRQSTATE1,
	ADSP2100_IRQSTATE2,
	ADSP2100_IRQSTATE3,
	ADSP2100_FLAGIN,
	ADSP2100_FLAGOUT,
	ADSP2100_FL0,
	ADSP2100_FL1,
	ADSP2100_FL2,
	ADSP2100_AX0_SEC,
	ADSP2100_AX1_SEC,
	ADSP2100_AY0_SEC,
	ADSP2100_AY1_SEC,
	ADSP2100_AR_SEC,
	ADSP2100_AF_SEC,
	ADSP2100_MX0_SEC,
	ADSP2100_MX1_SEC,
	ADSP2100_MY0_SEC,
	ADSP2100_MY1_SEC,
	ADSP2100_MR0_SEC,
	ADSP2100_MR1_SEC,
	ADSP2100_MR2_SEC,
	ADSP2100_MF_SEC,
	ADSP2100_SI_SEC,
	ADSP2100_SE_SEC,
	ADSP2100_SB_SEC,
	ADSP2100_SR0_SEC,
	ADSP2100_SR1_SEC,

	ADSP2100_GENPC = STATE_GENPC,
	ADSP2100_GENSP = STATE_GENSP,
	ADSP2100_GENPCBASE = STATE_GENPCBASE
};



//**************************************************************************
//  INTERFACE CONFIGURATION MACROS
//**************************************************************************

#define MCFG_ADSP21XX_CONFIG(_config) \
	adsp21xx_device::static_set_config(*device, _config); \



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class adsp21xx_device;

// transmit and receive data callbacks types
typedef INT32 (*adsp21xx_rx_func)(adsp21xx_device &device, int port);
typedef void  (*adsp21xx_tx_func)(adsp21xx_device &device, int port, INT32 data);
typedef void  (*adsp21xx_timer_func)(adsp21xx_device &device, int enable);


// ======================> adsp21xx_config

struct adsp21xx_config
{
	adsp21xx_rx_func		m_sport_rx_callback;	// callback for serial receive
	adsp21xx_tx_func		m_sport_tx_callback;	// callback for serial transmit
	adsp21xx_timer_func		m_timer_fired;			// callback for timer fired
};



// ======================> adsp21xx_device

class adsp21xx_device : public cpu_device,
						public adsp21xx_config
{
protected:
	enum
	{
		CHIP_TYPE_ADSP2100,
		CHIP_TYPE_ADSP2101,
		CHIP_TYPE_ADSP2104,
		CHIP_TYPE_ADSP2105,
		CHIP_TYPE_ADSP2115,
		CHIP_TYPE_ADSP2181,
	};

	// construction/destruction
	adsp21xx_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 chiptype);
	virtual ~adsp21xx_device();

public:
	// inline configuration helpers
	static void static_set_config(device_t &device, const adsp21xx_config &config);

	// public interfaces
	void load_boot_data(UINT8 *srcdata, UINT32 *dstdata);

protected:
	// device-level overrides
	virtual void device_start();
	virtual void device_reset();

	// device_execute_interface overrides
	virtual UINT32 execute_min_cycles() const;
	virtual UINT32 execute_max_cycles() const;
	virtual void execute_run();
	virtual void execute_set_input(int inputnum, int state);

	// device_state_interface overrides
	virtual void state_import(const device_state_entry &entry);
	virtual void state_string_export(const device_state_entry &entry, astring &string);

	// device_disasm_interface overrides
	virtual UINT32 disasm_min_opcode_bytes() const;
	virtual UINT32 disasm_max_opcode_bytes() const;
	virtual offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options);

	// helpers
	void create_tables();
	inline void update_mstat();
	inline UINT32 pc_stack_top();
	inline void set_pc_stack_top(UINT32 top);
	inline void pc_stack_push();
	inline void pc_stack_push_val(UINT32 val);
	inline void pc_stack_pop();
	inline UINT32 pc_stack_pop_val();
	inline UINT32 cntr_stack_top();
	inline void cntr_stack_push();
	inline void cntr_stack_pop();
	inline UINT32 loop_stack_top();
	inline void loop_stack_push(UINT32 value);
	inline void loop_stack_pop();
	inline void stat_stack_push();
	inline void stat_stack_pop();
//  inline int condition(int c);
	int slow_condition();
	inline void modify_address(UINT32 ireg, UINT32 mreg);
	inline void data_write_dag1(UINT32 op, INT32 val);
	inline UINT32 data_read_dag1(UINT32 op);
	inline void data_write_dag2(UINT32 op, INT32 val);
	inline UINT32 data_read_dag2(UINT32 op);
	inline void pgm_write_dag2(UINT32 op, INT32 val);
	inline UINT32 pgm_read_dag2(UINT32 op);
	void alu_op_ar(int op);
	void alu_op_ar_const(int op);
	void alu_op_af(int op);
	void alu_op_af_const(int op);
	void alu_op_none(int op);
	void mac_op_mr(int op);
	void mac_op_mr_xop(int op);
	void mac_op_mf(int op);
	void mac_op_mf_xop(int op);
	void shift_op(int op);
	void shift_op_imm(int op);

	// memory access
	inline UINT16 data_read(UINT32 addr);
	inline void data_write(UINT32 addr, UINT16 data);
	inline UINT16 io_read(UINT32 addr);
	inline void io_write(UINT32 addr, UINT16 data);
	inline UINT32 program_read(UINT32 addr);
	inline void program_write(UINT32 addr, UINT32 data);
	inline UINT32 opcode_read();

	// register read/write
	inline void update_i(int which);
	inline void update_l(int which);
	inline void write_reg0(int regnum, INT32 val);
	inline void write_reg1(int regnum, INT32 val);
	inline void write_reg2(int regnum, INT32 val);
	inline void write_reg3(int regnum, INT32 val);
	inline INT32 read_reg0(int regnum);
	inline INT32 read_reg1(int regnum);
	inline INT32 read_reg2(int regnum);
	inline INT32 read_reg3(int regnum);

	// interrupts
	virtual bool generate_irq(int which, int indx = 0) = 0;
	virtual void check_irqs() = 0;

	// internal state
	static const int PC_STACK_DEPTH		= 16;
	static const int CNTR_STACK_DEPTH	= 4;
	static const int STAT_STACK_DEPTH	= 4;
	static const int LOOP_STACK_DEPTH	= 4;

	// 16-bit registers that can be loaded signed or unsigned
	union adsp_reg16
	{
		UINT16	u;
		INT16	s;
	};

	// the SHIFT result register is 32 bits
	union adsp_shift
	{
#ifdef LSB_FIRST
		struct { adsp_reg16 sr0, sr1; } srx;
#else
		struct { adsp_reg16 sr1, sr0; } srx;
#endif
		UINT32 sr;
	};

	// the MAC result register is 40 bits
	union adsp_mac
	{
#ifdef LSB_FIRST
		struct { adsp_reg16 mr0, mr1, mr2, mrzero; } mrx;
		struct { UINT32 mr0, mr1; } mry;
#else
		struct { adsp_reg16 mrzero, mr2, mr1, mr0; } mrx;
		struct { UINT32 mr1, mr0; } mry;
#endif
		UINT64 mr;
	};

	// core registers which are replicated
	struct adsp_core
	{
		// ALU registers
		adsp_reg16	ax0, ax1;
		adsp_reg16	ay0, ay1;
		adsp_reg16	ar;
		adsp_reg16	af;

		// MAC registers
		adsp_reg16	mx0, mx1;
		adsp_reg16	my0, my1;
		adsp_mac	mr;
		adsp_reg16	mf;

		// SHIFT registers
		adsp_reg16	si;
		adsp_reg16	se;
		adsp_reg16	sb;
		adsp_shift	sr;

		// dummy registers
		adsp_reg16	zero;
	};

	// configuration
	const address_space_config		m_program_config;
	const address_space_config		m_data_config;
	UINT32							m_chip_type;

	// other CPU registers
	UINT32				m_pc;
	UINT32				m_ppc;
	UINT32				m_loop;
	UINT32				m_loop_condition;
	UINT32				m_cntr;

	// status registers
	UINT32				m_astat;
	UINT32				m_sstat;
	UINT32				m_mstat;
	UINT32				m_mstat_prev;
	UINT32				m_astat_clear;
	UINT32				m_idle;

	// live set of core registers
	adsp_core			m_core;

	// memory addressing registers
	UINT32				m_i[8];
	INT32				m_m[8];
	UINT32				m_l[8];
	UINT32				m_lmask[8];
	UINT32				m_base[8];
	UINT8				m_px;

	// stacks
	UINT32				m_loop_stack[LOOP_STACK_DEPTH];
	UINT32				m_cntr_stack[CNTR_STACK_DEPTH];
	UINT32				m_pc_stack[PC_STACK_DEPTH];
	UINT16				m_stat_stack[STAT_STACK_DEPTH][3];
	INT32				m_pc_sp;
	INT32				m_cntr_sp;
	INT32				m_stat_sp;
	INT32				m_loop_sp;

	// external I/O
	UINT8				m_flagout;
	UINT8				m_flagin;
	UINT8				m_fl0;
	UINT8				m_fl1;
	UINT8				m_fl2;
	UINT16				m_idma_addr;
	UINT16				m_idma_cache;
	UINT8				m_idma_offs;

	// interrupt handling
	UINT16				m_imask;
	UINT8				m_icntl;
	UINT16				m_ifc;
    UINT8   			m_irq_state[9];
    UINT8   			m_irq_latch[9];

	// other internal states
    int					m_icount;
	int					m_mstat_mask;
	int					m_imask_mask;

	// register maps
	INT16 *				m_read0_ptr[16];
	UINT32 *			m_read1_ptr[16];
	UINT32 *			m_read2_ptr[16];
	void *				m_alu_xregs[8];
	void *				m_alu_yregs[4];
	void *				m_mac_xregs[8];
	void *				m_mac_yregs[4];
	void *				m_shift_xregs[8];

	// alternate core registers (at end for performance)
	adsp_core			m_alt;

	// address spaces
    address_space *		m_program;
    address_space *		m_data;
    address_space *		m_io;
    direct_read_data *	m_direct;

	// tables
	UINT8				m_condition_table[0x1000];
	UINT16				m_mask_table[0x4000];
	UINT16				m_reverse_table[0x4000];

	// debugging
#if ADSP_TRACK_HOTSPOTS
	UINT32				m_pcbucket[0x4000];
#endif

	// flag definitions
	static const int SSFLAG		= 0x80;
	static const int MVFLAG		= 0x40;
	static const int QFLAG		= 0x20;
	static const int SFLAG		= 0x10;
	static const int CFLAG		= 0x08;
	static const int VFLAG		= 0x04;
	static const int NFLAG		= 0x02;
	static const int ZFLAG		= 0x01;
};


// ======================> adsp2100_device

class adsp2100_device : public adsp21xx_device
{
public:
	// construction/destruction
	adsp2100_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);

protected:
	// device_execute_interface overrides
	virtual UINT32 execute_input_lines() const;

	// device_memory_interface overrides
	virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;

	// interrupts
	virtual bool generate_irq(int which, int indx);
	virtual void check_irqs();
};


// ======================> adsp2101_device

class adsp2101_device : public adsp21xx_device
{
public:
	// construction/destruction
	adsp2101_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);

protected:
	adsp2101_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 chiptype);

	// device_execute_interface overrides
	virtual UINT32 execute_input_lines() const;

	// device_memory_interface overrides
	virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;

	// interrupts
	virtual bool generate_irq(int which, int indx);
	virtual void check_irqs();
};


// ======================> adsp2181_device

class adsp2181_device : public adsp21xx_device
{
public:
	// construction/destruction
	adsp2181_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);

protected:
	// device_execute_interface overrides
	virtual UINT32 execute_input_lines() const;

	// device_memory_interface overrides
	virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;

	// interrupts
	virtual bool generate_irq(int which, int indx);
	virtual void check_irqs();

	// address spaces
	const address_space_config		m_io_config;

public:
	// public interfaces
	void idma_addr_w(UINT16 data);
	UINT16 idma_addr_r();
	void idma_data_w(UINT16 data);
	UINT16 idma_data_r();
};


// ======================> trivial variants

class adsp2104_device : public adsp2101_device
{
public:
	adsp2104_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
};

class adsp2105_device : public adsp2101_device
{
public:
	adsp2105_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
};

class adsp2115_device : public adsp2101_device
{
public:
	adsp2115_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
};



// device type definition
extern const device_type ADSP2100;
extern const device_type ADSP2101;
extern const device_type ADSP2104;
extern const device_type ADSP2105;
extern const device_type ADSP2115;
extern const device_type ADSP2181;


#endif /* __ADSP2100_H__ */
