// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/*
 * vme.cpp
 *
 * The Versabus-E was standardized as the VME bus by VITA 1981 for Europe
 * in the single or double Euroboard form factor. Several standard revs has
 * been approved since then up until recently and the VME64 revision.
 *
 * This bus driver starts with Versabus and VME rev C.
 *   http://bitsavers.informatik.uni-stuttgart.de/pdf/motorola/versabus/M68KVBS_VERSAbus_Specification_Manual_Jul81.pdf
 *
 * Acronymes from the specification
 * ---------------------------------
 * BACKPLANE  - A printed circuit board which provides the interconnection path
                between other printed circuit cards.
   SLOT       - A single position at which a card may be inserted into the backplane.
                One slot may consist of more than one edge connector.
   BOARD/CARD - Interchangeable terms representing one printed circuit board capable
                of being inserted into the backplane and containing a collection of
                electronic components.
   MODULE     - A collection of electronic components with a single functional
                purpose. More than one module may exist on the same card, but one
                module should never be spread over multiple cards.
   MASTER     - A functional module capable of initiating data bus transfers.
   REQUESTER  - A functional module capable of requesting control of the data
                transfer bus.
   INTERRUPT  - A functional module capable of detecting interrupt requests
   HANDLER      and initiating appropriate responses.
   MASTER     - The combination of a MASTER, REQUESTER, INTERRUPT HANDLER, and
   SUB-SYSTEM   (optionally) an INTERRUPTER, which function together and which
                must be on the same card.

   NOTE! All MASTERS, REQUESTERS, and INTERRUPT HANDLERS must be pieces of a
         MASTER SUB-SYSTEM.

   SLAVE       - A functional module capable of responding to data transfer
                 operations generated by a MASTER.
   INTERRUPTER - A functional module capable of requesting service from a MASTER
                 SUB-SYSTEM by generating an interrupt request.
   SLAVE       - The combination of a SLAVE and INTERRUPTER which function together
   SUB-SYSTEM    and which must be on the same card.

   NOTE! All INTERRUPTERS must be part of either SLAVE SUB-SYSTEMS or MASTER
         SUB-SYSTEMS. However, SLAVES may exist as stand-alone elements.
         Such SLAVES will never be called SLAVE SUB-SYSTEMS.

   CONTROLLER  - The combination of modules used to provide utility and emergency
   SUB-SYSTEM    signals for the VERSAbus. There will always be one and only one
                 CONTROLLER SUB-SYSTEM. It can contain the following functional
                 modules:

                 a. Data Transfer Bus ARBITER
                 b. Emergency Data Transfer Bus REQUESTER
                 c. Power up/power down MASTER
                 d. System clock driver
                 e. System reset driver
                 f. System test controller
                 g. Power monitor (for AC clock and AC fail driver)

    In any VERSAbus system, only one each of the above functional modules will exist.
    The slot numbered Al is designated as the controller sub-system slot because the
    user will typically provide modules a through d on the board residing in this
    slot. System reset and the system test  controller are typically connected to
    an operator control panel and may be located elsewhere. The power monitor is
    interfaced to the incoming AC power source and may also be located remotely.
*/

/*
 * TODO:
 *  - bus arbitration
 *  - block, rmw and address-only cycles
 *  - utility bus (serial clock/data, acfail, sysreset, sysfail)
 *  - variants (16-bit backplanes, VME64)
 *  - location monitors
 */

#include "emu.h"
#include "vme.h"

#define VERBOSE 0
#include "logmacro.h"

DEFINE_DEVICE_TYPE(VME, vme_bus_device, "vme", "VME bus")
DEFINE_DEVICE_TYPE(VME_SLOT, vme_slot_device, "vme_slot", "VME slot")

vme_bus_device::vme_bus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, u8 datawidth)
	: device_t(mconfig, VME, tag, owner, clock)
	, device_memory_interface(mconfig, *this)
	, m_asc
	{
		{ "iack",   ENDIANNESS_BIG, datawidth, 4, -2, address_map_constructor(FUNC(vme_bus_device::iack), this) },
		{}, {}, {}, {}, {}, {}, {}, {},
		{ "a32_09", ENDIANNESS_BIG, datawidth, 32, 0, address_map_constructor(FUNC(vme_bus_device::a32), this) },
		{ "a32_0a", ENDIANNESS_BIG, datawidth, 32, 0, address_map_constructor(FUNC(vme_bus_device::a32), this) },
		{ "a32_0b", ENDIANNESS_BIG, datawidth, 32, 0, address_map_constructor(FUNC(vme_bus_device::a32), this) },
		{},
		{ "a32_0d", ENDIANNESS_BIG, datawidth, 32, 0, address_map_constructor(FUNC(vme_bus_device::a32), this) },
		{ "a32_0e", ENDIANNESS_BIG, datawidth, 32, 0, address_map_constructor(FUNC(vme_bus_device::a32), this) },
		{ "a32_0f", ENDIANNESS_BIG, datawidth, 32, 0, address_map_constructor(FUNC(vme_bus_device::a32), this) },
		{}, {}, {}, {}, {}, {}, {}, {},
		{}, {}, {}, {}, {}, {}, {}, {},
		{}, {}, {}, {}, {}, {}, {}, {},
		{},
		{ "a16_29", ENDIANNESS_BIG, datawidth, 16, 0, address_map_constructor(FUNC(vme_bus_device::a16), this) },
		{}, {}, {},
		{ "a16_2d", ENDIANNESS_BIG, datawidth, 16, 0, address_map_constructor(FUNC(vme_bus_device::a16), this) },
		{}, {},
		{}, {}, {}, {}, {}, {}, {}, {},
		{},
		{ "a24_39", ENDIANNESS_BIG, datawidth, 24, 0, address_map_constructor(FUNC(vme_bus_device::a24), this) },
		{ "a24_3a", ENDIANNESS_BIG, datawidth, 24, 0, address_map_constructor(FUNC(vme_bus_device::a24), this) },
		{ "a24_3b", ENDIANNESS_BIG, datawidth, 24, 0, address_map_constructor(FUNC(vme_bus_device::a24), this) },
		{},
		{ "a24_3d", ENDIANNESS_BIG, datawidth, 24, 0, address_map_constructor(FUNC(vme_bus_device::a24), this) },
		{ "a24_3e", ENDIANNESS_BIG, datawidth, 24, 0, address_map_constructor(FUNC(vme_bus_device::a24), this) },
		{ "a24_3f", ENDIANNESS_BIG, datawidth, 24, 0, address_map_constructor(FUNC(vme_bus_device::a24), this) },
	}
	, m_irq(*this)
	, m_berr(*this)
	, m_irq_count{}
	, m_iack(false)
{
}

void vme_bus_device::device_start()
{
	save_item(NAME(m_irq_count));
	save_item(NAME(m_iack));
}

device_memory_interface::space_config_vector vme_bus_device::memory_space_config() const
{
	return space_config_vector{
		std::make_pair(vme::IACK,  &m_asc[vme::IACK]),

		std::make_pair(vme::AM_09, &m_asc[vme::AM_09]),
		std::make_pair(vme::AM_0a, &m_asc[vme::AM_0a]),
		std::make_pair(vme::AM_0b, &m_asc[vme::AM_0b]),
		std::make_pair(vme::AM_0d, &m_asc[vme::AM_0d]),
		std::make_pair(vme::AM_0e, &m_asc[vme::AM_0e]),
		std::make_pair(vme::AM_0f, &m_asc[vme::AM_0f]),

		std::make_pair(vme::AM_29, &m_asc[vme::AM_29]),
		std::make_pair(vme::AM_2d, &m_asc[vme::AM_2d]),

		std::make_pair(vme::AM_39, &m_asc[vme::AM_39]),
		std::make_pair(vme::AM_3a, &m_asc[vme::AM_3a]),
		std::make_pair(vme::AM_3b, &m_asc[vme::AM_3b]),
		std::make_pair(vme::AM_3d, &m_asc[vme::AM_3d]),
		std::make_pair(vme::AM_3e, &m_asc[vme::AM_3e]),
		std::make_pair(vme::AM_3f, &m_asc[vme::AM_3f]),
	};
}

u32 vme_bus_device::read_iack(address_space &space, offs_t offset, u32 mem_mask)
{
	if (!device().machine().side_effects_disabled())
	{
		// decode the interrupt number from address lines A03-A01
		unsigned const irq = BIT(offset, 1, 3);

		LOG("read_iack 0x%08x mem_mask 0x%08x /IRQ%u /IACKIN asserted (%s)\n", offset, mem_mask, irq, machine().describe_context());

		// enable interrupt acknowledge daisy chain
		m_iack = true;
	}

	return space.unmap();
}

// generate bus errors on unmapped read cycles
u32 vme_bus_device::read_berr(address_space &space, offs_t offset, u32 mem_mask)
{
	u32 const data = space.unmap();

	if (!device().machine().side_effects_disabled())
	{
		LOG("read_berr %s 0x%08x data 0x%08x mem_mask 0x%08x /BERR asserted (%s)\n", space.name(), offset << 2, data, mem_mask, machine().describe_context());

		m_berr(0);
	}

	return data;
}

// generate bus errors on unmapped write cycles
void vme_bus_device::write_berr(address_space &space, offs_t offset, u32 data, u32 mem_mask)
{
	if (!device().machine().side_effects_disabled())
	{
		LOG("write_berr %s 0x%08x data 0x%08x mem_mask 0x%08x /BERR asserted (%s)\n", space.name(), offset << 2, data, mem_mask, machine().describe_context());

		m_berr(0);
	}
}

void vme_bus_device::iack(address_map &map)
{
	map(0x0, 0xf).r(FUNC(vme_bus_device::read_iack));
}

void vme_bus_device::a16(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(vme_bus_device::read_berr), FUNC(vme_bus_device::write_berr));
}

void vme_bus_device::a24(address_map &map)
{
	map(0x00'0000, 0xff'ffff).rw(FUNC(vme_bus_device::read_berr), FUNC(vme_bus_device::write_berr));
}

void vme_bus_device::a32(address_map &map)
{
	map(0x0000'0000, 0xffff'ffff).rw(FUNC(vme_bus_device::read_berr), FUNC(vme_bus_device::write_berr));
}

template <unsigned I> void vme_bus_device::irq_w(int state)
{
	bool const irq_active = m_irq_count[I - 1];

	// count assertions
	if (!state)
		m_irq_count[I - 1]++;
	else if (irq_active)
		m_irq_count[I - 1]--;

	// update line state
	if (irq_active ^ bool(m_irq_count[I - 1]))
	{
		LOG("bus /IRQ%u %s\n", I, state ? "cleared" : "asserted");
		m_irq[I - 1](state);
	}
}

template void vme_bus_device::irq_w<1>(int state);
template void vme_bus_device::irq_w<2>(int state);
template void vme_bus_device::irq_w<3>(int state);
template void vme_bus_device::irq_w<4>(int state);
template void vme_bus_device::irq_w<5>(int state);
template void vme_bus_device::irq_w<6>(int state);
template void vme_bus_device::irq_w<7>(int state);

vme_slot_device::vme_slot_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
	: device_t(mconfig, VME_SLOT, tag, owner, clock)
	, device_slot_interface(mconfig, *this)
{
}

void vme_slot_device::device_config_complete()
{
	device_vme_card_interface *const card(dynamic_cast<device_vme_card_interface *>(get_card_device()));
	if (card && owner())
		card->set_bus(*dynamic_cast<vme_bus_device *>(owner()));
}

void vme_slot_device::device_start()
{
}

#undef LOG_OUTPUT_FUNC
#define LOG_OUTPUT_FUNC device().logerror

device_vme_card_interface::device_vme_card_interface(machine_config const &mconfig, device_t &device)
	: device_interface(device, "vme")
	, m_bus(nullptr)
	, m_berr(*this)
	, m_irq(*this)
	, m_iack(*this, 0)
	, m_irq_active(0)
	, m_master(false)
{
}

void device_vme_card_interface::interface_post_start()
{
	device().save_item(NAME(m_irq_active));
	device().save_item(NAME(m_master));

	/*
	 * Install a read tap into the bus interrupt acknowledge address space,
	 * simulating a daisy chain and giving each card an opportunity to respond
	 * to the interrupt acknowledge cycle in slot sequence. Empty slots do not
	 * respond, acting as if they have /IACKIN jumpered to /IACKOUT.
	 */
	vme_space(vme::IACK).install_read_tap(0x0, 0xf, "iack",
		[this](offs_t offset, u32 &data, u32 mem_mask)
		{
			// decode the interrupt number from address lines A03-A01
			unsigned const irq = BIT(offset, 1, 3);

			/*
			 * An interrupt acknowledge cycle is being asserted. This device
			 * will only respond if the upstream interrupt acknowledge daisy
			 * chain input (/IACKIN) is asserted and this device is asserting
			 * the interrupt number being acknowledged.
			 */
			if (!m_bus->iackin_r() && BIT(m_irq_active, irq))
			{
				// deassert interrupt acknowledge daisy chain output
				m_bus->iackout_w(1);

				// read and return the interrupting device status/ID
				data = m_iack(irq);

				LOG("vme iack irq %u status 0x%08x /IACKOUT deasserted (%s)\n", irq, data, device().machine().describe_context());
			}
		});
}

void device_vme_card_interface::set_bus(vme_bus_device &bus)
{
	m_bus = &bus;

	// route incoming bus errors to a card only when it is the bus master
	m_bus->berr().append(
		[this](int state)
		{
			if (m_master)
			{
				LOG("vme incoming /BERR %s\n", state ? "cleared" : "asserted");
				m_berr(state);
			}
		});

	// route incoming interrupt requests to all cards
	m_bus->irq<1>().append([this](int state) { LOG("vme incoming /IRQ1 %s\n", state ? "cleared" : "asserted"); m_irq[0](state); });
	m_bus->irq<2>().append([this](int state) { LOG("vme incoming /IRQ2 %s\n", state ? "cleared" : "asserted"); m_irq[1](state); });
	m_bus->irq<3>().append([this](int state) { LOG("vme incoming /IRQ3 %s\n", state ? "cleared" : "asserted"); m_irq[2](state); });
	m_bus->irq<4>().append([this](int state) { LOG("vme incoming /IRQ4 %s\n", state ? "cleared" : "asserted"); m_irq[3](state); });
	m_bus->irq<5>().append([this](int state) { LOG("vme incoming /IRQ5 %s\n", state ? "cleared" : "asserted"); m_irq[4](state); });
	m_bus->irq<6>().append([this](int state) { LOG("vme incoming /IRQ6 %s\n", state ? "cleared" : "asserted"); m_irq[5](state); });
	m_bus->irq<7>().append([this](int state) { LOG("vme incoming /IRQ7 %s\n", state ? "cleared" : "asserted"); m_irq[6](state); });
}

template <unsigned I> void device_vme_card_interface::vme_irq_w(int state)
{
	// check whether the interrupt state has changed
	if (BIT(m_irq_active, I) == state)
	{
		LOG("vme_irq_w outgoing /IRQ%u %s\n", I, state ? "cleared" : "asserted");

		// record device interrupt state
		if (!state)
			m_irq_active |= 1U << I;
		else
			m_irq_active &= ~(1U << I);

		// update the bus irq state
		m_bus->irq_w<I>(state);
	}
}

template void device_vme_card_interface::vme_irq_w<1>(int state);
template void device_vme_card_interface::vme_irq_w<2>(int state);
template void device_vme_card_interface::vme_irq_w<3>(int state);
template void device_vme_card_interface::vme_irq_w<4>(int state);
template void device_vme_card_interface::vme_irq_w<5>(int state);
template void device_vme_card_interface::vme_irq_w<6>(int state);
template void device_vme_card_interface::vme_irq_w<7>(int state);

//--------------- P2 connector below--------------------------
/*
The VME P2 connector only specifies the mid row B of the connector
and leaves row A and C to be system specific. This has resulted in
a number of variants that has been more or less standardized:

- VMXbus was available on the first VME boards but not standardized hence
an almost compatible variant was developed by Motorola called MVMX32bus.
- VSBbus replaced VMX and MVMX32and was approved by IEEE in 1988
- SCSA is a P2 standardization for telephony voice and facsimile applications
- SkyChannel is packet switched P2 architecture from Sky Computers and standardized
through VITA/VSO.
- RACEway is a 40Mhz P2 bus allowing 480MBps throughput from Mercusry Computers and
standardized through VITA/VSO.
- VME64 adds two more rows, called 'z' and 'd', of user defined pins to the P2 connector
- P2CI adds a PCI bus onto a VME64 P2 connector

URL:s
http://rab.ict.pwr.wroc.pl/dydaktyka/supwa/vme/secbuses.html
http://www.interfacebus.com/Design_Connector_VME_P2_Buses.html

TODO: Figure out a good way to let all these variants coexist and interconnect in a VME system

*/
