// license:BSD-3-Clause
// copyright-holders:AJR
/**********************************************************************

    RST interrupt vector buffer

    The hardware circuit emulated by this device, which is generally
    made of various TTL gates and buffers, combines three independent
    interrupt request lines into a single level trigger and also
    generates a 8080-compatible RST opcode vector when the CPU
    acknowledges the interrupt, reflecting the current state of all
    three inputs.

    The positive version of this device treats all of its inputs and
    outputs as active high, in accordance with 8080 convention and
    some actual practice. An active-low IRQ would need to be supplied
    for a Z80, which may be used with this type of circuit in IM 0,
    though the negative polarity of the Z80 IRQ is not currently
    accounted for by MAME.

    Table of interrupt vectors for positive version:

        Active    Vector      Instruction    Address of
        inputs    fetched     executed       service routine
        -------   -------     -----------    ---------------
        1 only    CF          RST 1          0008
        2 only    D7          RST 2          0010
        4 only    E7          RST 4          0020
        1 and 2   DF          RST 3          0018
        1 and 4   EF          RST 5          0028
        2 and 4   F7          RST 6          0030
        1, 2, 4   FF          RST 7          0038

    An alternate version of this device buffers the logical negative
    of the request lines. This variant is primarily associated with
    Z80-based sound systems. Here the inputs, asserted singly, will
    produce a RST 18H, RST 28H or RST 30H interrupt (using Zilog
    rather than Intel syntax). When multiple request lines are active
    at once, the result is equivalent to the logical AND of the
    corresponding base vectors.

    This type of circuit features no built-in edge triggers or
    prioritization. Since its inputs are not latched, a spurious
    interrupt may occur if one or more lines are asserted just long
    enough for the CPU to accept the interrupt, but deasserted before
    the CPU's interrupt acknowledge cycle completes. In the positive
    version, the vector placed on the bus (C7) is a RST 0 instruction,
    which (after the return address is pushed onto the stack) will
    transfer control to 0000 as if the CPU had just been reset. The
    negative version provides a more useful vector (RST 7/RST 38H)
    for spurious interrupts, but will still generate RST 0 if all
    three inputs are active at once, though this situation cannot
    occur if the buffer only admits two request lines (as is
    generally the case with this version).

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

#include "emu.h"
#include "machine/rstbuf.h"

// device type definition
DEFINE_DEVICE_TYPE(RST_POS_BUFFER, rst_pos_buffer_device, "rst_pos_buffer", "RST Interrupt Buffer (positive modification)")
DEFINE_DEVICE_TYPE(RST_NEG_BUFFER, rst_neg_buffer_device, "rst_neg_buffer", "RST Interrupt Buffer (negative modification)")


//**************************************************************************
//  RST INTERRUPT BUFFER DEVICE
//**************************************************************************

//-------------------------------------------------
//  rst_buffer_device - constructor
//-------------------------------------------------

rst_buffer_device::rst_buffer_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, type, tag, owner, clock)
	, m_int_cb(*this)
	, m_input_buffer(0)
{
}


//-------------------------------------------------
//  rst_pos_buffer_device - constructor
//-------------------------------------------------

rst_pos_buffer_device::rst_pos_buffer_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: rst_buffer_device(mconfig, RST_POS_BUFFER, tag, owner, clock)
{
}


//-------------------------------------------------
//  rst_neg_buffer_device - constructor
//-------------------------------------------------

rst_neg_buffer_device::rst_neg_buffer_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: rst_buffer_device(mconfig, RST_NEG_BUFFER, tag, owner, clock)
{
}


//-------------------------------------------------
//  device_resolve_objects - resolve objects that
//  may be needed for other devices to set
//  initial conditions at start time
//-------------------------------------------------

void rst_buffer_device::device_resolve_objects()
{
	m_int_cb.resolve_safe();
}


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

void rst_buffer_device::device_start()
{
	// save input state
	save_item(NAME(m_input_buffer));
}


//-------------------------------------------------
//  sync_set_input - synchronize asserted input
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(rst_buffer_device::sync_set_input)
{
	if (m_input_buffer == 0)
	{
		// assert interrupt when one line becomes active
		m_input_buffer = param;
		m_int_cb(1);
	}
	else
		m_input_buffer |= param;
}


//-------------------------------------------------
//  sync_clear_input - synchronize cleared input
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(rst_buffer_device::sync_clear_input)
{
	if (m_input_buffer == param)
	{
		// deassert interrupt when no more lines are active
		m_input_buffer = 0;
		m_int_cb(0);
	}
	else
		m_input_buffer &= ~param;
}


//-------------------------------------------------
//  sync_input - helper to synchronize input lines
//-------------------------------------------------

void rst_buffer_device::sync_input(bool state, u8 mask)
{
	if (state)
		machine().scheduler().synchronize(timer_expired_delegate(FUNC(rst_pos_buffer_device::sync_set_input), this), mask);
	else
		machine().scheduler().synchronize(timer_expired_delegate(FUNC(rst_pos_buffer_device::sync_clear_input), this), mask);
}


//-------------------------------------------------
//  inta_cb - provide vector when interrupt is
//  acknowledged by CPU
//-------------------------------------------------

IRQ_CALLBACK_MEMBER(rst_buffer_device::inta_cb)
{
	return get_vector();
}
