// license:BSD-3-Clause
// copyright-holders:Juergen Buchmueller
/***************************************************************************
z80daisy.c
Z80/180 daisy chaining support functions.
***************************************************************************/
#include "emu.h"
#include "z80daisy.h"
#define VERBOSE 0
//**************************************************************************
// DEVICE Z80 DAISY INTERFACE
//**************************************************************************
//-------------------------------------------------
// device_z80daisy_interface - constructor
//-------------------------------------------------
device_z80daisy_interface::device_z80daisy_interface(const machine_config &mconfig, device_t &device)
: device_interface(device, "z80daisy"),
m_daisy_next(nullptr),
m_last_opcode(0)
{
}
//-------------------------------------------------
// ~device_z80daisy_interface - destructor
//-------------------------------------------------
device_z80daisy_interface::~device_z80daisy_interface()
{
}
//-------------------------------------------------
// interface_post_start - work to be done after
// actually starting a device
//-------------------------------------------------
void device_z80daisy_interface::interface_post_start()
{
device().save_item(NAME(m_last_opcode));
}
//-------------------------------------------------
// interface_post_reset - work to be done after a
// device is reset
//-------------------------------------------------
void device_z80daisy_interface::interface_post_reset()
{
m_last_opcode = 0;
}
//-------------------------------------------------
// z80daisy_decode - handle state machine that
// decodes the RETI instruction from M1 fetches
//-------------------------------------------------
void device_z80daisy_interface::z80daisy_decode(uint8_t opcode)
{
switch (m_last_opcode)
{
case 0xed:
// ED 4D = RETI
if (opcode == 0x4d)
z80daisy_irq_reti();
m_last_opcode = 0;
break;
case 0xcb:
case 0xdd:
case 0xfd:
// CB xx, DD xx, FD xx are just two-byte opcodes
m_last_opcode = 0;
break;
default:
// TODO: ED affects IEO
m_last_opcode = opcode;
break;
}
}
//**************************************************************************
// Z80 DAISY CHAIN
//**************************************************************************
//-------------------------------------------------
// z80_daisy_chain_interface - constructor
//-------------------------------------------------
z80_daisy_chain_interface::z80_daisy_chain_interface(const machine_config &mconfig, device_t &device)
: device_interface(device, "z80daisychain"),
m_daisy_config(nullptr),
m_chain(nullptr)
{
}
//-------------------------------------------------
// z80_daisy_chain_interface - destructor
//-------------------------------------------------
z80_daisy_chain_interface::~z80_daisy_chain_interface()
{
}
//-------------------------------------------------
// interface_post_start - work to be done after
// actually starting a device
//-------------------------------------------------
void z80_daisy_chain_interface::interface_post_start()
{
if (m_daisy_config != nullptr)
daisy_init(m_daisy_config);
}
void z80_daisy_chain_interface::daisy_init(const z80_daisy_config *daisy)
{
assert(daisy != nullptr);
// create a linked list of devices
device_z80daisy_interface **tailptr = &m_chain;
for ( ; daisy->devname != nullptr; daisy++)
{
// find the device
device_t *target = device().subdevice(daisy->devname);
if (target == nullptr)
{
target = device().siblingdevice(daisy->devname);
if (target == nullptr)
fatalerror("Unable to locate device '%s'\n", daisy->devname);
}
// make sure it has an interface
device_z80daisy_interface *intf;
if (!target->interface(intf))
fatalerror("Device '%s' does not implement the z80daisy interface!\n", daisy->devname);
// splice it out of the list if it was previously added
device_z80daisy_interface **oldtailptr = tailptr;
while (*oldtailptr != nullptr)
{
if (*oldtailptr == intf)
*oldtailptr = (*oldtailptr)->m_daisy_next;
else
oldtailptr = &(*oldtailptr)->m_daisy_next;
}
// add the interface to the list
intf->m_daisy_next = *tailptr;
*tailptr = intf;
tailptr = &(*tailptr)->m_daisy_next;
}
osd_printf_verbose("Daisy chain = %s\n", daisy_show_chain());
}
//-------------------------------------------------
// interface_post_reset - work to be done after a
// device is reset
//-------------------------------------------------
void z80_daisy_chain_interface::interface_post_reset()
{
// loop over all chained devices and call their reset function
for (device_z80daisy_interface *intf = m_chain; intf != nullptr; intf = intf->m_daisy_next)
intf->device().reset();
}
//-------------------------------------------------
// update_irq_state - update the IRQ state and
// return assert/clear based on the state
//-------------------------------------------------
int z80_daisy_chain_interface::daisy_update_irq_state()
{
// loop over all devices; dev[0] is highest priority
for (device_z80daisy_interface *intf = m_chain; intf != nullptr; intf = intf->m_daisy_next)
{
// if this device is asserting the INT line, that's the one we want
int state = intf->z80daisy_irq_state();
if (state & Z80_DAISY_INT)
return ASSERT_LINE;
// if this device is asserting the IEO line, it blocks everyone else
if (state & Z80_DAISY_IEO)
return CLEAR_LINE;
}
return CLEAR_LINE;
}
//-------------------------------------------------
// daisy_get_irq_device - return the device
// in the chain that requested the interrupt
//-------------------------------------------------
device_z80daisy_interface *z80_daisy_chain_interface::daisy_get_irq_device()
{
// loop over all devices; dev[0] is the highest priority
for (device_z80daisy_interface *intf = m_chain; intf != nullptr; intf = intf->m_daisy_next)
{
// if this device is asserting the INT line, that's the one we want
int state = intf->z80daisy_irq_state();
if (state & Z80_DAISY_INT)
return intf;
}
if (VERBOSE && daisy_chain_present())
device().logerror("Interrupt from outside Z80 daisy chain\n");
return nullptr;
}
//-------------------------------------------------
// call_reti_device - signal a RETI operator to
// the chain
//-------------------------------------------------
void z80_daisy_chain_interface::daisy_call_reti_device()
{
// loop over all devices; dev[0] is the highest priority
for (device_z80daisy_interface *intf = m_chain; intf != nullptr; intf = intf->m_daisy_next)
{
// if this device is asserting the IEO line, that's the one we want
int state = intf->z80daisy_irq_state();
if (state & Z80_DAISY_IEO)
{
intf->z80daisy_decode(0xed);
intf->z80daisy_decode(0x4d);
return;
}
}
}
//-------------------------------------------------
// daisy_show_chain - list devices in the chain
// in string format (for debugging purposes)
//-------------------------------------------------
std::string z80_daisy_chain_interface::daisy_show_chain() const
{
std::ostringstream result;
// loop over all devices
for (device_z80daisy_interface *intf = m_chain; intf != nullptr; intf = intf->m_daisy_next)
{
if (intf != m_chain)
result << " -> ";
result << intf->device().tag();
}
return result.str();
}