// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**********************************************************************

    Commodore Magic Voice cartridge emulation

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

/*

LA05-123 Pinout
---------------
                _____   _____
     NEXTP   1 |*    \_/     | 28  +5V
       PD0   2 |             | 27  _ROML2
       PD1   3 |             | 26  _ROML
       PD2   4 |             | 25  _I/O2
       PD3   5 |             | 24  _GAME
     CLEAR   6 |             | 23  PHI2
_RAM/EPROM   7 |  LA05-123   | 22  _ROMH2
       PB5   8 |  LA05-124   | 21  _ROMH
       PB6   9 |             | 20  CLOCK
     _6525  10 |             | 19  SDO
    _EPROM  11 |             | 18  NEXTS
      CA12  12 |             | 17  _DA/CA
      CA14  13 |             | 16  CA15
       GND  14 |_____________| 15  CA13


http://www.stefan-uhlmann.de/cbm/MVM/index.html

*/

/*

    TODO:

    - T6721A speech synthesis

*/

#include "emu.h"
#include "magic_voice.h"
#include "speaker.h"



//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define T6721A_TAG      "u5"
#define MOS6525_TAG     "u2"
#define CD40105_TAG     "u1"

#define A12 BIT(offset, 12)
#define A13 BIT(offset, 13)
#define A14 BIT(offset, 14)
#define A15 BIT(offset, 15)
#define PB5 BIT(m_tpi_pb, 5)
#define PB6 BIT(m_tpi_pb, 6)



//**************************************************************************
//  DEVICE DEFINITIONS
//**************************************************************************

DEFINE_DEVICE_TYPE(C64_MAGIC_VOICE, c64_magic_voice_cartridge_device, "c64_magic_voice", "C64 Magic Voice cartridge")


//-------------------------------------------------
//  tpi6525_interface tpi_intf
//-------------------------------------------------

WRITE_LINE_MEMBER( c64_magic_voice_cartridge_device::tpi_irq_w )
{
	m_slot->nmi_w(state);
}

uint8_t c64_magic_voice_cartridge_device::tpi_pa_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5       J1 _GAME
	    6       T6721 _EOS
	    7       FIFO DIR

	*/

	uint8_t data = 0;

	data |= m_exp->game_r(get_offset(m_ca), 1, 1, 1, 0, 0) << 5;
	data |= m_vslsi->eos_r() << 6;
	data |= m_fifo->dir_r() << 7;

	return data;
}

void c64_magic_voice_cartridge_device::tpi_pa_w(uint8_t data)
{
	/*

	    bit     description

	    0       FIFO D0
	    1       FIFO D1
	    2       FIFO D2
	    3       FIFO D3
	    4       FIFO SI
	    5
	    6
	    7

	*/

	m_fifo->write(data & 0x0f);
	m_fifo->si_w(BIT(data, 4));
}

uint8_t c64_magic_voice_cartridge_device::tpi_pb_r()
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5
	    6
	    7       J1 _EXROM

	*/

	uint8_t data = 0;

	data |= m_exp->exrom_r(get_offset(m_ca), 1, 1, 1, 0, 0) << 7;

	return data;
}

void c64_magic_voice_cartridge_device::tpi_pb_w(uint8_t data)
{
	/*

	    bit     description

	    0       T6721 D0
	    1       T6721 D1
	    2       T6721 D2
	    3       T6721 D3
	    4       T6721 _WR
	    5       LA05-124 pin 8 (DA/CA)
	    6       LA05-124 pin 9 (passthru)
	    7

	*/

	if (!BIT(m_tpi_pb, 4) && BIT(data, 4))
	{
		m_vslsi->write(data & 0x0f);
	}

	m_tpi_pb = data;
}

WRITE_LINE_MEMBER( c64_magic_voice_cartridge_device::tpi_ca_w )
{
	m_tpi_pc6 = state;
}

WRITE_LINE_MEMBER( c64_magic_voice_cartridge_device::tpi_cb_w )
{
	m_exrom = state;
}

//-------------------------------------------------
//  t6721_interface
//-------------------------------------------------

WRITE_LINE_MEMBER( c64_magic_voice_cartridge_device::phi2_w )
{
	if (state)
	{
		m_vslsi->di_w(m_pd & 0x01);

		m_pd >>= 1;
	}
}

WRITE_LINE_MEMBER( c64_magic_voice_cartridge_device::dtrd_w )
{
	m_fifo->so_w(!state);

	m_pd = m_fifo->read();
}

WRITE_LINE_MEMBER( c64_magic_voice_cartridge_device::apd_w )
{
	if (state)
	{
		m_fifo->reset();
		m_pd = 0;
	}
}


//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

void c64_magic_voice_cartridge_device::device_add_mconfig(machine_config &config)
{
	TPI6525(config, m_tpi, 0);
	m_tpi->out_irq_cb().set(FUNC(c64_magic_voice_cartridge_device::tpi_irq_w));
	m_tpi->in_pa_cb().set(FUNC(c64_magic_voice_cartridge_device::tpi_pa_r));
	m_tpi->out_pa_cb().set(FUNC(c64_magic_voice_cartridge_device::tpi_pa_w));
	m_tpi->in_pb_cb().set(FUNC(c64_magic_voice_cartridge_device::tpi_pb_r));
	m_tpi->out_pb_cb().set(FUNC(c64_magic_voice_cartridge_device::tpi_pb_w));
	m_tpi->out_ca_cb().set(FUNC(c64_magic_voice_cartridge_device::tpi_ca_w));
	m_tpi->out_cb_cb().set(FUNC(c64_magic_voice_cartridge_device::tpi_cb_w));

	CD40105(config, m_fifo, 0);
	m_fifo->in_ready_cb().set(m_tpi, FUNC(tpi6525_device::i3_w));

	SPEAKER(config, "mono").front_center();
	T6721A(config, m_vslsi, XTAL(640'000));
	m_vslsi->eos_handler().set(m_tpi, FUNC(tpi6525_device::i2_w));
	m_vslsi->phi2_handler().set(FUNC(c64_magic_voice_cartridge_device::phi2_w));
	m_vslsi->dtrd_handler().set(FUNC(c64_magic_voice_cartridge_device::dtrd_w));
	m_vslsi->apd_handler().set(FUNC(c64_magic_voice_cartridge_device::apd_w));
	m_vslsi->add_route(ALL_OUTPUTS, "mono", 0.25);

	C64_EXPANSION_SLOT(config, m_exp, DERIVED_CLOCK(1, 1), c64_expansion_cards, nullptr);
	m_exp->set_passthrough();
}



//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  c64_magic_voice_cartridge_device - constructor
//-------------------------------------------------

c64_magic_voice_cartridge_device::c64_magic_voice_cartridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, C64_MAGIC_VOICE, tag, owner, clock),
	device_c64_expansion_card_interface(mconfig, *this),
	m_vslsi(*this, T6721A_TAG),
	m_tpi(*this, MOS6525_TAG),
	m_fifo(*this, CD40105_TAG),
	m_exp(*this, "exp"),
	m_ca(0),
	m_tpi_pb(0x60),
	m_tpi_pc6(1),
	m_pd(0)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void c64_magic_voice_cartridge_device::device_start()
{
	// state saving
	save_item(NAME(m_tpi_pb));
	save_item(NAME(m_tpi_pc6));
	save_item(NAME(m_pd));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void c64_magic_voice_cartridge_device::device_reset()
{
	m_tpi->reset();

	m_exrom = 1;

	m_tpi_pb = 0x60;
	m_tpi_pc6 = 1;
	m_pd = 0;
}


//-------------------------------------------------
//  c64_cd_r - cartridge data read
//-------------------------------------------------

uint8_t c64_magic_voice_cartridge_device::c64_cd_r(offs_t offset, uint8_t data, int sphi2, int ba, int roml, int romh, int io1, int io2)
{
	if (!io2 && sphi2)
	{
		m_ca = offset;
		data = m_tpi->read(offset & 0x07);
	}

	if (PB6 && A13 && A15)
	{
		data = m_romh[(A14 << 13) | (offset & 0x1fff)];
	}

	int roml2 = !(!roml || (roml && !PB5 && A12 && A13 && !A14 && A15));
	int romh2 = !((!romh && !PB6) || (!PB5 && A12 && A13 && !A14 && !A15));

	data = m_exp->cd_r(get_offset(offset), data, sphi2, ba, roml2, romh2, io1, 1);

	return data;
}


//-------------------------------------------------
//  c64_cd_w - cartridge data write
//-------------------------------------------------

void c64_magic_voice_cartridge_device::c64_cd_w(offs_t offset, uint8_t data, int sphi2, int ba, int roml, int romh, int io1, int io2)
{
	if (!io2 && sphi2)
	{
		m_tpi->write(offset & 0x07, data);
	}

	int roml2 = !(!roml || (roml && !PB5 && A12 && A13 && !A14 && A15));
	int romh2 = !((!romh && !PB6) || (!PB5 && A12 && A13 && !A14 && !A15));

	m_exp->cd_w(get_offset(offset), data, sphi2, ba, roml2, romh2, io1, 1);
}


//-------------------------------------------------
//  c64_game_r - GAME read
//-------------------------------------------------

int c64_magic_voice_cartridge_device::c64_game_r(offs_t offset, int sphi2, int ba, int rw)
{
	return !((m_tpi_pc6 && sphi2) || (!m_tpi_pc6 && sphi2 && !PB5 && A12 && A13 && !A14));
}


//-------------------------------------------------
//  get_offset -
//-------------------------------------------------

offs_t c64_magic_voice_cartridge_device::get_offset(offs_t offset)
{
	if (!PB5 && A12 && A13 && !A14)
	{
		offset = ((m_tpi_pb & 0x0f) << 12) | (offset & 0xfff);
	}

	return offset;
}
