// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Angelo Salese
#ifndef MAME_NEC_PCE_CD_H
#define MAME_NEC_PCE_CD_H

#pragma once

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

#include "imagedev/cdromimg.h"
#include "machine/nvram.h"
#include "sound/cdda.h"
#include "sound/msm5205.h"

#define PCE_BRAM_SIZE               0x800
#define PCE_ADPCM_RAM_SIZE          0x10000
#define PCE_ACARD_RAM_SIZE          0x200000
#define PCE_CD_COMMAND_BUFFER_SIZE  0x100

#define PCE_CD_IRQ_TRANSFER_READY       0x40
#define PCE_CD_IRQ_TRANSFER_DONE        0x20
#define PCE_CD_IRQ_BRAM                 0x10 /* ??? */
#define PCE_CD_IRQ_SAMPLE_FULL_PLAY     0x08
#define PCE_CD_IRQ_SAMPLE_HALF_PLAY     0x04

#define PCE_CD_ADPCM_PLAY_FLAG      0x08
#define PCE_CD_ADPCM_STOP_FLAG      0x01

#define PCE_CD_DATA_FRAMES_PER_SECOND   75

enum {
	PCE_CD_CDDA_OFF = 0,
	PCE_CD_CDDA_PLAYING,
	PCE_CD_CDDA_PAUSED
};



// ======================> pce_cd_device

class pce_cd_device : public device_t,
					  public device_memory_interface
{
public:
	// construction/destruction
	pce_cd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	void update();

	void late_setup();

	void bram_w(offs_t offset, uint8_t data);
	void intf_w(offs_t offset, uint8_t data);
	void acard_w(offs_t offset, uint8_t data);
	uint8_t bram_r(offs_t offset);
	uint8_t intf_r(offs_t offset);
	uint8_t acard_r(offs_t offset);

protected:
	// device-level overrides
	virtual void device_start() override;
	virtual void device_add_mconfig(machine_config &config) override;
	virtual void device_reset() override;
	virtual space_config_vector memory_space_config() const override;

private:
	const address_space_config m_space_config;

	uint8_t cdc_status_r();
	void cdc_status_w(uint8_t data);
	uint8_t cdc_reset_r();
	void cdc_reset_w(uint8_t data);
	uint8_t irq_mask_r();
	void irq_mask_w(uint8_t data);
	uint8_t irq_status_r();
	uint8_t cdc_data_r();
	void cdc_data_w(uint8_t data);
	uint8_t bram_status_r();
	void bram_unlock_w(uint8_t data);
	uint8_t cdda_data_r(offs_t offset);
	uint8_t cd_data_r();
	uint8_t adpcm_dma_control_r();
	void adpcm_dma_control_w(uint8_t data);
	uint8_t adpcm_status_r();
	uint8_t adpcm_data_r();
	void adpcm_data_w(uint8_t data);
	void adpcm_address_lo_w(uint8_t data);
	void adpcm_address_hi_w(uint8_t data);
	uint8_t adpcm_address_control_r();
	void adpcm_address_control_w(uint8_t data);
	void adpcm_playback_rate_w(uint8_t data);
	void fader_control_w(uint8_t data);

	uint8_t m_reset_reg = 0;
	uint8_t m_irq_mask = 0;
	uint8_t m_irq_status = 0;
	uint8_t m_cdc_status = 0;
	uint8_t m_cdc_data = 0;
	uint8_t m_bram_status = 0;
	uint8_t m_adpcm_status = 0;
	uint16_t m_adpcm_latch_address = 0;
	uint8_t m_adpcm_control = 0;
	uint8_t m_adpcm_dma_reg = 0;
	uint8_t m_fader_ctrl = 0;

	void regs_map(address_map &map);
	void adpcm_stop(uint8_t irq_flag);
	void adpcm_play();
	void reply_status_byte(uint8_t status);
	void test_unit_ready();
	void read_6();
	void nec_set_audio_start_position();
	void nec_set_audio_stop_position();
	void nec_pause();
	void nec_get_subq();
	void nec_get_dir_info();
	void end_of_list();
	void handle_data_output();
	void handle_data_input();
	void handle_message_output();
	void handle_message_input();
	void set_irq_line(int num, int state);
	void set_adpcm_ram_byte(uint8_t val);
	uint8_t get_cd_data_byte();
	uint8_t get_adpcm_ram_byte();

	TIMER_CALLBACK_MEMBER(data_timer_callback);
	TIMER_CALLBACK_MEMBER(cdda_fadeout_callback);
	TIMER_CALLBACK_MEMBER(cdda_fadein_callback);
	TIMER_CALLBACK_MEMBER(adpcm_fadeout_callback);
	TIMER_CALLBACK_MEMBER(adpcm_fadein_callback);
	TIMER_CALLBACK_MEMBER(clear_ack);
	TIMER_CALLBACK_MEMBER(adpcm_dma_timer_callback);

	required_device<cpu_device> m_maincpu;

	std::unique_ptr<uint8_t[]>   m_bram;
	std::unique_ptr<uint8_t[]>   m_adpcm_ram;
	int     m_bram_locked = 0;
	int     m_adpcm_read_ptr = 0;
	uint8_t   m_adpcm_read_buf = 0;
	int     m_adpcm_write_ptr = 0;
	uint8_t   m_adpcm_write_buf = 0;
	int     m_adpcm_length = 0;
	int     m_adpcm_clock_divider = 0;
	uint32_t  m_msm_start_addr = 0;
	uint32_t  m_msm_end_addr = 0;
	uint32_t  m_msm_half_addr = 0;
	uint8_t   m_msm_nibble = 0;
	uint8_t   m_msm_idle = 0;
	uint8_t   m_msm_repeat = 0;

	/* SCSI signals */
	int     m_scsi_BSY = 0;   /* Busy. Bus in use */
	int     m_scsi_SEL = 0;   /* Select. Initiator has won arbitration and has selected a target */
	int     m_scsi_CD = 0;    /* Control/Data. Target is sending control (data) information */
	int     m_scsi_IO = 0;    /* Input/Output. Target is sending (receiving) information */
	int     m_scsi_MSG = 0;   /* Message. Target is sending or receiving a message */
	int     m_scsi_REQ = 0;   /* Request. Target is requesting a data transfer */
	int     m_scsi_ACK = 0;   /* Acknowledge. Initiator acknowledges that it is ready for a data transfer */
	int     m_scsi_ATN = 0;   /* Attention. Initiator has a message ready for the target */
	int     m_scsi_RST = 0;   /* Reset. Initiator forces all targets and any other initiators to do a warm reset */
	int     m_scsi_last_RST = 0;  /* To catch setting of RST signal */
	int     m_cd_motor_on = 0;
	int     m_selected = 0;
	std::unique_ptr<uint8_t[]>  m_command_buffer;
	int     m_command_buffer_index = 0;
	int     m_status_sent = 0;
	int     m_message_after_status = 0;
	int     m_message_sent = 0;
	std::unique_ptr<uint8_t[]> m_data_buffer;
	int     m_data_buffer_size = 0;
	int     m_data_buffer_index = 0;
	int     m_data_transferred = 0;

	/* Arcade Card specific */
	std::unique_ptr<uint8_t[]>  m_acard_ram;
	uint8_t   m_acard_latch = 0;
	uint8_t   m_acard_ctrl[4];
	uint32_t  m_acard_base_addr[4];
	uint16_t  m_acard_addr_offset[4];
	uint16_t  m_acard_addr_inc[4];
	uint32_t  m_acard_shift = 0;
	uint8_t   m_acard_shift_reg = 0;

	uint32_t  m_current_frame = 0;
	uint32_t  m_end_frame = 0;
	uint32_t  m_last_frame = 0;
	uint8_t   m_cdda_status = 0;
	uint8_t   m_cdda_play_mode = 0;
	std::unique_ptr<uint8_t[]>   m_subcode_buffer;
	uint8_t   m_end_mark = 0;

	required_device<msm5205_device> m_msm;
	required_device<cdda_device> m_cdda;
	required_device<nvram_device> m_nvram;
	required_device<cdrom_image_device> m_cdrom;

	const cdrom_file::toc*  m_toc = nullptr;
	emu_timer   *m_data_timer = nullptr;
	emu_timer   *m_adpcm_dma_timer = nullptr;

	emu_timer   *m_cdda_fadeout_timer = nullptr;
	emu_timer   *m_cdda_fadein_timer = nullptr;
	double       m_cdda_volume = 0;
	emu_timer   *m_adpcm_fadeout_timer = nullptr;
	emu_timer   *m_adpcm_fadein_timer = nullptr;
	double       m_adpcm_volume = 0;

	emu_timer   *m_ack_clear_timer = nullptr;

	DECLARE_WRITE_LINE_MEMBER(msm5205_int);
	void nvram_init(nvram_device &nvram, void *data, size_t size);

	DECLARE_WRITE_LINE_MEMBER(cdda_end_mark_cb);
};



// device type definition
DECLARE_DEVICE_TYPE(PCE_CD, pce_cd_device)

#endif // MAME_NEC_PCE_CD_H
