// license:BSD-3-Clause
// copyright-holders:Ted Green
//*************************************
// iteagle fpga device
//*************************************
#ifndef MAME_MACHINE_ITEAGLE_FPGA_H
#define MAME_MACHINE_ITEAGLE_FPGA_H

#include "machine/pci.h"
#include "machine/nvram.h"
#include "machine/eepromser.h"
#include "machine/z80scc.h"
#include "bus/rs232/rs232.h"
#include "screen.h"

// Functional emulation of AMD AM85C30 serial controller
// Two channels, A & B
class iteagle_am85c30
{
public:
	void reset();
	void write_control(uint8_t data, int channel);
	uint8_t read_control(int channel);
	void write_data(uint8_t data, int channel);
	uint8_t read_data(int channel);
	void write_rx_str(int channel, std::string resp);
	std::string get_tx_str(int channel) { return m_serial_tx[channel]; };
	void clear_tx_str(int channel) { m_serial_tx[channel].clear(); };
	bool check_interrupt() { return (m_rr_regs[0][3] != 0); };
private:
	uint8_t m_rr_regs[2][16];
	uint8_t m_wr_regs[2][16];
	std::string m_serial_tx[2];
	std::string m_serial_rx[2];
};

class iteagle_fpga_device : public pci_device
{
public:
	template <typename T, typename U>
	iteagle_fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&screen_tag, U &&cpu_tag, int irq_num, int serial_num)
		: iteagle_fpga_device(mconfig, tag, owner, clock)
	{
		set_screen_tag(std::forward<T>(screen_tag));
		set_irq_info(std::forward<U>(cpu_tag), irq_num, serial_num);
	}
	iteagle_fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	void set_init_info(int version, int seq_init) {m_version=version; m_seq_init=seq_init;}
	template <typename T> void set_screen_tag(T &&tag) { m_screen.set_tag(std::forward<T>(tag)); }
	template <typename T> void set_irq_info(T &&tag, const int irq_num, int serial_num)
	{ m_cpu.set_tag(std::forward<T>(tag)); m_irq_num = irq_num; m_serial_irq_num = serial_num; }

	DECLARE_WRITE_LINE_MEMBER(vblank_update);
	void serial_rx_w(uint8_t data);

	enum { IO_SYSTEM, IO_IN1, IO_SW5, IO_NUM };
	template <unsigned N> auto in_callback() { return m_in_cb[N].bind(); }
	auto trackx_callback() { return m_trackx_cb.bind(); }
	auto tracky_callback() { return m_tracky_cb.bind(); }
	auto gunx_callback() { return m_gunx_cb.bind(); }
	auto guny_callback() { return m_guny_cb.bind(); }

protected:
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
	virtual void device_add_mconfig(machine_config &config) override;

private:
	required_device<nvram_device> m_rtc;
	optional_device<nvram_device> m_e1_nvram;
	required_device<scc85c30_device> m_scc1;
	required_device<screen_device> m_screen;
	required_device<device_execute_interface> m_cpu;
	devcb_read16::array<3> m_in_cb;
	devcb_read8 m_trackx_cb;
	devcb_read8 m_tracky_cb;
	devcb_read16 m_gunx_cb;
	devcb_read16 m_guny_cb;

	emu_timer *     m_timer;
	int m_irq_num;
	int m_serial_irq_num;

	uint32_t m_fpga_regs[0x20 / 4];
	uint32_t m_rtc_regs[0x800 / 4];
	uint32_t m_e1_nv_data[0x40 / 4];
	uint32_t m_e1_ram[0x10000 / 4];
	uint32_t m_prev_reg;

	uint32_t m_version;
	uint32_t m_seq_init;
	uint32_t m_seq;
	uint32_t m_seq_rem1, m_seq_rem2;

	int m_vblank_state;
	int m_gun_x, m_gun_y;

	iteagle_am85c30 m_serial0_1;
	iteagle_am85c30 m_serial2_3;

	void update_sequence(uint32_t data);
	void update_sequence_eg1(uint32_t data);

	void rtc_map(address_map &map);
	void fpga_map(address_map &map);
	void ram_map(address_map &map);

	uint32_t fpga_r(offs_t offset, uint32_t mem_mask = ~0);
	void fpga_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t rtc_r(offs_t offset, uint32_t mem_mask = ~0);
	void rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	uint32_t e1_nvram_r(offs_t offset, uint32_t mem_mask = ~0);
	void e1_nvram_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
	uint32_t e1_ram_r(offs_t offset, uint32_t mem_mask = ~0);
	void e1_ram_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	DECLARE_WRITE_LINE_MEMBER(serial_interrupt);
};

class iteagle_eeprom_device : public pci_device {
public:
	iteagle_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
	// optional information overrides
	virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
							uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;

	void set_info(int sw_version, int hw_version) {m_sw_version=sw_version; m_hw_version=hw_version;}

protected:
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_add_mconfig(machine_config &config) override;

private:
	address_space *m_memory_space;
	uint16_t m_sw_version;
	uint8_t m_hw_version;

	std::array<uint16_t, 0x40> m_iteagle_default_eeprom;

	void eeprom_map(address_map &map);
	uint32_t eeprom_r(offs_t offset, uint32_t mem_mask = ~0);
	void eeprom_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

	required_device<eeprom_serial_93cxx_device> m_eeprom;
};

// Mimic Cypress CY82C693 Peripheral Controller
class iteagle_periph_device : public pci_device {
public:
	iteagle_periph_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

protected:
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_add_mconfig(machine_config &config) override;

private:
	optional_device<nvram_device> m_rtc;

	uint32_t m_ctrl_regs[0xd0/4];
	uint8_t m_rtc_regs[0x100];

	void ctrl_map(address_map &map);

	uint32_t ctrl_r(offs_t offset, uint32_t mem_mask = ~0);
	void ctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);

};

DECLARE_DEVICE_TYPE(ITEAGLE_FPGA, iteagle_fpga_device)
DECLARE_DEVICE_TYPE(ITEAGLE_EEPROM, iteagle_eeprom_device)
DECLARE_DEVICE_TYPE(ITEAGLE_PERIPH, iteagle_periph_device)

#endif // MAME_MACHINE_ITEAGLE_FPGA_H
