summaryrefslogtreecommitdiffstats
path: root/src/emu
diff options
context:
space:
mode:
authorGravatarGravatar Vas Crabb <vas@vastheman.com>2018-07-07 02:40:29 +1000
committerGravatarGravatar Vas Crabb <vas@vastheman.com>2018-07-07 02:40:29 +1000
commitc3fb11c2c98a5c28ece6a27093a0f9def350ac64 (patch)
treec68b38f05ed1d32358add721fda7f45e8803479f /src/emu
parent5d9e33b786d7ef452317439359f3cbd8cc920513 (diff)
devcb3
There are multiple issues with the current device callbacks: * They always dispatch through a pointer-to-member * Chained callbacks are a linked list so the branch unit can't predict the early * There's a runtime decision made on the left/right shift direction * There are runtime NULL checks on various objects * Binding a lambda isn't practical * Arbitrary transformations are not supported * When chaining callbacks it isn't clear what the MCFG_DEVCB_ modifiers apply to * It isn't possible to just append to a callback in derived configuration * The macros need a magic, hidden local called devcb * Moving code that uses the magic locals around is error-prone * Writing the MCFG_ macros to make a device usable is a pain * You can't discover applicable MCFG_ macros with intellisense * Macros are not scoped * Using an inappropriate macro isn't detected at compile time * Lots of other things This changeset overcomes the biggest obstacle to remving MCFG_ macros altogether. Essentially, to allow a devcb to be configured, call .bind() and expose the result (a bind target for the callback). Bind target methods starting with "set" repace the current callbacks; methods starting with "append" append to them. You can't reconfigure a callback after resolving it. There's no need to use a macro matching the handler signatures - use FUNC for everything. Current device is implied if no tag/finder is supplied (no need for explicit this). Lambdas are supported, and the memory space and offset are optional. These kinds of things work: * .read_cb().set([this] () { return something; }); * .read_cb().set([this] (offs_t offset) { return ~offset; }); * .write_cb().set([this] (offs_t offset, u8 data) { m_array[offset] = data; }); * .write_cb().set([this] (int state) { some_var = state; }); Arbitrary transforms are allowed, and they can modify offset/mask for example: * .read_cb().set(FUNC(my_state::handler)).transform([] (u8 data) { return bitswap<4>(data, 1, 3, 0, 2); }); * .read_cb().set(m_dev, FUNC(some_device::member)).transform([] (offs_t &offset, u8 data) { offset ^= 3; return data; }); It's possible to stack arbitrary transforms, at the cost of compile time (the whole transform stack gets inlined at compile time). Shifts count as an arbitrary transform, but mask/exor does not. Order of mask/shift/exor now matters. Modifications are applied in the specified order. These are NOT EQUIVALENT: * .read_cb().set(FUNC(my_state::handler)).mask(0x06).lshift(2); * .read_cb().set(FUNC(my_state::handler)).lshift(2).mask(0x06); The bit helper no longer reverses its behaviour for read callbacks, and I/O ports are no longer aware of the field mask. Binding a read callback to no-op is not supported - specify a constant. The GND and VCC aliases have been removed intentionally - they're TTL-centric, and were already being abused. Other quirks have been preserved, including write logger only logging when the data is non-zero (quite unhelpful in many of the cases where it's used). Legacy syntax is still supported for simple cases, but will be phased out. New devices should not have MCFG_ macros. I don't think I've missed any fundamental issues, but if I've broken something, let me know.
Diffstat (limited to 'src/emu')
-rw-r--r--src/emu/devcb.cpp1031
-rw-r--r--src/emu/devcb.h2980
-rw-r--r--src/emu/device.cpp11
-rw-r--r--src/emu/device.h9
-rw-r--r--src/emu/emu.h1
-rw-r--r--src/emu/emufwd.h6
-rw-r--r--src/emu/mconfig.h2
-rw-r--r--src/emu/output.h5
-rw-r--r--src/emu/screen.h8
-rw-r--r--src/emu/validity.cpp6
10 files changed, 2703 insertions, 1356 deletions
diff --git a/src/emu/devcb.cpp b/src/emu/devcb.cpp
index d22a9667e81..75e43ad606e 100644
--- a/src/emu/devcb.cpp
+++ b/src/emu/devcb.cpp
@@ -1,875 +1,228 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Vas Crabb
/***************************************************************************
- devcb.c
+ devcb.cpp
Device callback interface helpers.
***************************************************************************/
#include "emu.h"
-
-
-
-//**************************************************************************
-// DEVCB BASE CLASS
-//**************************************************************************
-
-//-------------------------------------------------
-// devcb_base - constructor
-//-------------------------------------------------
-
-devcb_base::devcb_base(device_t &device, u64 defmask)
- : m_owner(device)
- , m_base(device)
- , m_type(CALLBACK_NONE)
- , m_target_tag(nullptr)
- , m_target_int(0)
- , m_space_tag(nullptr)
- , m_space_num(0)
- , m_space(nullptr)
- , m_rshift(0)
- , m_mask(defmask)
- , m_defmask(defmask)
- , m_xor(0)
-{
- m_target.ptr = nullptr;
+#include "devcb.h"
+
+template class devcb_read<u8>;
+template class devcb_read<u16>;
+template class devcb_read<u32>;
+template class devcb_read<u64>;
+template class devcb_read<int, 1U>;
+
+template class devcb_read8::delegate_builder<read8_delegate>;
+template class devcb_read8::delegate_builder<read16_delegate>;
+template class devcb_read8::delegate_builder<read32_delegate>;
+template class devcb_read8::delegate_builder<read64_delegate>;
+template class devcb_read8::delegate_builder<read_line_delegate>;
+
+template class devcb_read16::delegate_builder<read8_delegate>;
+template class devcb_read16::delegate_builder<read16_delegate>;
+template class devcb_read16::delegate_builder<read32_delegate>;
+template class devcb_read16::delegate_builder<read64_delegate>;
+template class devcb_read16::delegate_builder<read_line_delegate>;
+
+template class devcb_read32::delegate_builder<read8_delegate>;
+template class devcb_read32::delegate_builder<read16_delegate>;
+template class devcb_read32::delegate_builder<read32_delegate>;
+template class devcb_read32::delegate_builder<read64_delegate>;
+template class devcb_read32::delegate_builder<read_line_delegate>;
+
+template class devcb_read64::delegate_builder<read8_delegate>;
+template class devcb_read64::delegate_builder<read16_delegate>;
+template class devcb_read64::delegate_builder<read32_delegate>;
+template class devcb_read64::delegate_builder<read64_delegate>;
+template class devcb_read64::delegate_builder<read_line_delegate>;
+
+template class devcb_read_line::delegate_builder<read8_delegate>;
+template class devcb_read_line::delegate_builder<read16_delegate>;
+template class devcb_read_line::delegate_builder<read32_delegate>;
+template class devcb_read_line::delegate_builder<read64_delegate>;
+template class devcb_read_line::delegate_builder<read_line_delegate>;
+
+template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read8_delegate> >;
+template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read16_delegate> >;
+template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read32_delegate> >;
+template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read64_delegate> >;
+template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read_line_delegate> >;
+template class devcb_read8::creator_impl<devcb_read8::ioport_builder>;
+
+template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read8_delegate> >;
+template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read16_delegate> >;
+template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read32_delegate> >;
+template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read64_delegate> >;
+template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read_line_delegate> >;
+template class devcb_read16::creator_impl<devcb_read16::ioport_builder>;
+
+template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read8_delegate> >;
+template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read16_delegate> >;
+template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read32_delegate> >;
+template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read64_delegate> >;
+template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read_line_delegate> >;
+template class devcb_read32::creator_impl<devcb_read32::ioport_builder>;
+
+template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read8_delegate> >;
+template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read16_delegate> >;
+template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read32_delegate> >;
+template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read64_delegate> >;
+template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read_line_delegate> >;
+template class devcb_read64::creator_impl<devcb_read64::ioport_builder>;
+
+template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read8_delegate> >;
+template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read16_delegate> >;
+template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read32_delegate> >;
+template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read64_delegate> >;
+template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read_line_delegate> >;
+template class devcb_read_line::creator_impl<devcb_read_line::ioport_builder>;
+
+template class devcb_write<u8>;
+template class devcb_write<u16>;
+template class devcb_write<u32>;
+template class devcb_write<u64>;
+template class devcb_write<int, 1U>;
+
+template class devcb_write8::delegate_builder<write8_delegate>;
+template class devcb_write8::delegate_builder<write16_delegate>;
+template class devcb_write8::delegate_builder<write32_delegate>;
+template class devcb_write8::delegate_builder<write64_delegate>;
+template class devcb_write8::delegate_builder<write_line_delegate>;
+
+template class devcb_write16::delegate_builder<write8_delegate>;
+template class devcb_write16::delegate_builder<write16_delegate>;
+template class devcb_write16::delegate_builder<write32_delegate>;
+template class devcb_write16::delegate_builder<write64_delegate>;
+template class devcb_write16::delegate_builder<write_line_delegate>;
+
+template class devcb_write32::delegate_builder<write8_delegate>;
+template class devcb_write32::delegate_builder<write16_delegate>;
+template class devcb_write32::delegate_builder<write32_delegate>;
+template class devcb_write32::delegate_builder<write64_delegate>;
+template class devcb_write32::delegate_builder<write_line_delegate>;
+
+template class devcb_write64::delegate_builder<write8_delegate>;
+template class devcb_write64::delegate_builder<write16_delegate>;
+template class devcb_write64::delegate_builder<write32_delegate>;
+template class devcb_write64::delegate_builder<write64_delegate>;
+template class devcb_write64::delegate_builder<write_line_delegate>;
+
+template class devcb_write_line::delegate_builder<write8_delegate>;
+template class devcb_write_line::delegate_builder<write16_delegate>;
+template class devcb_write_line::delegate_builder<write32_delegate>;
+template class devcb_write_line::delegate_builder<write64_delegate>;
+template class devcb_write_line::delegate_builder<write_line_delegate>;
+
+template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write8_delegate> >;
+template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write16_delegate> >;
+template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write32_delegate> >;
+template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write64_delegate> >;
+template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write_line_delegate> >;
+template class devcb_write8::creator_impl<devcb_write8::inputline_builder>;
+template class devcb_write8::creator_impl<devcb_write8::latched_inputline_builder>;
+template class devcb_write8::creator_impl<devcb_write8::ioport_builder>;
+template class devcb_write8::creator_impl<devcb_write8::membank_builder>;
+template class devcb_write8::creator_impl<devcb_write8::output_builder>;
+template class devcb_write8::creator_impl<devcb_write8::log_builder>;
+
+template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write8_delegate> >;
+template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write16_delegate> >;
+template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write32_delegate> >;
+template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write64_delegate> >;
+template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write_line_delegate> >;
+template class devcb_write16::creator_impl<devcb_write16::inputline_builder>;
+template class devcb_write16::creator_impl<devcb_write16::latched_inputline_builder>;
+template class devcb_write16::creator_impl<devcb_write16::ioport_builder>;
+template class devcb_write16::creator_impl<devcb_write16::membank_builder>;
+template class devcb_write16::creator_impl<devcb_write16::output_builder>;
+template class devcb_write16::creator_impl<devcb_write16::log_builder>;
+
+template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write8_delegate> >;
+template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write16_delegate> >;
+template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write32_delegate> >;
+template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write64_delegate> >;
+template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write_line_delegate> >;
+template class devcb_write32::creator_impl<devcb_write32::inputline_builder>;
+template class devcb_write32::creator_impl<devcb_write32::latched_inputline_builder>;
+template class devcb_write32::creator_impl<devcb_write32::ioport_builder>;
+template class devcb_write32::creator_impl<devcb_write32::membank_builder>;
+template class devcb_write32::creator_impl<devcb_write32::output_builder>;
+template class devcb_write32::creator_impl<devcb_write32::log_builder>;
+
+template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write8_delegate> >;
+template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write16_delegate> >;
+template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write32_delegate> >;
+template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write64_delegate> >;
+template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write_line_delegate> >;
+template class devcb_write64::creator_impl<devcb_write64::inputline_builder>;
+template class devcb_write64::creator_impl<devcb_write64::latched_inputline_builder>;
+template class devcb_write64::creator_impl<devcb_write64::ioport_builder>;
+template class devcb_write64::creator_impl<devcb_write64::membank_builder>;
+template class devcb_write64::creator_impl<devcb_write64::output_builder>;
+template class devcb_write64::creator_impl<devcb_write64::log_builder>;
+
+template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write8_delegate> >;
+template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write16_delegate> >;
+template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write32_delegate> >;
+template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write64_delegate> >;
+template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write_line_delegate> >;
+template class devcb_write_line::creator_impl<devcb_write_line::inputline_builder>;
+template class devcb_write_line::creator_impl<devcb_write_line::latched_inputline_builder>;
+template class devcb_write_line::creator_impl<devcb_write_line::ioport_builder>;
+template class devcb_write_line::creator_impl<devcb_write_line::membank_builder>;
+template class devcb_write_line::creator_impl<devcb_write_line::output_builder>;
+template class devcb_write_line::creator_impl<devcb_write_line::log_builder>;
+
+
+devcb_base::devcb_base(device_t &owner)
+ : m_owner(owner)
+ , m_dflt_space_finder(owner, finder_base::DUMMY_TAG)
+ , m_dflt_space_index(-1)
+ , m_dflt_space(nullptr)
+{
+ owner.register_callback(*this);
}
-//-------------------------------------------------
-// devcb_base - destructor
-//-------------------------------------------------
-
devcb_base::~devcb_base()
{
}
-//-------------------------------------------------
-// reset - reset/initialize state
-//-------------------------------------------------
-
-void devcb_base::reset(callback_type type)
-{
- reset(m_owner.mconfig().current_device(), type);
-}
-
-void devcb_base::reset(device_t &base, callback_type type)
-{
- m_base = base;
- m_type = type;
- m_target_tag = nullptr;
- m_target_int = 0;
- m_space_tag = nullptr;
- m_space_num = 0;
- m_space = nullptr;
- m_target.ptr = nullptr;
- m_rshift = 0;
- m_mask = m_defmask;
- m_xor = 0;
- devcb_reset();
-}
-
-
-//-------------------------------------------------
-// resolve_ioport - resolve an I/O port or fatal
-// error if we can't find it
-//-------------------------------------------------
-
-void devcb_base::resolve_ioport()
-{
- // attempt to resolve, fatal error if fail
- m_target.ioport = m_target_tag ? m_base.get().ioport(m_target_tag) : nullptr;
- if (!m_target.ioport)
- throw emu_fatalerror("Unable to resolve I/O port callback reference to '%s' in device '%s'\n", m_target_tag, m_base.get().tag());
-
- // adjust the mask to match the port bits
- u64 port_mask = 0;
- for (const ioport_field &field : m_target.ioport->fields())
- port_mask |= field.mask();
- m_mask = shift_mask(port_mask);
-}
-
-
-//-------------------------------------------------
-// resolve_membank - resolve a memory bank or
-// fatal error if we can't find it
-//-------------------------------------------------
-
-void devcb_base::resolve_membank()
-{
- // attempt to resolve, fatal error if fail
- m_target.membank = m_target_tag ? m_base.get().membank(m_target_tag) : nullptr;
- if (!m_target.membank)
- throw emu_fatalerror("Unable to resolve memory bank callback reference to '%s' in device '%s'\n", m_target_tag, m_base.get().tag());
-}
-
-
-//-------------------------------------------------
-// resolve_output - resolve an output item
-//-------------------------------------------------
-
-void devcb_base::resolve_output()
-{
- assert(m_target_tag);
- m_target.item = &m_base.get().machine().output().find_or_create_item(m_target_tag, 0);
-}
-
-
-//-------------------------------------------------
-// resolve_inputline - resolve a device and input
-// number or fatal error if we can't find it
-//-------------------------------------------------
-
-void devcb_base::resolve_inputline()
-{
- // attempt to resolve, fatal error if fail
- m_target.device = m_target_tag ? m_base.get().subdevice(m_target_tag) : nullptr;
- if (!m_target.device)
- throw emu_fatalerror("Unable to resolve device reference to '%s' in device '%s'\n", m_target_tag, m_base.get().tag());
-
- // make sure we have an execute interface
- device_execute_interface *exec;
- if (!m_target.device->interface(exec))
- throw emu_fatalerror("No execute interface found for device reference to '%s' in device '%s'\n", m_target_tag, m_base.get().tag());
-}
-
-
-//-------------------------------------------------
-// resolve_space - resolve an address space or
-// fatal error if we can't find it
-//-------------------------------------------------
-
-void devcb_base::resolve_space()
-{
- // attempt to resolve, fatal error if fail
- device_t *const spacedev(m_space_tag ? m_base.get().subdevice(m_space_tag) : nullptr);
- if (!spacedev)
- throw emu_fatalerror("Unable to resolve device reference to '%s' in device '%s'\n", m_space_tag, m_base.get().tag());
- if (!spacedev->memory().has_space(m_space_num))
- throw emu_fatalerror("Unable to resolve device address space %d on '%s' in device '%s'\n", m_space_num, m_space_tag, m_base.get().tag());
- m_space = &spacedev->memory().space(m_space_num);
-}
-
-
-
-//**************************************************************************
-// DEVCB READ CLASS
-//**************************************************************************
-
-//-------------------------------------------------
-// devcb_read_base - constructor
-//-------------------------------------------------
-
-devcb_read_base::devcb_read_base(device_t &device, u64 defmask, bool chained)
- : devcb_base(device, defmask),
- m_adapter(&devcb_read_base::read_unresolved_adapter)
-{
- if (!chained)
- device.m_input_callbacks.push_back(this);
-}
-
-
-//-------------------------------------------------
-// devcb_reset - reset/initialize local state
-//-------------------------------------------------
-
-void devcb_read_base::devcb_reset()
-{
- m_readline = read_line_delegate();
- m_read8 = read8_delegate();
- m_read16 = read16_delegate();
- m_read32 = read32_delegate();
- m_read64 = read64_delegate();
- m_adapter = &devcb_read_base::read_unresolved_adapter;
- m_chain = nullptr;
-}
-
-
-//-------------------------------------------------
-// chain_alloc - add another callback to the
-// input chain
-//-------------------------------------------------
-
-devcb_read_base &devcb_read_base::chain_alloc()
-{
- // set up the chained callback pointer
- m_chain.reset(new devcb_read_base(m_owner, m_defmask, true));
- return *m_chain;
-}
-
-
-//-------------------------------------------------
-// resolve - resolve the specified callback to
-// its final form
-//-------------------------------------------------
-
-void devcb_read_base::resolve()
+void devcb_base::validity_check(validity_checker &valid) const
{
- // first resolve any address spaces
- if (m_space_tag)
- resolve_space();
- else
- m_space = &m_owner.machine().dummy_space();
-
- // then handle the various types
- const char *name = "unknown";
- try
- {
- switch (m_type)
- {
- case CALLBACK_NONE:
- break;
-
- case CALLBACK_LINE:
- name = m_readline.name();
- m_readline.bind_relative_to(m_base);
- m_target_int = 0;
- m_adapter = m_readline.isnull() ? &devcb_read_base::read_constant_adapter : &devcb_read_base::read_line_adapter;
- m_mask = shift_mask(1);
- break;
-
- case CALLBACK_8:
- name = m_read8.name();
- m_read8.bind_relative_to(m_base);
- m_target_int = 0;
- m_adapter = m_read8.isnull() ? &devcb_read_base::read_constant_adapter : &devcb_read_base::read8_adapter;
- m_mask = shift_mask(0xff);
- break;
-
- case CALLBACK_16:
- name = m_read16.name();
- m_read16.bind_relative_to(m_base);
- m_target_int = 0;
- m_adapter = m_read16.isnull() ? &devcb_read_base::read_constant_adapter : &devcb_read_base::read16_adapter;
- m_mask = shift_mask(0xffff);
- break;
-
- case CALLBACK_32:
- name = m_read32.name();
- m_read32.bind_relative_to(m_base);
- m_target_int = 0;
- m_adapter = m_read32.isnull() ? &devcb_read_base::read_constant_adapter : &devcb_read_base::read32_adapter;
- m_mask = shift_mask(0xffffffff);
- break;
-
- case CALLBACK_64:
- name = m_read64.name();
- m_read64.bind_relative_to(m_base);
- m_target_int = 0;
- m_adapter = m_read64.isnull() ? &devcb_read_base::read_constant_adapter : &devcb_read_base::read64_adapter;
- break;
-
- case CALLBACK_IOPORT:
- name = m_target_tag;
- resolve_ioport();
- m_target_int = 0;
- m_adapter = &devcb_read_base::read_ioport_adapter;
- break;
-
- case CALLBACK_MEMBANK:
- throw emu_fatalerror("Device read callbacks can't be connected to bank switches\n");
-
- case CALLBACK_OUTPUT:
- throw emu_fatalerror("Device read callbacks can't be connected to output items\n");
-
- case CALLBACK_LOG:
- m_adapter = &devcb_read_base::read_logged_adapter;
- m_mask = 0;
- break;
-
- case CALLBACK_CONSTANT:
- if (m_xor != 0)
- throw emu_fatalerror("devcb_read: Attempt to invert constant value (%lX ^ %lX)\n", (unsigned long)shift_mask(m_target_int), (unsigned long)m_xor);
- m_adapter = &devcb_read_base::read_constant_adapter;
- m_mask = shift_mask(m_target_int);
- break;
-
- case CALLBACK_INPUTLINE:
- case CALLBACK_ASSERTLINE:
- case CALLBACK_CLEARLINE:
- case CALLBACK_HOLDLINE:
- throw emu_fatalerror("Device read callbacks can't be connected to input lines\n");
- }
- }
- catch (binding_type_exception &binderr)
+ // if a default address space is configured, ensure it exists
+ if (m_dflt_space_finder)
{
- throw emu_fatalerror("devcb_read: Error performing a late bind of type %s to %s (name=%s)\n", binderr.m_actual_type.name(), binderr.m_target_type.name(), name);
+ if (!m_dflt_space_finder->has_space(m_dflt_space_index))
+ osd_printf_error("Callback references non-existent memory space %d of device %s (%s)\n", m_dflt_space_index, m_dflt_space_finder->device().tag(), m_dflt_space_finder->device().name());
}
-
- // resolve callback chain recursively
- if (m_chain != nullptr)
- m_chain->resolve();
-
- // protect against bus contention (the masks must not overlap)
- for (const devcb_read_base *chained_cb = m_chain.get(); chained_cb != nullptr; chained_cb = chained_cb->m_chain.get())
- if ((m_mask & chained_cb->m_mask) != 0)
- throw emu_fatalerror("Device %s read callback (name=%s) overlaps with chained callback (%lX & %lX)", m_owner.tag(), name, (unsigned long)m_mask, (unsigned long)chained_cb->m_mask);
-}
-
-
-//-------------------------------------------------
-// resolve_safe - resolve the callback; if not
-// specified, resolve to a constant callback with
-// the given value
-//-------------------------------------------------
-
-void devcb_read_base::resolve_safe(u64 none_constant_value)
-{
- // convert to a constant if none specified
- if (m_type == CALLBACK_NONE)
+ else if (m_dflt_space_finder.finder_tag() != finder_base::DUMMY_TAG)
{
- m_target_int = none_constant_value;
- m_type = CALLBACK_CONSTANT;
+ osd_printf_error("Callback references non-existent memory space device tag %s\n", m_dflt_space_finder.finder_tag());
}
- resolve();
-}
-
-
-//-------------------------------------------------
-// read_unresolved_adapter - error-generating
-// unresolved adapter
-//-------------------------------------------------
-
-u64 devcb_read_base::read_unresolved_adapter(address_space &space, offs_t offset, u64 mask)
-{
- throw emu_fatalerror("Attempted to read through an unresolved devcb item");
-}
-
-
-//-------------------------------------------------
-// read_line_adapter - read from a line delegate
-//-------------------------------------------------
-
-u64 devcb_read_base::read_line_adapter(address_space &space, offs_t offset, u64 mask)
-{
- return shift_mask_xor(m_readline() & 1);
-}
-
-
-//-------------------------------------------------
-// read8_adapter - read from an 8-bit delegate
-//-------------------------------------------------
-
-u64 devcb_read_base::read8_adapter(address_space &space, offs_t offset, u64 mask)
-{
- return shift_mask_xor(m_read8(space, offset, unshift_mask(mask)));
-}
-
-
-//-------------------------------------------------
-// read16_adapter - read from a 16-bit delegate
-//-------------------------------------------------
-
-u64 devcb_read_base::read16_adapter(address_space &space, offs_t offset, u64 mask)
-{
- return shift_mask_xor(m_read16(space, offset, unshift_mask(mask)));
-}
-
-
-//-------------------------------------------------
-// read32_adapter - read from a 32-bit delegate
-//-------------------------------------------------
-
-u64 devcb_read_base::read32_adapter(address_space &space, offs_t offset, u64 mask)
-{
- return shift_mask_xor(m_read32(space, offset, unshift_mask(mask)));
-}
-
-
-//-------------------------------------------------
-// read64_adapter - read from a 64-bit delegate
-//-------------------------------------------------
-
-u64 devcb_read_base::read64_adapter(address_space &space, offs_t offset, u64 mask)
-{
- return shift_mask_xor(m_read64(space, offset, unshift_mask(mask)));
-}
-
-
-//-------------------------------------------------
-// read_ioport - read from an I/O port
-//-------------------------------------------------
-
-u64 devcb_read_base::read_ioport_adapter(address_space &space, offs_t offset, u64 mask)
-{
- return shift_mask_xor(m_target.ioport->read());
-}
-
-
-//-------------------------------------------------
-// read_logged_adapter - log a read and return
-// zero
-//-------------------------------------------------
-
-u64 devcb_read_base::read_logged_adapter(address_space &space, offs_t offset, u64 mask)
-{
- m_owner.logerror("%s: %s\n", m_base.get().machine().describe_context(), m_target_tag);
- return 0;
}
-//-------------------------------------------------
-// read_constant - read from a constant
-//-------------------------------------------------
-
-u64 devcb_read_base::read_constant_adapter(address_space &space, offs_t offset, u64 mask)
+void devcb_base::resolve()
{
- return shift_mask(m_target_int);
-}
-
-
-//-------------------------------------------------
-// validity_check - check the validity of the
-// callback object
-//-------------------------------------------------
-
-void devcb_read_base::validity_check(validity_checker &valid) const
-{
- switch (m_type)
- {
- case CALLBACK_NONE:
- case CALLBACK_CONSTANT:
- case CALLBACK_LOG:
- case CALLBACK_IOPORT:
- break;
-
- case CALLBACK_LINE:
- if (!m_base.get().subdevice(m_readline.device_name()))
- osd_printf_error("Device %s not found for READLINE callback (%s)\n", m_readline.device_name(), m_readline.name());
- break;
-
- case CALLBACK_8:
- if (!m_base.get().subdevice(m_read8.device_name()))
- osd_printf_error("Device %s not found for READ8 callback (%s)\n", m_read8.device_name(), m_read8.name());
- break;
-
- case CALLBACK_16:
- if (!m_base.get().subdevice(m_read16.device_name()))
- osd_printf_error("Device %s not found for READ16 callback (%s)\n", m_read16.device_name(), m_read16.name());
- break;
-
- case CALLBACK_32:
- if (!m_base.get().subdevice(m_read32.device_name()))
- osd_printf_error("Device %s not found for READ32 callback (%s)\n", m_read32.device_name(), m_read32.name());
- break;
-
- case CALLBACK_64:
- if (!m_base.get().subdevice(m_read64.device_name()))
- osd_printf_error("Device %s not found for READ64 callback (%s)\n", m_read64.device_name(), m_read64.name());
- break;
-
- case CALLBACK_MEMBANK:
- osd_printf_error("Device read callbacks can't be connected to bank switches\n");
- break;
-
- case CALLBACK_OUTPUT:
- osd_printf_error("Device read callbacks can't be connected to output items\n");
- break;
-
- case CALLBACK_INPUTLINE:
- case CALLBACK_ASSERTLINE:
- case CALLBACK_CLEARLINE:
- case CALLBACK_HOLDLINE:
- throw emu_fatalerror("Device read callbacks can't be connected to input lines\n");
- }
-
- if (m_chain)
- m_chain->validity_check(valid);
-}
-
-
-
-//**************************************************************************
-// DEVCB WRITE CLASS
-//**************************************************************************
-
-//-------------------------------------------------
-// devcb_write_base - constructor
-//-------------------------------------------------
-
-devcb_write_base::devcb_write_base(device_t &device, u64 defmask, bool chained)
- : devcb_base(device, defmask),
- m_adapter(&devcb_write_base::write_unresolved_adapter)
-{
- if (!chained)
- device.m_output_callbacks.push_back(this);
-}
-
-
-//-------------------------------------------------
-// devcb_reset - reset/initialize local state
-//-------------------------------------------------
-
-void devcb_write_base::devcb_reset()
-{
- m_writeline = write_line_delegate();
- m_write8 = write8_delegate();
- m_write16 = write16_delegate();
- m_write32 = write32_delegate();
- m_write64 = write64_delegate();
- m_adapter = &devcb_write_base::write_unresolved_adapter;
- m_chain = nullptr;
-}
-
-
-//-------------------------------------------------
-// chain_alloc - add another callback to the
-// output chain
-//-------------------------------------------------
-
-devcb_write_base &devcb_write_base::chain_alloc()
-{
- // set up the chained callback pointer
- m_chain.reset(new devcb_write_base(m_owner, m_defmask, true));
- return *m_chain;
-}
-
-
-//-------------------------------------------------
-// resolve - resolve the specified callback to
-// its final form
-//-------------------------------------------------
-
-void devcb_write_base::resolve()
-{
- // first resolve any address spaces
- if (m_space_tag)
- resolve_space();
+ if (m_dflt_space_finder && m_dflt_space_finder->has_space(m_dflt_space_index))
+ m_dflt_space = &m_dflt_space_finder->space(m_dflt_space_index);
else
- m_space = &m_owner.machine().dummy_space();
-
- // then handle the various types
- const char *name = "unknown";
- try
- {
- switch (m_type)
- {
- case CALLBACK_NONE:
- break;
-
- case CALLBACK_LINE:
- name = m_writeline.name();
- m_writeline.bind_relative_to(m_base);
- m_adapter = m_writeline.isnull() ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write_line_adapter;
- break;
-
- case CALLBACK_8:
- name = m_write8.name();
- m_write8.bind_relative_to(m_base);
- m_adapter = m_write8.isnull() ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write8_adapter;
- break;
-
- case CALLBACK_16:
- name = m_write16.name();
- m_write16.bind_relative_to(m_base);
- m_adapter = m_write16.isnull() ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write16_adapter;
- break;
-
- case CALLBACK_32:
- name = m_write32.name();
- m_write32.bind_relative_to(m_base);
- m_adapter = m_write32.isnull() ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write32_adapter;
- break;
-
- case CALLBACK_64:
- name = m_write64.name();
- m_write64.bind_relative_to(m_base);
- m_adapter = m_write64.isnull() ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write64_adapter;
- break;
-
- case CALLBACK_IOPORT:
- resolve_ioport();
- m_adapter = !m_target.ioport ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write_ioport_adapter;
- break;
-
- case CALLBACK_MEMBANK:
- resolve_membank();
- m_adapter = !m_target.membank ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write_membank_adapter;
- break;
-
- case CALLBACK_OUTPUT:
- resolve_output();
- m_adapter = !m_target.item ? &devcb_write_base::write_noop_adapter : &devcb_write_base::write_output_adapter;
- break;
-
- case CALLBACK_LOG:
- m_adapter = &devcb_write_base::write_logged_adapter;
- break;
-
- case CALLBACK_CONSTANT:
- m_adapter = &devcb_write_base::write_noop_adapter;
- break;
-
- case CALLBACK_INPUTLINE:
- resolve_inputline();
- m_adapter = &devcb_write_base::write_inputline_adapter;
- break;
-
- case CALLBACK_ASSERTLINE:
- resolve_inputline();
- m_adapter = &devcb_write_base::write_assertline_adapter;
- break;
-
- case CALLBACK_CLEARLINE:
- resolve_inputline();
- m_adapter = &devcb_write_base::write_clearline_adapter;
- break;
-
- case CALLBACK_HOLDLINE:
- resolve_inputline();
- m_adapter = &devcb_write_base::write_holdline_adapter;
- break;
- }
- }
- catch (binding_type_exception &binderr)
- {
- throw emu_fatalerror("devcb_write: Error performing a late bind of type %s to %s (name=%s)\n", binderr.m_actual_type.name(), binderr.m_target_type.name(), name);
- }
-
- // resolve callback chain recursively
- if (m_chain)
- m_chain->resolve();
-}
-
-
-//-------------------------------------------------
-// resolve_safe - resolve the callback; if not
-// specified, resolve to a no-op
-//-------------------------------------------------
-
-void devcb_write_base::resolve_safe()
-{
- // convert to a constant if none specified
- if (m_type == CALLBACK_NONE)
- m_type = CALLBACK_CONSTANT;
- resolve();
-}
-
-
-//-------------------------------------------------
-// validity_check - check the validity of the
-// callback object
-//-------------------------------------------------
-
-void devcb_write_base::validity_check(validity_checker &valid) const
-{
- switch (m_type)
- {
- case CALLBACK_NONE:
- break;
-
- case CALLBACK_CONSTANT:
- case CALLBACK_LOG:
- case CALLBACK_IOPORT:
- case CALLBACK_MEMBANK:
- case CALLBACK_OUTPUT:
- break;
-
- case CALLBACK_LINE:
- if (!m_base.get().subdevice(m_writeline.device_name()))
- osd_printf_error("Device %s not found for WRITELINE callback (%s)\n", m_writeline.device_name(), m_writeline.name());
- break;
-
- case CALLBACK_8:
- if (!m_base.get().subdevice(m_write8.device_name()))
- osd_printf_error("Device %s not found for WRITE8 callback (%s)\n", m_write8.device_name(), m_write8.name());
- break;
-
- case CALLBACK_16:
- if (!m_base.get().subdevice(m_write16.device_name()))
- osd_printf_error("Device %s not found for WRITE16 callback (%s)\n", m_write16.device_name(), m_write16.name());
- break;
-
- case CALLBACK_32:
- if (!m_base.get().subdevice(m_write32.device_name()))
- osd_printf_error("Device %s not found for WRITE32 callback (%s)\n", m_write32.device_name(), m_write32.name());
- break;
-
- case CALLBACK_64:
- if (!m_base.get().subdevice(m_write64.device_name()))
- osd_printf_error("Device %s not found for WRITE64 callback (%s)\n", m_write64.device_name(), m_write64.name());
- break;
-
- case CALLBACK_INPUTLINE:
- case CALLBACK_ASSERTLINE:
- case CALLBACK_CLEARLINE:
- case CALLBACK_HOLDLINE:
- {
- device_t *const device = m_base.get().subdevice(m_target_tag);
- device_execute_interface *execute;
- if (!device)
- osd_printf_error("Device %s not found for INPUTLINE callback (%d)\n", m_target_tag, int(m_target_int));
- else if (!device->interface(execute))
- osd_printf_error("Device %s has no interface for INPUTLINE callback (%d)\n", m_target_tag, int(m_target_int));
- }
- break;
- }
-
- if (m_chain)
- m_chain->validity_check(valid);
-}
-
-
-//-------------------------------------------------
-// write_unresolved_adapter - error-generating
-// unresolved adapter
-//-------------------------------------------------
-
-void devcb_write_base::write_unresolved_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- throw emu_fatalerror("Attempted to write through an unresolved devcb item");
+ m_dflt_space = &m_owner.machine().dummy_space();
}
-//-------------------------------------------------
-// write_line_adapter - write from a line delegate
-//-------------------------------------------------
-
-void devcb_write_base::write_line_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- m_writeline(unshift_mask_xor(data) & 1);
-}
-
-
-//-------------------------------------------------
-// write8_adapter - write from an 8-bit delegate
-//-------------------------------------------------
-
-void devcb_write_base::write8_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- m_write8(space, offset, unshift_mask_xor(data), unshift_mask(mask));
-}
-
-
-//-------------------------------------------------
-// write16_adapter - write from a 16-bit delegate
-//-------------------------------------------------
-
-void devcb_write_base::write16_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- m_write16(space, offset, unshift_mask_xor(data), unshift_mask(mask));
-}
-
-
-//-------------------------------------------------
-// write32_adapter - write from a 32-bit delegate
-//-------------------------------------------------
-
-void devcb_write_base::write32_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- m_write32(space, offset, unshift_mask_xor(data), unshift_mask(mask));
-}
-
-
-//-------------------------------------------------
-// write64_adapter - write from a 64-bit delegate
-//-------------------------------------------------
-
-void devcb_write_base::write64_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- m_write64(space, offset, unshift_mask_xor(data), unshift_mask(mask));
-}
-
-
-//-------------------------------------------------
-// write_ioport_adapter - write to an I/O port
-//-------------------------------------------------
-
-void devcb_write_base::write_ioport_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- if (m_target.ioport)
- m_target.ioport->write(unshift_mask_xor(data));
-}
-
-
-//-------------------------------------------------
-// write_membank_adapter - switch a memory bank
-//-------------------------------------------------
-
-void devcb_write_base::write_membank_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- if (m_target.membank)
- m_target.membank->set_entry(unshift_mask_xor(data));
-}
-
-
-//-------------------------------------------------
-// write_output_adapter - set an output item
-//-------------------------------------------------
-
-void devcb_write_base::write_output_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- if (m_target.item)
- m_target.item->set(unshift_mask_xor(data));
-}
-
-
-//-------------------------------------------------
-// write_logged_adapter - log write if masked
-// value is nonzero
-//-------------------------------------------------
-
-void devcb_write_base::write_logged_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- if (unshift_mask_xor(data) != 0)
- m_owner.logerror("%s: %s\n", m_owner.machine().describe_context(), m_target_tag);
-}
-
-
-//-------------------------------------------------
-// write_constant - write from a constant
-//-------------------------------------------------
-
-void devcb_write_base::write_noop_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- // constant for writes is a no-op
-}
-
-
-//-------------------------------------------------
-// write_inputline_adapter - write to a device's
-// input line
-//-------------------------------------------------
-
-void devcb_write_base::write_inputline_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- m_target.device->execute().set_input_line(m_target_int, unshift_mask_xor(data) & 1);
-}
-
-
-//-------------------------------------------------
-// write_assertline_adapter - write to a device's
-// input line
-//-------------------------------------------------
-
-void devcb_write_base::write_assertline_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- if (unshift_mask_xor(data) & 1)
- m_target.device->execute().set_input_line(m_target_int, ASSERT_LINE);
-}
-
-
-//-------------------------------------------------
-// write_clearline_adapter - write to a device's
-// input line
-//-------------------------------------------------
-
-void devcb_write_base::write_clearline_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
+devcb_read_base::~devcb_read_base()
{
- if (unshift_mask_xor(data) & 1)
- m_target.device->execute().set_input_line(m_target_int, CLEAR_LINE);
}
-//-------------------------------------------------
-// write_clearline_adapter - write to a device's
-// input line
-//-------------------------------------------------
-void devcb_write_base::write_holdline_adapter(address_space &space, offs_t offset, u64 data, u64 mask)
+devcb_write_base::~devcb_write_base()
{
- if (unshift_mask_xor(data) & 1)
- m_target.device->execute().set_input_line(m_target_int, HOLD_LINE);
}
diff --git a/src/emu/devcb.h b/src/emu/devcb.h
index 833d6938a1c..5f65235cb70 100644
--- a/src/emu/devcb.h
+++ b/src/emu/devcb.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Vas Crabb
/***************************************************************************
devcb.h
@@ -7,636 +7,2630 @@
Device callback interface helpers.
***************************************************************************/
-
-#pragma once
-
-#ifndef __EMU_H__
-#error Dont include this file directly; include emu.h instead.
-#endif
-
#ifndef MAME_EMU_DEVCB_H
#define MAME_EMU_DEVCB_H
+#pragma once
+
+#include <cassert>
#include <functional>
+#include <memory>
+#include <string>
+#include <typeinfo>
#include <type_traits>
#include <utility>
+#include <vector>
-namespace emu { namespace detail {
-template <typename Delegate> struct devcb_delegate_initialiser
+//**************************************************************************
+// LEGACY SYNAX SUPPORT - WILL BE REMOVED
+//**************************************************************************
+
+#include <tuple>
+
+#define DEVCB_READ8(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_READ16(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_READ32(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_READ64(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_READLINE(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+
+#define DEVCB_WRITE8(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_WRITE16(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_WRITE32(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_WRITE64(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+#define DEVCB_WRITELINE(tag, _class, _func) (std::forward_as_tuple((tag), &_class::_func, #_class "::" #_func))
+
+template <typename T> struct devcb_constant_t { T m_value; };
+template <typename T> auto DEVCB_CONSTANT(T &&value) { return devcb_constant_t<std::decay_t<T> >{ std::forward<T>(value) }; }
+
+struct DEVCB_IOPORT
{
- devcb_delegate_initialiser(char const *tag, Delegate &&delegate) : m_base(nullptr), m_delegate(std::move(delegate))
- {
- }
- template <typename T>
- devcb_delegate_initialiser(T &device, std::enable_if_t<is_device_implementation<T>::value, Delegate &&> delegate) : m_base(&device), m_delegate(std::move(delegate))
- {
- }
- template <typename T>
- devcb_delegate_initialiser(T &interface, std::enable_if_t<is_device_interface<T>::value, Delegate &&> delegate) : m_base(&interface.device()), m_delegate(std::move(delegate))
- {
- }
- template <typename DeviceClass, bool Required>
- devcb_delegate_initialiser(device_finder<DeviceClass, Required> const &finder, Delegate &&delegate) : m_base(&finder.finder_target().first), m_delegate(std::move(delegate))
- {
- }
+ constexpr DEVCB_IOPORT(char const *tag) : m_tag(tag) { }
+ char const *m_tag;
+};
- device_t *m_base;
- Delegate &&m_delegate;
+struct DEVCB_INPUTLINE
+{
+ constexpr DEVCB_INPUTLINE(char const *tag, int linenum) : m_tag(tag), m_linenum(linenum) { }
+ char const *m_tag;
+ int m_linenum;
};
-inline char const *devcb_delegate_get_tag(char const *tag) { return tag; }
-template <typename T>
-inline std::enable_if_t<is_device_implementation<T>::value, char const *> devcb_delegate_get_tag(T &device) { return DEVICE_SELF; }
-template <typename T>
-inline std::enable_if_t<is_device_interface<T>::value, char const *> devcb_delegate_get_tag(T &interface) { return DEVICE_SELF; }
-template <typename DeviceClass, bool Required>
-inline char const *devcb_delegate_get_tag(device_finder<DeviceClass, Required> const &finder) { return finder.finder_tag(); }
+struct DEVCB_ASSERTLINE
+{
+ constexpr DEVCB_ASSERTLINE(char const *tag, int linenum) : m_tag(tag), m_linenum(linenum) { }
+ char const *m_tag;
+ int m_linenum;
+};
-template <typename DescType> struct devcb_tag_desc_creator
+struct DEVCB_CLEARLINE
{
- static DescType create(char const *tag)
- {
- return DescType{ nullptr, tag };
- }
- static DescType create(device_t &device)
- {
- return DescType{ &device, DEVICE_SELF };
- }
- static DescType create(device_interface &interface)
- {
- return DescType{ &interface.device(), DEVICE_SELF };
- }
- template <typename DeviceClass, bool Required>
- static DescType create(device_finder<DeviceClass, Required> const &finder)
- {
- std::pair<device_t &, char const *> const target(finder.finder_target());
- return DescType{ &target.first, target.second };
- }
+ constexpr DEVCB_CLEARLINE(char const *tag, int linenum) : m_tag(tag), m_linenum(linenum) { }
+ char const *m_tag;
+ int m_linenum;
};
-template <typename DescType> struct devcb_line_desc_creator
+struct DEVCB_HOLDLINE
{
- static DescType create(char const *tag, int inputnum)
- {
- return DescType{ nullptr, tag, inputnum };
- }
- static DescType create(device_t &device, int inputnum)
- {
- return DescType{ &device, DEVICE_SELF, inputnum };
- }
- static DescType create(device_interface &interface, int inputnum)
- {
- return DescType{ &interface.device(), DEVICE_SELF, inputnum };
- }
- template <typename DeviceClass, bool Required>
- static DescType create(device_finder<DeviceClass, Required> const &finder, int inputnum)
- {
- std::pair<device_t &, char const *> const target(finder.finder_target());
- return DescType{ &target.first, target.second, inputnum };
- }
+ constexpr DEVCB_HOLDLINE(char const *tag, int linenum) : m_tag(tag), m_linenum(linenum) { }
+ char const *m_tag;
+ int m_linenum;
};
-} } // namespace emu::detail
+struct DEVCB_MEMBANK
+{
+ constexpr DEVCB_MEMBANK(char const *tag) : m_tag(tag) { }
+ char const *m_tag;
+};
+struct DEVCB_OUTPUT
+{
+ constexpr DEVCB_OUTPUT(char const *tag) : m_tag(tag) { }
+ char const *m_tag;
+};
-//**************************************************************************
-// MACROS
-//**************************************************************************
+enum devcb_noop_t { DEVCB_NOOP };
-// wrappers for read callbacks into any tagged device
-#define DEVCB_READLINE(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<read_line_delegate>((tag), read_line_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_READ8(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<read8_delegate>((tag), read8_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_READ16(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<read16_delegate>((tag), read16_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_READ32(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<read32_delegate>((tag), read32_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_READ64(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<read64_delegate>((tag), read64_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-
-// wrappers for write callbacks into any tagged device
-#define DEVCB_WRITELINE(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<write_line_delegate>((tag), write_line_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_WRITE8(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<write8_delegate>((tag), write8_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_WRITE16(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<write16_delegate>((tag), write16_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_WRITE32(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<write32_delegate>((tag), write32_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-#define DEVCB_WRITE64(tag, _class, _func) (emu::detail::devcb_delegate_initialiser<write64_delegate>((tag), write64_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr)))
-
-// machine config helpers to add shift, mask, or address space configuration
-#define MCFG_DEVCB_RSHIFT(_shift) devcb->set_rshift(_shift);
-#define MCFG_DEVCB_MASK(_mask) devcb->set_mask(_mask);
-#define MCFG_DEVCB_BIT(_bit) devcb->set_rshift(-(_bit)).set_mask(1ULL << (_bit));
-#define MCFG_DEVCB_XOR(_xor) devcb->set_xor(_xor);
-#define MCFG_DEVCB_INVERT devcb->set_xor(~u64(0));
-#define MCFG_DEVCB_ADDRESS_SPACE(_device, _spacenum) devcb->set_space(_device, _spacenum);
-
-// machine config helpers for chaining callbacks
-#define MCFG_DEVCB_CHAIN_INPUT(_desc) devcb = &downcast<devcb_read_base &>(*devcb).chain_alloc().set_callback(DEVCB_##_desc);
-#define MCFG_DEVCB_CHAIN_OUTPUT(_desc) devcb = &downcast<devcb_write_base &>(*devcb).chain_alloc().set_callback(DEVCB_##_desc);
//**************************************************************************
-// TYPE DEFINITIONS
+// DELEGATE TYPES
//**************************************************************************
-// base delegate type for a read_line/write_line
typedef device_delegate<int ()> read_line_delegate;
typedef device_delegate<void (int)> write_line_delegate;
+namespace emu { namespace detail {
-// ======================> devcb_base
-
-class devcb_base
-{
-protected:
- // enumerate the types of callbacks
- enum callback_type
- {
- CALLBACK_NONE,
- CALLBACK_LINE,
- CALLBACK_8,
- CALLBACK_16,
- CALLBACK_32,
- CALLBACK_64,
- CALLBACK_IOPORT,
- CALLBACK_MEMBANK,
- CALLBACK_OUTPUT,
- CALLBACK_LOG,
- CALLBACK_CONSTANT,
- CALLBACK_INPUTLINE,
- CALLBACK_ASSERTLINE,
- CALLBACK_CLEARLINE,
- CALLBACK_HOLDLINE
- };
+template <typename T, typename Enable = void> struct read_line_device_class { };
+template <typename T, typename Enable = void> struct write_line_device_class { };
+
+template <typename T, typename Ret, typename... Params>
+struct read_line_device_class<Ret (T::*)(Params...), std::enable_if_t<std::is_constructible<read_line_delegate, Ret (T::*)(Params...), char const *, char const *, T *>::value> > { using type = T; };
+template <typename T, typename Ret, typename... Params>
+struct read_line_device_class<Ret (T::*)(Params...) const, std::enable_if_t<std::is_constructible<read_line_delegate, Ret (T::*)(Params...) const, char const *, char const *, T *>::value> > { using type = T; };
+template <typename T, typename Ret, typename... Params>
+struct read_line_device_class<Ret (*)(T &, Params...), std::enable_if_t<std::is_constructible<read_line_delegate, Ret (*)(T &, Params...), char const *, char const *, T *>::value> > { using type = T; };
+template <typename T, typename Ret, typename... Params>
+struct read_line_device_class<Ret (*)(T *, Params...), std::enable_if_t<std::is_constructible<read_line_delegate, Ret (*)(T *, Params...), char const *, char const *, T *>::value> > { using type = T; };
+
+template <typename T, typename Ret, typename... Params>
+struct write_line_device_class<Ret (T::*)(Params...), std::enable_if_t<std::is_constructible<write_line_delegate, Ret (T::*)(Params...), char const *, char const *, T *>::value> > { using type = T; };
+template <typename T, typename Ret, typename... Params>
+struct write_line_device_class<Ret (T::*)(Params...) const, std::enable_if_t<std::is_constructible<write_line_delegate, Ret (T::*)(Params...) const, char const *, char const *, T *>::value> > { using type = T; };
+template <typename T, typename Ret, typename... Params>
+struct write_line_device_class<Ret (*)(T &, Params...), std::enable_if_t<std::is_constructible<write_line_delegate, Ret (*)(T &, Params...), char const *, char const *, T *>::value> > { using type = T; };
+template <typename T, typename Ret, typename... Params>
+struct write_line_device_class<Ret (*)(T *, Params...), std::enable_if_t<std::is_constructible<write_line_delegate, Ret (*)(T *, Params...), char const *, char const *, T *>::value> > { using type = T; };
+
+template <typename T> using read_line_device_class_t = typename read_line_device_class<T>::type;
+template <typename T> using write_line_device_class_t = typename write_line_device_class<T>::type;
- // construction/destruction
- devcb_base(device_t &device, u64 defmask);
- virtual ~devcb_base();
+template <typename T>
+inline read_line_delegate make_delegate(T &&func, char const *name, char const *tag, read_line_device_class_t<std::remove_reference_t<T> > *obj)
+{ return read_line_delegate(func, name, tag, obj); }
- template <callback_type Type> struct tag_desc
- {
- device_t *m_base;
- char const *m_tag;
- };
+template <typename T>
+inline write_line_delegate make_delegate(T &&func, char const *name, char const *tag, write_line_device_class_t<std::remove_reference_t<T> > *obj)
+{ return write_line_delegate(func, name, tag, obj); }
- template <callback_type Type> struct line_desc
- {
- device_t *m_base;
- char const *m_tag;
- int m_inputnum;
- };
+} } // namespace emu::detail
-public:
- // getters
- bool isnull() const { return (m_type == CALLBACK_NONE); }
- // additional configuration
- devcb_base &set_space(const char *device, int space = 0) { m_space_tag = device; m_space_num = space; return *this; }
- devcb_base &set_rshift(int rshift) { m_rshift = rshift; return *this; }
- devcb_base &set_mask(u64 mask) { m_mask = mask; return *this; }
- devcb_base &set_xor(u64 xorval) { m_xor = xorval; return *this; }
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
- // construction helper classes
- struct null_desc
+/// \brief Base callback helper
+///
+/// Provides utilities for supporting multiple read/write/transform
+/// signatures, and the base exclusive-or/mask transform methods.
+class devcb_base
+{
+public:
+ template <typename T> void set_space(T &&tag, int index)
{
- };
+ m_dflt_space_finder.set_tag(std::forward<T>(tag));
+ m_dflt_space_index = index;
+ }
- using ioport_desc = tag_desc<CALLBACK_IOPORT>;
- using membank_desc = tag_desc<CALLBACK_MEMBANK>;
- using output_desc = tag_desc<CALLBACK_OUTPUT>;
+ virtual void validity_check(validity_checker &valid) const;
- struct constant_desc
+protected:
+ // This is in C++17 but not C++14
+ template <typename... T> struct void_wrapper { using type = void; };
+ template <typename... T> using void_t = typename void_wrapper<T...>::type;
+
+ // Intermediate is larger of input and output, mask is forced to unsigned
+ template <typename T, typename U, typename Enable = void> struct intermediate;
+ template <typename T, typename U> struct intermediate<T, U, std::enable_if_t<sizeof(T) >= sizeof(U)> > { using type = T; };
+ template <typename T, typename U> struct intermediate<T, U, std::enable_if_t<sizeof(T) < sizeof(U)> > { using type = U; };
+ template <typename T, typename U> using intermediate_t = typename intermediate<T, U>::type;
+ template <typename T, typename U> using mask_t = std::make_unsigned_t<intermediate_t<T, U> >;
+
+ // Detecting candidates for transform functions
+ template <typename Input, typename Result, typename Func, typename Enable = void> struct is_transform_form1 { static constexpr bool value = false; };
+ template <typename Input, typename Result, typename Func, typename Enable = void> struct is_transform_form2 { static constexpr bool value = false; };
+ template <typename Input, typename Result, typename Func, typename Enable = void> struct is_transform_form3 { static constexpr bool value = false; };
+ template <typename Input, typename Result, typename Func, typename Enable = void> struct is_transform_form4 { static constexpr bool value = false; };
+ template <typename Input, typename Result, typename Func, typename Enable = void> struct is_transform_form5 { static constexpr bool value = false; };
+ template <typename Input, typename Result, typename Func, typename Enable = void> struct is_transform_form6 { static constexpr bool value = false; };
+ template <typename Input, typename Result, typename Func> struct is_transform_form1<Input, Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (address_space &, offs_t &, Input, std::make_unsigned_t<Input> &)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Input, typename Result, typename Func> struct is_transform_form2<Input, Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (address_space &, offs_t &, Input)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Input, typename Result, typename Func> struct is_transform_form3<Input, Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (offs_t &, Input, std::make_unsigned_t<Input> &)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Input, typename Result, typename Func> struct is_transform_form4<Input, Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (offs_t &, Input)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Input, typename Result, typename Func> struct is_transform_form5<Input, Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (address_space &, Input)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Input, typename Result, typename Func> struct is_transform_form6<Input, Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (Input)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Input, typename Result, typename Func> struct is_transform { static constexpr bool value = is_transform_form1<Input, Result, Func>::value || is_transform_form2<Input, Result, Func>::value || is_transform_form3<Input, Result, Func>::value || is_transform_form4<Input, Result, Func>::value || is_transform_form5<Input, Result, Func>::value || is_transform_form6<Input, Result, Func>::value; };
+
+ // Determining the result type of a transform function
+ template <typename Input, typename Result, typename Func, typename Enable = void> struct transform_result;
+ template <typename Input, typename Result, typename Func> struct transform_result<Input, Result, Func, std::enable_if_t<is_transform_form1<Input, Result, Func>::value> > { using type = std::result_of_t<Func (address_space &, offs_t &, Input, std::make_unsigned_t<Input> &)>; };
+ template <typename Input, typename Result, typename Func> struct transform_result<Input, Result, Func, std::enable_if_t<is_transform_form2<Input, Result, Func>::value> > { using type = std::result_of_t<Func (address_space &, offs_t &, Input)>; };
+ template <typename Input, typename Result, typename Func> struct transform_result<Input, Result, Func, std::enable_if_t<is_transform_form3<Input, Result, Func>::value> > { using type = std::result_of_t<Func (offs_t &, Input, std::make_unsigned_t<Input> &)>; };
+ template <typename Input, typename Result, typename Func> struct transform_result<Input, Result, Func, std::enable_if_t<is_transform_form4<Input, Result, Func>::value> > { using type = std::result_of_t<Func (offs_t &, Input)>; };
+ template <typename Input, typename Result, typename Func> struct transform_result<Input, Result, Func, std::enable_if_t<is_transform_form5<Input, Result, Func>::value> > { using type = std::result_of_t<Func (address_space &, Input)>; };
+ template <typename Input, typename Result, typename Func> struct transform_result<Input, Result, Func, std::enable_if_t<is_transform_form6<Input, Result, Func>::value> > { using type = std::result_of_t<Func (Input)>; };
+ template <typename Input, typename Result, typename Func> using transform_result_t = typename transform_result<Input, Result, Func>::type;
+
+ // Mapping method types to delegate types
+ template <typename T, typename Enable = void> struct delegate_type;
+ template <typename T> struct delegate_type<T, void_t<emu::detail::read8_device_class_t<std::remove_reference_t<T> > > > { using type = read8_delegate; using device_class = emu::detail::read8_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::read16_device_class_t<std::remove_reference_t<T> > > > { using type = read16_delegate; using device_class = emu::detail::read16_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::read32_device_class_t<std::remove_reference_t<T> > > > { using type = read32_delegate; using device_class = emu::detail::read32_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::read64_device_class_t<std::remove_reference_t<T> > > > { using type = read64_delegate; using device_class = emu::detail::read64_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::read_line_device_class_t<std::remove_reference_t<T> > > > { using type = read_line_delegate; using device_class = emu::detail::read_line_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::write8_device_class_t<std::remove_reference_t<T> > > > { using type = write8_delegate; using device_class = emu::detail::write8_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::write16_device_class_t<std::remove_reference_t<T> > > > { using type = write16_delegate; using device_class = emu::detail::write16_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::write32_device_class_t<std::remove_reference_t<T> > > > { using type = write32_delegate; using device_class = emu::detail::write32_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::write64_device_class_t<std::remove_reference_t<T> > > > { using type = write64_delegate; using device_class = emu::detail::write64_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> struct delegate_type<T, void_t<emu::detail::write_line_device_class_t<std::remove_reference_t<T> > > > { using type = write_line_delegate; using device_class = emu::detail::write_line_device_class_t<std::remove_reference_t<T> >; };
+ template <typename T> using delegate_type_t = typename delegate_type<T>::type;
+ template <typename T> using delegate_device_class_t = typename delegate_type<T>::device_class;
+
+ // Invoking transform callbacks
+ template <typename Input, typename Result, typename T> static std::enable_if_t<is_transform_form1<Input, Result, T>::value, mask_t<transform_result_t<Input, Result, T>, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> &mem_mask) { return std::make_unsigned_t<transform_result_t<Input, Result, T> >(cb(space, offset, data, mem_mask)); }
+ template <typename Input, typename Result, typename T> static std::enable_if_t<is_transform_form2<Input, Result, T>::value, mask_t<transform_result_t<Input, Result, T>, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> &mem_mask) { return std::make_unsigned_t<transform_result_t<Input, Result, T> >(cb(space, data, offset)); }
+ template <typename Input, typename Result, typename T> static std::enable_if_t<is_transform_form3<Input, Result, T>::value, mask_t<transform_result_t<Input, Result, T>, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> &mem_mask) { return std::make_unsigned_t<transform_result_t<Input, Result, T> >(cb(offset, data, mem_mask)); }
+ template <typename Input, typename Result, typename T> static std::enable_if_t<is_transform_form4<Input, Result, T>::value, mask_t<transform_result_t<Input, Result, T>, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> &mem_mask) { return std::make_unsigned_t<transform_result_t<Input, Result, T> >(cb(offset, data)); }
+ template <typename Input, typename Result, typename T> static std::enable_if_t<is_transform_form5<Input, Result, T>::value, mask_t<transform_result_t<Input, Result, T>, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> &mem_mask) { return std::make_unsigned_t<transform_result_t<Input, Result, T> >(cb(space, data)); }
+ template <typename Input, typename Result, typename T> static std::enable_if_t<is_transform_form6<Input, Result, T>::value, mask_t<transform_result_t<Input, Result, T>, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> &mem_mask) { return std::make_unsigned_t<transform_result_t<Input, Result, T> >(cb(data)); }
+
+ // Working with devices and interfaces
+ template <typename T> static std::enable_if_t<emu::detail::is_device_implementation<T>::value, const char *> get_tag(T &obj) { return obj.tag(); }
+ template <typename T> static std::enable_if_t<emu::detail::is_device_interface<T>::value, const char *> get_tag(T &obj) { return obj.device().tag(); }
+ template <typename T, typename U> static std::enable_if_t<std::is_convertible<std::add_pointer_t<U>, std::add_pointer_t<T> >::value, T &> cast_reference(U &obj) { return downcast<T &>(obj); }
+ template <typename T, typename U> static std::enable_if_t<!std::is_convertible<std::add_pointer_t<U>, std::add_pointer_t<T> >::value, T &> cast_reference(U &obj) { return dynamic_cast<T &>(obj); }
+
+ /// \brief Base transform helper
+ ///
+ /// Provides member functions for setting exclusive-or, mask and
+ /// shifts. Exclusive-or and mask values are stored; it's assumed
+ /// that the implementation supports lamba transforms to allow
+ /// shifts.
+ template <typename T, typename Impl>
+ class transform_base
{
- u64 m_value;
+ public:
+ Impl &exor(std::make_unsigned_t<T> val) { m_exor ^= val; return static_cast<Impl &>(*this); }
+ Impl &mask(std::make_unsigned_t<T> val) { m_mask = m_inherited_mask ? val : (m_mask & val); m_inherited_mask = false; return static_cast<Impl &>(*this); }
+ Impl &invert() { return exor(~std::make_unsigned_t<T>(0)); }
+
+ auto rshift(unsigned val)
+ {
+ auto trans(static_cast<Impl &>(*this).transform([val] (offs_t offset, T data, std::make_unsigned_t<T> &mem_mask) { mem_mask >>= val; return data >> val; }));
+ return std::move(trans.mask(m_mask >> val));
+ }
+ auto lshift(unsigned val)
+ {
+ auto trans(static_cast<Impl &>(*this).transform([val] (offs_t offset, T data, std::make_unsigned_t<T> &mem_mask) { mem_mask <<= val; return data << val; }));
+ return std::move(trans.mask(m_mask << val));
+ }
+ auto bit(unsigned val) { return std::move(rshift(val).mask(T(1U))); }
+
+ constexpr std::make_unsigned_t<T> exor() const { return m_exor & m_mask; }
+ constexpr std::make_unsigned_t<T> mask() const { return m_mask; }
+
+ protected:
+ constexpr bool need_exor() const { return std::make_unsigned_t<T>(0) != (m_exor & m_mask); }
+ constexpr bool need_mask() const { return std::make_unsigned_t<T>(~std::make_unsigned_t<T>(0)) != m_mask; }
+
+ constexpr bool inherited_mask() const { return m_inherited_mask; }
+
+ constexpr transform_base(std::make_unsigned_t<T> mask) : m_mask(mask) { }
+ constexpr transform_base(transform_base const &) = default;
+ transform_base(transform_base &&) = default;
+ transform_base &operator=(transform_base const &) = default;
+ transform_base &operator=(transform_base &&) = default;
+
+ private:
+ std::make_unsigned_t<T> m_exor = std::make_unsigned_t<T>(0);
+ std::make_unsigned_t<T> m_mask;
+ bool m_inherited_mask = true;
};
- struct logger_desc
- {
- const char *m_string;
- };
+ devcb_base(device_t &owner);
+ ~devcb_base();
- using inputline_desc = line_desc<CALLBACK_INPUTLINE>;
- using assertline_desc = line_desc<CALLBACK_ASSERTLINE>;
- using clearline_desc = line_desc<CALLBACK_CLEARLINE>;
- using holdline_desc = line_desc<CALLBACK_HOLDLINE>;
+ device_t &owner() const { return m_owner; }
- // shared callback setters
- devcb_base &set_callback(null_desc null) { reset(CALLBACK_NONE); return *this; }
- template <callback_type Type> devcb_base &set_callback(tag_desc<Type> desc)
- {
- if (desc.m_base) reset(*desc.m_base, Type);
- else reset(Type);
- m_target_tag = desc.m_tag;
- return *this;
- }
- devcb_base &set_callback(constant_desc constant) { reset(CALLBACK_CONSTANT); m_target_int = constant.m_value; return *this; }
- devcb_base &set_callback(logger_desc logger) { reset(CALLBACK_LOG); m_target_tag = logger.m_string; return *this; }
- void reset() { reset(m_owner, CALLBACK_NONE); }
+ void resolve();
-protected:
- // internal helpers
- inline u64 shift_mask(u64 value) const { return ((m_rshift < 0) ? (value << -m_rshift) : (value >> m_rshift)) & m_mask; }
- inline u64 shift_mask_xor(u64 value) const { return (((m_rshift < 0) ? (value << -m_rshift) : (value >> m_rshift)) ^ m_xor) & m_mask; }
- inline u64 unshift_mask(u64 value) const { return (m_rshift < 0) ? ((value & m_mask) >> -m_rshift) : ((value & m_mask) << m_rshift); }
- inline u64 unshift_mask_xor(u64 value) const { return (m_rshift < 0) ? (((value ^ m_xor) & m_mask) >> -m_rshift) : (((value ^ m_xor) & m_mask) << m_rshift); }
- void reset(callback_type type);
- void reset(device_t &base, callback_type type);
- virtual void devcb_reset() = 0;
- void resolve_ioport();
- void resolve_membank();
- void resolve_output();
- void resolve_inputline();
- void resolve_space();
-
- // the callback target is going to be one of these
- union callback_target
- {
- void * ptr;
- device_t * device;
- ioport_port * ioport;
- memory_bank * membank;
- output_manager::output_item *item;
- };
+ address_space &default_space() { assert(m_dflt_space); return *m_dflt_space; }
- // configuration
- device_t & m_owner; // reference to our owning device
- std::reference_wrapper<device_t> m_base; // device to resolve relative to
- callback_type m_type; // type of callback registered
- const char * m_target_tag; // tag of target object
- u64 m_target_int; // integer value of target object
- const char * m_space_tag; // tag of address space device
- int m_space_num; // address space number of space device
-
- // derived state
- address_space * m_space; // target address space
- callback_target m_target; // resolved pointer to target object
- int m_rshift; // right shift to apply to data read
- u64 m_mask; // mask to apply to data read
- const u64 m_defmask; // default mask
- u64 m_xor; // XOR to apply to data read
+private:
+ device_t &m_owner;
+ optional_device<device_memory_interface> m_dflt_space_finder;
+ int m_dflt_space_index;
+ address_space *m_dflt_space;
};
-// ======================> devcb_read_base
-
+/// \brief Read callback utilities
+///
+/// Helpers that don't need to be templated on callback type.
class devcb_read_base : public devcb_base
{
protected:
- // construction/destruction
- devcb_read_base(device_t &device, u64 defmask, bool chained = false);
+ // Detecting candidates for read functions
+ template <typename Result, typename Func, typename Enable = void> struct is_read_form1 { static constexpr bool value = false; };
+ template <typename Result, typename Func, typename Enable = void> struct is_read_form2 { static constexpr bool value = false; };
+ template <typename Result, typename Func, typename Enable = void> struct is_read_form3 { static constexpr bool value = false; };
+ template <typename Result, typename Func, typename Enable = void> struct is_read_form4 { static constexpr bool value = false; };
+ template <typename Result, typename Func, typename Enable = void> struct is_read_form5 { static constexpr bool value = false; };
+ template <typename Result, typename Func, typename Enable = void> struct is_read_form6 { static constexpr bool value = false; };
+ template <typename Result, typename Func> struct is_read_form1<Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (address_space &, offs_t, Result)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Result, typename Func> struct is_read_form2<Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (address_space &, offs_t)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Result, typename Func> struct is_read_form3<Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (offs_t, Result)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Result, typename Func> struct is_read_form4<Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (offs_t)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Result, typename Func> struct is_read_form5<Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func (address_space &)>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Result, typename Func> struct is_read_form6<Result, Func, std::enable_if_t<std::is_convertible<std::result_of_t<Func ()>, Result>::value> > { static constexpr bool value = true; };
+ template <typename Result, typename Func> struct is_read { static constexpr bool value = is_read_form1<Result, Func>::value || is_read_form2<Result, Func>::value || is_read_form3<Result, Func>::value || is_read_form4<Result, Func>::value || is_read_form5<Result, Func>::value || is_read_form6<Result, Func>::value; };
+
+ // Determining the result type of a read function
+ template <typename Result, typename Func, typename Enable = void> struct read_result;
+ template <typename Result, typename Func> struct read_result<Result, Func, std::enable_if_t<is_read_form1<Result, Func>::value> > { using type = std::result_of_t<Func (address_space &, offs_t, std::make_unsigned_t<Result>)>; };
+ template <typename Result, typename Func> struct read_result<Result, Func, std::enable_if_t<is_read_form2<Result, Func>::value> > { using type = std::result_of_t<Func (address_space &, offs_t)>; };
+ template <typename Result, typename Func> struct read_result<Result, Func, std::enable_if_t<is_read_form3<Result, Func>::value> > { using type = std::result_of_t<Func (offs_t, std::make_unsigned_t<Result>)>; };
+ template <typename Result, typename Func> struct read_result<Result, Func, std::enable_if_t<is_read_form4<Result, Func>::value> > { using type = std::result_of_t<Func (offs_t)>; };
+ template <typename Result, typename Func> struct read_result<Result, Func, std::enable_if_t<is_read_form5<Result, Func>::value> > { using type = std::result_of_t<Func (address_space &)>; };
+ template <typename Result, typename Func> struct read_result<Result, Func, std::enable_if_t<is_read_form6<Result, Func>::value> > { using type = std::result_of_t<Func ()>; };
+ template <typename Result, typename Func> using read_result_t = typename read_result<Result, Func>::type;
+
+ // Detecting candidates for read delegates
+ template <typename T, typename Enable = void> struct is_read_method { static constexpr bool value = false; };
+ template <typename T> struct is_read_method<T, void_t<emu::detail::read8_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_read_method<T, void_t<emu::detail::read16_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_read_method<T, void_t<emu::detail::read32_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_read_method<T, void_t<emu::detail::read64_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_read_method<T, void_t<emu::detail::read_line_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+
+ // Invoking read callbacks
+ template <typename Result, typename T> static std::enable_if_t<is_read_form1<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask) { return std::make_unsigned_t<read_result_t<Result, T> >(cb(space, offset, mem_mask)); }
+ template <typename Result, typename T> static std::enable_if_t<is_read_form2<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask) { return std::make_unsigned_t<read_result_t<Result, T> >(cb(space, offset)); }
+ template <typename Result, typename T> static std::enable_if_t<is_read_form3<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask) { return std::make_unsigned_t<read_result_t<Result, T> >(cb(offset, mem_mask)); }
+ template <typename Result, typename T> static std::enable_if_t<is_read_form4<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask) { return std::make_unsigned_t<read_result_t<Result, T> >(cb(offset)); }
+ template <typename Result, typename T> static std::enable_if_t<is_read_form5<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask) { return std::make_unsigned_t<read_result_t<Result, T> >(cb(space)); }
+ template <typename Result, typename T> static std::enable_if_t<is_read_form6<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask) { return std::make_unsigned_t<read_result_t<Result, T> >(cb()); }
+
+ // Delegate characteristics
+ template <typename T, typename Dummy = void> struct delegate_traits;
+ template <typename Dummy> struct delegate_traits<read8_delegate, Dummy> { static constexpr u8 default_mask = ~u8(0); };
+ template <typename Dummy> struct delegate_traits<read16_delegate, Dummy> { static constexpr u16 default_mask = ~u16(0); };
+ template <typename Dummy> struct delegate_traits<read32_delegate, Dummy> { static constexpr u32 default_mask = ~u32(0); };
+ template <typename Dummy> struct delegate_traits<read64_delegate, Dummy> { static constexpr u64 default_mask = ~u64(0); };
+ template <typename Dummy> struct delegate_traits<read_line_delegate, Dummy> { static constexpr unsigned default_mask = 1U; };
+
+ using devcb_base::devcb_base;
+ ~devcb_read_base();
+};
- template <typename Delegate, typename Dummy> struct set_helper
- {
- static constexpr bool valid = false;
- };
- template <typename Dummy> struct set_helper<read_line_delegate, Dummy>
- {
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_LINE;
- static void apply(devcb_read_base &devcb, read_line_delegate &&delegate) { devcb.m_readline = std::move(delegate); }
- };
- template <typename Dummy> struct set_helper<read8_delegate, Dummy>
- {
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_8;
- static void apply(devcb_read_base &devcb, read8_delegate &&delegate) { devcb.m_read8 = std::move(delegate); }
- };
- template <typename Dummy> struct set_helper<read16_delegate, Dummy>
- {
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_16;
- static void apply(devcb_read_base &devcb, read16_delegate &&delegate) { devcb.m_read16 = std::move(delegate); }
- };
- template <typename Dummy> struct set_helper<read32_delegate, Dummy>
- {
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_32;
- static void apply(devcb_read_base &devcb, read32_delegate &&delegate) { devcb.m_read32 = std::move(delegate); }
- };
- template <typename Dummy> struct set_helper<read64_delegate, Dummy>
- {
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_64;
- static void apply(devcb_read_base &devcb, read64_delegate &&delegate) { devcb.m_read64 = std::move(delegate); }
- };
-public:
- // callback configuration
- using devcb_base::set_callback;
- template <typename Delegate>
- std::enable_if_t<set_helper<Delegate, void>::valid, devcb_read_base &> set_callback(emu::detail::devcb_delegate_initialiser<Delegate> &&desc)
+/// \brief Write callback utilities
+///
+/// Helpers that don't need to be templated on callback type.
+class devcb_write_base : public devcb_base
+{
+protected:
+ // Detecting candidates for write functions
+ template <typename Input, typename Func, typename Enable = void> struct is_write_form1 { static constexpr bool value = false; };
+ template <typename Input, typename Func, typename Enable = void> struct is_write_form2 { static constexpr bool value = false; };
+ template <typename Input, typename Func, typename Enable = void> struct is_write_form3 { static constexpr bool value = false; };
+ template <typename Input, typename Func, typename Enable = void> struct is_write_form4 { static constexpr bool value = false; };
+ template <typename Input, typename Func, typename Enable = void> struct is_write_form5 { static constexpr bool value = false; };
+ template <typename Input, typename Func, typename Enable = void> struct is_write_form6 { static constexpr bool value = false; };
+ template <typename Input, typename Func> struct is_write_form1<Input, Func, void_t<std::result_of_t<Func (address_space &, offs_t, Input, std::make_unsigned_t<Input>)> > > { static constexpr bool value = true; };
+ template <typename Input, typename Func> struct is_write_form2<Input, Func, void_t<std::result_of_t<Func (address_space &, offs_t, Input)> > > { static constexpr bool value = true; };
+ template <typename Input, typename Func> struct is_write_form3<Input, Func, void_t<std::result_of_t<Func (offs_t, Input, std::make_unsigned_t<Input>)> > > { static constexpr bool value = true; };
+ template <typename Input, typename Func> struct is_write_form4<Input, Func, void_t<std::result_of_t<Func (offs_t, Input)> > > { static constexpr bool value = true; };
+ template <typename Input, typename Func> struct is_write_form5<Input, Func, void_t<std::result_of_t<Func (address_space &, Input)> > > { static constexpr bool value = true; };
+ template <typename Input, typename Func> struct is_write_form6<Input, Func, void_t<std::result_of_t<Func (Input)> > > { static constexpr bool value = true; };
+ template <typename Input, typename Func> struct is_write { static constexpr bool value = is_write_form1<Input, Func>::value || is_write_form2<Input, Func>::value || is_write_form3<Input, Func>::value || is_write_form4<Input, Func>::value || is_write_form5<Input, Func>::value || is_write_form6<Input, Func>::value; };
+
+ // Detecting candidates for write delegates
+ template <typename T, typename Enable = void> struct is_write_method { static constexpr bool value = false; };
+ template <typename T> struct is_write_method<T, void_t<emu::detail::write8_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_write_method<T, void_t<emu::detail::write16_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_write_method<T, void_t<emu::detail::write32_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_write_method<T, void_t<emu::detail::write64_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+ template <typename T> struct is_write_method<T, void_t<emu::detail::write_line_device_class_t<std::remove_reference_t<T> > > > { static constexpr bool value = true; };
+
+ // Invoking write callbacks
+ template <typename Input, typename T> static std::enable_if_t<is_write_form1<Input, T>::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> mem_mask) { return cb(space, offset, data, mem_mask); }
+ template <typename Input, typename T> static std::enable_if_t<is_write_form2<Input, T>::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> mem_mask) { return cb(space, data, offset); }
+ template <typename Input, typename T> static std::enable_if_t<is_write_form3<Input, T>::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> mem_mask) { return cb(offset, data, mem_mask); }
+ template <typename Input, typename T> static std::enable_if_t<is_write_form4<Input, T>::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> mem_mask) { return cb(offset, data); }
+ template <typename Input, typename T> static std::enable_if_t<is_write_form5<Input, T>::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> mem_mask) { return cb(space, data); }
+ template <typename Input, typename T> static std::enable_if_t<is_write_form6<Input, T>::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t<Input> mem_mask) { return cb(data); }
+
+ // Delegate characteristics
+ template <typename T, typename Dummy = void> struct delegate_traits;
+ template <typename Dummy> struct delegate_traits<write8_delegate, Dummy> { using input_t = u8; static constexpr u8 default_mask = ~u8(0); };
+ template <typename Dummy> struct delegate_traits<write16_delegate, Dummy> { using input_t = u16; static constexpr u16 default_mask = ~u16(0); };
+ template <typename Dummy> struct delegate_traits<write32_delegate, Dummy> { using input_t = u32; static constexpr u32 default_mask = ~u32(0); };
+ template <typename Dummy> struct delegate_traits<write64_delegate, Dummy> { using input_t = u64; static constexpr u64 default_mask = ~u64(0); };
+ template <typename Dummy> struct delegate_traits<write_line_delegate, Dummy> { using input_t = int; static constexpr unsigned default_mask = 1U; };
+
+ using devcb_base::devcb_base;
+ ~devcb_write_base();
+};
+
+
+/// \brief Read callback helper
+///
+/// Allows binding a variety of signatures, composing a result from
+/// multiple callbacks, and chained arbitrary transforms. Transforms
+/// may modify the offset and mask.
+template <typename Result, std::make_unsigned_t<Result> DefaultMask = std::make_unsigned_t<Result>(~std::make_unsigned_t<Result>(0))>
+class devcb_read : public devcb_read_base
+{
+private:
+ using func_t = std::function<Result (address_space &, offs_t, std::make_unsigned_t<Result>)>;
+
+ class creator
{
- if (desc.m_base) reset(*desc.m_base, set_helper<Delegate, void>::type);
- else reset(set_helper<Delegate, void>::type);
- set_helper<Delegate, void>::apply(*this, std::move(desc.m_delegate));
- return *this;
- }
- template <typename... Params> auto &chain(Params &&... args) { return chain_alloc().set_callback(std::forward<Params>(args)...); }
- devcb_read_base &chain_alloc();
+ public:
+ using ptr = std::unique_ptr<creator>;
- // resolution
- void resolve();
- void resolve_safe(u64 none_constant_value);
+ virtual ~creator() { }
+ virtual void validity_check(validity_checker &valid) const = 0;
+ virtual func_t create() = 0;
- // validity checking
- void validity_check(validity_checker &valid) const;
+ std::make_unsigned_t<Result> mask() const { return m_mask; }
-protected:
- // internal helpers
- virtual void devcb_reset() override;
- inline u64 read(address_space &space, offs_t offset, u64 mask);
+ protected:
+ creator(std::make_unsigned_t<Result> mask) : m_mask(mask) { }
-private:
- // adapters
- u64 read_unresolved_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read_line_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read8_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read16_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read32_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read64_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read_ioport_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read_logged_adapter(address_space &space, offs_t offset, u64 mask);
- u64 read_constant_adapter(address_space &space, offs_t offset, u64 mask);
-
- // configuration
- read_line_delegate m_readline; // copy of registered line reader
- read8_delegate m_read8; // copy of registered 8-bit reader
- read16_delegate m_read16; // copy of registered 16-bit reader
- read32_delegate m_read32; // copy of registered 32-bit reader
- read64_delegate m_read64; // copy of registered 64-bit reader
-
- // derived state
- typedef u64 (devcb_read_base::*adapter_func)(address_space &, offs_t, u64);
- adapter_func m_adapter; // actual callback to invoke
- std::unique_ptr<devcb_read_base> m_chain; // next callback for chained input
-};
+ std::make_unsigned_t<Result> m_mask;
+ };
+ template <typename T>
+ class creator_impl : public creator
+ {
+ public:
+ creator_impl(T &&builder) : creator(builder.mask()), m_builder(std::move(builder)) { }
-// ======================> devcb_write_base
+ virtual void validity_check(validity_checker &valid) const override { m_builder.validity_check(valid); }
-class devcb_write_base : public devcb_base
-{
-protected:
- // construction/destruction
- devcb_write_base(device_t &device, u64 defmask, bool chained = false);
+ virtual func_t create() override
+ {
+ func_t result;
+ m_builder.build([&result] (auto &&f) { result = [cb = std::move(f)] (address_space &space, offs_t offset, typename T::input_mask_t mem_mask) { return cb(space, offset, mem_mask); }; });
+ return std::move(result);
+ }
+
+ private:
+ T m_builder;
+ };
- template <typename Delegate, typename Dummy> struct set_helper
+ class log_creator : public creator
{
- static constexpr bool valid = false;
+ public:
+ log_creator(device_t &devbase, std::string &&message) : creator(0U), m_devbase(devbase), m_message(std::move(message)) { }
+
+ virtual void validity_check(validity_checker &valid) const override { }
+
+ virtual func_t create() override
+ {
+ return
+ [&devbase = m_devbase, message = std::move(m_message)] (address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask)
+ {
+ devbase.logerror("%s: %s\n", devbase.machine().describe_context(), message);
+ return Result(0);
+ };
+ }
+
+ private:
+ device_t &m_devbase;
+ std::string m_message;
};
- template <typename Dummy> struct set_helper<write_line_delegate, Dummy>
+
+ class builder_base
+ {
+ protected:
+ builder_base(devcb_read &target, bool append) : m_target(target), m_append(append) { }
+ builder_base(builder_base const &) = delete;
+ builder_base(builder_base &&) = default;
+ ~builder_base() { assert(m_consumed); }
+ builder_base &operator=(builder_base const &) = delete;
+ builder_base &operator=(builder_base &&) = default;
+
+ void consume() { m_consumed = true; }
+ void built() { assert(!m_built); m_built = true; }
+
+ template <typename T>
+ void register_creator()
+ {
+ if (!m_consumed)
+ {
+ if (!m_append)
+ m_target.m_creators.clear();
+ consume();
+ m_target.m_creators.emplace_back(std::make_unique<creator_impl<T> >(std::move(static_cast<T &>(*this))));
+ }
+ }
+
+ devcb_read &m_target;
+ bool const m_append;
+ bool m_consumed = false;
+ bool m_built = false;
+ };
+
+ template <typename Source, typename Func>
+ class transform_builder : public builder_base, public transform_base<mask_t<transform_result_t<typename Source::output_t, Result, Func>, Result>, transform_builder<Source, Func> >
{
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_LINE;
- static void apply(devcb_write_base &devcb, write_line_delegate &&delegate) { devcb.m_writeline = std::move(delegate); }
+ public:
+ template <typename T, typename U> friend class transform_builder;
+
+ using input_t = typename Source::output_t;
+ using output_t = mask_t<transform_result_t<input_t, Result, Func>, Result>;
+ using input_mask_t = mask_t<input_t, typename Source::input_mask_t>;
+
+ template <typename T>
+ transform_builder(devcb_read &target, bool append, Source &&src, T &&cb, output_t mask)
+ : builder_base(target, append)
+ , transform_base<output_t, transform_builder>(mask)
+ , m_src(std::move(src))
+ , m_cb(std::forward<T>(cb))
+ { m_src.consume(); }
+ transform_builder(transform_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<output_t, transform_builder>(std::move(that))
+ , m_src(std::move(that.m_src))
+ , m_cb(std::move(that.m_cb))
+ {
+ m_src.consume();
+ that.consume();
+ that.built();
+ }
+ ~transform_builder() { this->template register_creator<transform_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<output_t, Result, T>::value, transform_builder<transform_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ output_t const m(this->mask());
+ if (this->inherited_mask())
+ this->mask(std::make_unsigned_t<transform_result_t<typename Source::output_t, Result, Func> >(~std::make_unsigned_t<transform_result_t<typename Source::output_t, Result, Func> >(0)));
+ return transform_builder<transform_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, std::move(*this), std::forward<T>(cb), m);
+ }
+
+ void validity_check(validity_checker &valid) const { m_src.validity_check(valid); }
+
+ template <typename T>
+ void build(T &&chain)
+ {
+ assert(this->m_consumed);
+ auto wrap([this, c = std::forward<T>(chain)] (auto &&f) mutable { this->build(std::move(c), std::move(f)); });
+ m_src.build(std::move(wrap));
+ }
+
+ private:
+ transform_builder(transform_builder const &) = delete;
+ transform_builder &operator=(transform_builder const &) = delete;
+ transform_builder &operator=(transform_builder &&that) = delete;
+
+ template <typename T, typename U>
+ void build(T &&chain, U &&f)
+ {
+ assert(this->m_consumed);
+ this->built();
+ chain(
+ [src = std::forward<U>(f), cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t &offset, input_mask_t &mem_mask)
+ {
+ typename Source::input_mask_t source_mask(mem_mask);
+ auto const data(src(space, offset, source_mask));
+ mem_mask = source_mask & mask;
+ return (devcb_read::invoke_transform<input_t, Result>(cb, space, offset, data, mem_mask) ^ exor) & mask;
+ });
+ }
+
+ Source m_src;
+ Func m_cb;
};
- template <typename Dummy> struct set_helper<write8_delegate, Dummy>
+
+ template <typename Func>
+ class functoid_builder : public builder_base, public transform_base<mask_t<read_result_t<Result, Func>, Result>, functoid_builder<Func> >
{
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_8;
- static void apply(devcb_write_base &devcb, write8_delegate &&delegate) { devcb.m_write8 = std::move(delegate); }
+ public:
+ template <typename T, typename U> friend class transform_builder;
+
+ using output_t = mask_t<read_result_t<Result, Func>, Result>;
+ using input_mask_t = std::make_unsigned_t<Result>;
+
+ template <typename T>
+ functoid_builder(devcb_read &target, bool append, T &&cb)
+ : builder_base(target, append)
+ , transform_base<output_t, functoid_builder>(DefaultMask)
+ , m_cb(std::forward<T>(cb))
+ { }
+ functoid_builder(functoid_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<output_t, functoid_builder>(std::move(that))
+ , m_cb(std::move(that.m_cb))
+ {
+ that.consume();
+ that.built();
+ }
+ ~functoid_builder() { this->template register_creator<functoid_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<output_t, Result, T>::value, transform_builder<functoid_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ output_t const m(this->mask());
+ if (this->inherited_mask())
+ this->mask(std::make_unsigned_t<read_result_t<Result, Func> >(~std::make_unsigned_t<read_result_t<Result, Func> >(0)));
+ return transform_builder<functoid_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, std::move(*this), std::forward<T>(cb), m);
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ template <typename T>
+ void build(T &&chain)
+ {
+ assert(this->m_consumed);
+ this->built();
+ chain(
+ [cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_mask_t mem_mask)
+ { return (devcb_read::invoke_read<Result>(cb, space, offset, mem_mask & mask) ^ exor) & mask; });
+ }
+
+ private:
+ functoid_builder(functoid_builder const &) = delete;
+ functoid_builder &operator=(functoid_builder const &) = delete;
+ functoid_builder &operator=(functoid_builder &&that) = delete;
+
+ Func m_cb;
};
- template <typename Dummy> struct set_helper<write16_delegate, Dummy>
+
+ template <typename Delegate>
+ class delegate_builder : public builder_base, public transform_base<mask_t<read_result_t<Result, Delegate>, Result>, delegate_builder<Delegate> >
{
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_16;
- static void apply(devcb_write_base &devcb, write16_delegate &&delegate) { devcb.m_write16 = std::move(delegate); }
+ public:
+ template <typename T, typename U> friend class transform_builder;
+
+ using output_t = mask_t<read_result_t<Result, Delegate>, Result>;
+ using input_mask_t = std::make_unsigned_t<Result>;
+
+ template <typename T>
+ delegate_builder(devcb_read &target, bool append, device_t &devbase, char const *tag, T &&func, char const *name)
+ : builder_base(target, append)
+ , transform_base<output_t, delegate_builder>(DefaultMask & delegate_traits<Delegate>::default_mask)
+ , m_devbase(devbase)
+ , m_delegate(std::forward<T>(func), name, tag, std::add_pointer_t<devcb_read::delegate_device_class_t<T> >(nullptr))
+ { }
+ template <typename T>
+ delegate_builder(devcb_read &target, bool append, device_t &devbase, devcb_read::delegate_device_class_t<T> &obj, T &&func, char const *name)
+ : builder_base(target, append)
+ , transform_base<output_t, delegate_builder>(DefaultMask & delegate_traits<Delegate>::default_mask)
+ , m_devbase(devbase)
+ , m_delegate(std::forward<T>(func), name, devcb_read::get_tag(obj), &obj)
+ { }
+ delegate_builder(delegate_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<output_t, delegate_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_delegate(std::move(that.m_delegate))
+ {
+ that.consume();
+ that.built();
+ }
+ ~delegate_builder() { this->template register_creator<delegate_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<output_t, Result, T>::value, transform_builder<delegate_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ output_t const m(this->mask());
+ if (this->inherited_mask())
+ this->mask(delegate_traits<Delegate>::default_mask);
+ return transform_builder<delegate_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, std::move(*this), std::forward<T>(cb), m);
+ }
+
+ void validity_check(validity_checker &valid) const
+ {
+ if (!m_devbase.subdevice(m_delegate.device_name()))
+ osd_printf_error("Read callback bound to non-existent object tag %s (%s)\n", m_delegate.device_name(), m_delegate.name());
+ }
+
+ template <typename T>
+ void build(T &&chain)
+ {
+ assert(this->m_consumed);
+ this->built();
+ m_delegate.bind_relative_to(m_devbase);
+ chain(
+ [cb = std::move(this->m_delegate), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_mask_t mem_mask)
+ { return (devcb_read::invoke_read<Result>(cb, space, offset, mem_mask & mask) ^ exor) & mask; });
+ }
+
+ private:
+ delegate_builder(delegate_builder const &) = delete;
+ delegate_builder &operator=(delegate_builder const &) = delete;
+ delegate_builder &operator=(delegate_builder &&that) = delete;
+
+ device_t &m_devbase;
+ Delegate m_delegate;
};
- template <typename Dummy> struct set_helper<write32_delegate, Dummy>
+
+ class ioport_builder : public builder_base, public transform_base<mask_t<ioport_value, Result>, ioport_builder>
{
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_32;
- static void apply(devcb_write_base &devcb, write32_delegate &&delegate) { devcb.m_write32 = std::move(delegate); }
+ public:
+ template <typename T, typename U> friend class transform_builder;
+
+ using output_t = mask_t<ioport_value, Result>;
+ using input_mask_t = std::make_unsigned_t<Result>;
+
+ ioport_builder(devcb_read &target, bool append, device_t &devbase, std::string &&tag)
+ : builder_base(target, append)
+ , transform_base<output_t, ioport_builder>(DefaultMask)
+ , m_devbase(devbase)
+ , m_tag(std::move(tag))
+ { }
+ ioport_builder(ioport_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<output_t, ioport_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+ ~ioport_builder() { this->template register_creator<ioport_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<output_t, Result, T>::value, transform_builder<ioport_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ output_t const m(this->mask());
+ if (this->inherited_mask())
+ this->mask(std::make_unsigned_t<ioport_value>(~std::make_unsigned_t<ioport_value>(0)));
+ return transform_builder<ioport_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, std::move(*this), std::forward<T>(cb), m);
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ template <typename T>
+ void build(T &&chain)
+ {
+ assert(this->m_consumed);
+ this->built();
+ ioport_port *const ioport(m_devbase.ioport(m_tag.c_str()));
+ if (!ioport)
+ throw emu_fatalerror("Read callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name());
+ chain(
+ [&port = *ioport, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_mask_t mem_mask)
+ { return (port.read() ^ exor) & mask; });
+ }
+
+ private:
+ ioport_builder(ioport_builder const &) = delete;
+ ioport_builder &operator=(ioport_builder const &) = delete;
+ ioport_builder &operator=(ioport_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_tag;
};
- template <typename Dummy> struct set_helper<write64_delegate, Dummy>
+
+ class binder
{
- static constexpr bool valid = true;
- static constexpr callback_type type = CALLBACK_64;
- static void apply(devcb_write_base &devcb, write64_delegate &&delegate) { devcb.m_write64 = std::move(delegate); }
+ public:
+ binder(devcb_read &target) : m_target(target) { }
+ binder(binder const &) = delete;
+ binder(binder &&that) : m_target(that.m_target), m_append(that.m_append), m_used(that.m_used) { that.m_used = true; }
+ binder &operator=(binder const &) = delete;
+ binder &operator=(binder &&) = delete;
+
+ template <typename T>
+ std::enable_if_t<is_read<Result, T>::value, functoid_builder<std::remove_reference_t<T> > > set(T &&cb)
+ {
+ set_used();
+ return functoid_builder<std::remove_reference_t<T> >(m_target, m_append, std::forward<T>(cb));
+ }
+
+ template <typename T>
+ std::enable_if_t<is_read_method<T>::value, delegate_builder<delegate_type_t<T> > > set(T &&func, char const *name)
+ {
+ set_used();
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner().mconfig().current_device(), DEVICE_SELF, std::forward<T>(func), name);
+ }
+
+ template <typename T>
+ std::enable_if_t<is_read_method<T>::value, delegate_builder<delegate_type_t<T> > > set(char const *tag, T &&func, char const *name)
+ {
+ set_used();
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner().mconfig().current_device(), tag, std::forward<T>(func), name);
+ }
+
+ template <typename T, typename U>
+ std::enable_if_t<is_read_method<T>::value, delegate_builder<delegate_type_t<T> > > set(U &obj, T &&func, char const *name)
+ {
+ set_used();
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner().mconfig().current_device(), devcb_read::cast_reference<delegate_device_class_t<T> >(obj), std::forward<T>(func), name);
+ }
+
+ template <typename T, typename U, bool R>
+ std::enable_if_t<is_read_method<T>::value, delegate_builder<delegate_type_t<T> > > set(device_finder<U, R> &finder, T &&func, char const *name)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, target.first, target.second, std::forward<T>(func), name);
+ }
+
+ template <typename T, typename U, bool R>
+ std::enable_if_t<is_read_method<T>::value, delegate_builder<delegate_type_t<T> > > set(device_finder<U, R> const &finder, T &&func, char const *name)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, target.first, target.second, std::forward<T>(func), name);
+ }
+
+ template <typename... Params>
+ auto append(Params &&... args)
+ {
+ m_append = true;
+ return set(std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ ioport_builder set_ioport(Params &&... args)
+ {
+ set_used();
+ return ioport_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward<Params>(args)...));
+ }
+
+ template <bool R>
+ ioport_builder set_ioport(ioport_finder<R> &finder)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return ioport_builder(m_target, m_append, target.first, std::string(target.second));
+ }
+
+ template <bool R>
+ ioport_builder set_ioport(ioport_finder<R> const &finder)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return ioport_builder(m_target, m_append, target.first, std::string(target.second));
+ }
+
+ template <typename... Params>
+ ioport_builder append_ioport(Params &&... args)
+ {
+ m_append = true;
+ return set_ioport(std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ void set_log(device_t &devbase, Params &&... args)
+ {
+ set_used();
+ if (!m_append)
+ m_target.m_creators.clear();
+ m_target.m_creators.emplace_back(std::make_unique<log_creator>(devbase, std::string(std::forward<Params>(args)...)));
+ }
+
+ template <typename T, typename... Params>
+ std::enable_if_t<emu::detail::is_device_implementation<std::remove_reference_t<T> >::value> set_log(T &devbase, Params &&... args)
+ {
+ set_log(static_cast<device_t &>(devbase), std::forward<Params>(args)...);
+ }
+
+ template <typename T, typename... Params>
+ std::enable_if_t<emu::detail::is_device_interface<std::remove_reference_t<T> >::value> set_log(T &devbase, Params &&... args)
+ {
+ set_log(devbase.device(), std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ void set_log(Params &&... args)
+ {
+ set_log(m_target.owner().mconfig().current_device(), std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ void append_log(Params &&... args)
+ {
+ m_append = true;
+ set_log(std::forward<Params>(args)...);
+ }
+
+ auto set_constant(Result val) { return set([val] () { return val; }); }
+ auto append_constant(Result val) { return append([val] () { return val; }); }
+
+ private:
+ void set_used() { assert(!m_used); m_used = true; }
+
+ devcb_read &m_target;
+ bool m_append = false;
+ bool m_used = false;
};
+ std::vector<func_t> m_functions;
+ std::vector<typename creator::ptr> m_creators;
+
public:
- // callback configuration
- using devcb_base::set_callback;
- template <typename Delegate>
- std::enable_if_t<set_helper<Delegate, void>::valid, devcb_write_base &> set_callback(emu::detail::devcb_delegate_initialiser<Delegate> &&desc)
+ devcb_read(device_t &owner);
+
+ binder bind();
+ void reset();
+
+ // legacy syntax support - will be removed
+ template <typename T, typename U, typename V>
+ devcb_base &set_callback(std::tuple<T, U, V> const &desc)
{
- if (desc.m_base) reset(*desc.m_base, set_helper<Delegate, void>::type);
- else reset(set_helper<Delegate, void>::type);
- set_helper<Delegate, void>::apply(*this, std::move(desc.m_delegate));
+ bind().set(std::forward<T>(std::get<0>(desc)), std::forward<U>(std::get<1>(desc)), std::forward<V>(std::get<2>(desc)));
return *this;
}
- template <callback_type Type> devcb_write_base &set_callback(line_desc<Type> desc)
+ devcb_base &set_callback(DEVCB_IOPORT &&desc) { bind().set_ioport(desc.m_tag); return *this; }
+ template <typename T>
+ devcb_base &set_callback(devcb_constant_t<T> &&desc)
{
- if (desc.m_base) reset(*desc.m_base, Type);
- else reset(Type);
- m_target_tag = desc.m_tag;
- m_target_int = desc.m_inputnum;
+ bind().set_constant(std::move(desc.m_value));
return *this;
}
- template <typename... Params> auto &chain(Params &&... args) { return chain_alloc().set_callback(std::forward<Params>(args)...); }
- devcb_write_base &chain_alloc();
- // resolution
- void resolve();
- void resolve_safe();
+ virtual void validity_check(validity_checker &valid) const override;
- // validity checking
- void validity_check(validity_checker &valid) const;
+ void resolve();
+ void resolve_safe(Result dflt);
-protected:
- // internal helpers
- virtual void devcb_reset() override;
- inline void write(address_space &space, offs_t offset, u64 data, u64 mask);
+ Result operator()(address_space &space, offs_t offset = 0, std::make_unsigned_t<Result> mem_mask = DefaultMask);
+ Result operator()(offs_t offset, std::make_unsigned_t<Result> mem_mask = DefaultMask);
+ Result operator()();
-private:
- // adapters
- void write_unresolved_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_line_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write8_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write16_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write32_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write64_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_ioport_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_membank_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_output_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_logged_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_noop_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_inputline_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_assertline_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_clearline_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
- void write_holdline_adapter(address_space &space, offs_t offset, u64 data, u64 mask);
-
- // configuration
- write_line_delegate m_writeline; // copy of registered line writer
- write8_delegate m_write8; // copy of registered 8-bit writer
- write16_delegate m_write16; // copy of registered 16-bit writer
- write32_delegate m_write32; // copy of registered 32-bit writer
- write64_delegate m_write64; // copy of registered 64-bit writer
-
- // derived state
- typedef void (devcb_write_base::*adapter_func)(address_space &, offs_t, u64, u64);
- adapter_func m_adapter; // actual callback to invoke
- std::unique_ptr<devcb_write_base> m_chain; // next callback for chained output
+ bool isnull() const { return m_functions.empty() && m_creators.empty(); }
+ explicit operator bool() const { return !m_functions.empty(); }
};
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+devcb_read<Result, DefaultMask>::devcb_read(device_t &owner)
+ : devcb_read_base(owner)
+{
+}
-// ======================> devcb_read_line
-
-class devcb_read_line : public devcb_read_base
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+typename devcb_read<Result, DefaultMask>::binder devcb_read<Result, DefaultMask>::bind()
{
-public:
- devcb_read_line(device_t &device) : devcb_read_base(device, 1) { }
- int operator()() { return read(*m_space, 0, 0xffU) & 1; }
- int operator()(address_space &space) { return read((m_space_tag != nullptr) ? *m_space : space, 0, 0xffU) & 1; }
-};
+ return binder(*this);
+}
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+void devcb_read<Result, DefaultMask>::reset()
+{
+ assert(m_functions.empty());
+ m_creators.clear();
+}
-// ======================> devcb_read8
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+void devcb_read<Result, DefaultMask>::validity_check(validity_checker &valid) const
+{
+ assert(m_functions.empty());
+ devcb_read_base::validity_check(valid);
+ for (typename std::vector<typename creator::ptr>::const_iterator i = m_creators.begin(); m_creators.end() != i; ++i)
+ {
+ (*i)->validity_check(valid);
+ std::make_unsigned_t<Result> const m((*i)->mask());
+ for (typename std::vector<typename creator::ptr>::const_iterator j = std::next(i); m_creators.end() != j; ++j)
+ {
+ std::make_unsigned_t<Result> const n((*j)->mask());
+ if (m & n)
+ osd_printf_error("Read callback masks %lX and %lX overlap\n", static_cast<unsigned long>(m), static_cast<unsigned long>(n)); // FIXME: doesn't work with u64
+ }
+ }
+}
-class devcb_read8 : public devcb_read_base
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+void devcb_read<Result, DefaultMask>::resolve()
{
-public:
- devcb_read8(device_t &device) : devcb_read_base(device, 0xff) { }
- u8 operator()(offs_t offset = 0, u8 mask = 0xff) { return read(*m_space, offset, mask) & mask; }
- u8 operator()(address_space &space, offs_t offset = 0, u8 mask = 0xff) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; }
-};
+ assert(m_functions.empty());
+ devcb_read_base::resolve();
+ m_functions.reserve(m_creators.size());
+ for (typename creator::ptr const &c : m_creators)
+ m_functions.emplace_back(c->create());
+ m_creators.clear();
+}
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+void devcb_read<Result, DefaultMask>::resolve_safe(Result dflt)
+{
+ resolve();
+ if (m_functions.empty())
+ m_functions.emplace_back([dflt] (address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask) { return dflt; });
+}
-// ======================> devcb_read16
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+Result devcb_read<Result, DefaultMask>::operator()(address_space &space, offs_t offset, std::make_unsigned_t<Result> mem_mask)
+{
+ assert(m_creators.empty() && !m_functions.empty());
+ typename std::vector<func_t>::const_iterator it(m_functions.begin());
+ std::make_unsigned_t<Result> result((*it)(space, offset, mem_mask));
+ while (m_functions.end() != ++it)
+ result |= (*it)(space, offset, mem_mask);
+ return result;
+}
-class devcb_read16 : public devcb_read_base
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+Result devcb_read<Result, DefaultMask>::operator()(offs_t offset, std::make_unsigned_t<Result> mem_mask)
{
-public:
- devcb_read16(device_t &device) : devcb_read_base(device, 0xffff) { }
- u16 operator()(offs_t offset = 0, u16 mask = 0xffff) { return read(*m_space, offset, mask) & mask; }
- u16 operator()(address_space &space, offs_t offset = 0, u16 mask = 0xffff) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; }
-};
+ return this->operator()(this->default_space(), offset, mem_mask);
+}
+template <typename Result, std::make_unsigned_t<Result> DefaultMask>
+Result devcb_read<Result, DefaultMask>::operator()()
+{
+ return this->operator()(this->default_space(), 0U, DefaultMask);
+}
-// ======================> devcb_read32
-class devcb_read32 : public devcb_read_base
+/// \brief Write callback helper
+///
+/// Allows binding a variety of signatures, sending the value to
+/// multiple callbacks, and chained arbitrary transforms. Transforms
+/// may modify the offset and mask.
+template <typename Input, std::make_unsigned_t<Input> DefaultMask = std::make_unsigned_t<Input>(~std::make_unsigned_t<Input>(0))>
+class devcb_write : public devcb_write_base
{
-public:
- devcb_read32(device_t &device) : devcb_read_base(device, 0xffffffff) { }
- u32 operator()(offs_t offset = 0, u32 mask = 0xffffffff) { return read(*m_space, offset, mask) & mask; }
- u32 operator()(address_space &space, offs_t offset = 0, u32 mask = 0xffffffff) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; }
-};
+private:
+ using func_t = std::function<void (address_space &, offs_t, Input, std::make_unsigned_t<Input>)>;
+ class creator
+ {
+ public:
+ using ptr = std::unique_ptr<creator>;
-// ======================> devcb_read64
+ virtual ~creator() { }
+ virtual void validity_check(validity_checker &valid) const = 0;
+ virtual func_t create() = 0;
+ };
-class devcb_read64 : public devcb_read_base
-{
-public:
- devcb_read64(device_t &device) : devcb_read_base(device, 0xffffffffffffffffU) { }
- u64 operator()(offs_t offset = 0, u64 mask = 0xffffffffffffffffU) { return read(*m_space, offset, mask) & mask; }
- u64 operator()(address_space &space, offs_t offset = 0, u64 mask = 0xffffffffffffffffU) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; }
-};
+ template <typename T>
+ class creator_impl : public creator
+ {
+ public:
+ creator_impl(T &&builder) : m_builder(std::move(builder)) { }
+ virtual void validity_check(validity_checker &valid) const override { m_builder.validity_check(valid); }
-// ======================> devcb_write_line
+ virtual func_t create() override
+ {
+ return [cb = m_builder.build()] (address_space &space, offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask) { cb(space, offset, data, mem_mask); };
+ }
-class devcb_write_line : public devcb_write_base
-{
-public:
- devcb_write_line(device_t &device) : devcb_write_base(device, 1) { }
- void operator()(int state) { write(*m_space, 0, state & 1, 0xffU); }
- void operator()(address_space &space, int state) { write((m_space_tag != nullptr) ? *m_space : space, 0, state & 1, 0xffU); }
-};
+ private:
+ T m_builder;
+ };
+ class nop_creator : public creator
+ {
+ public:
+ virtual void validity_check(validity_checker &valid) const override { }
+ virtual func_t create() override { return [] (address_space &space, offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask) { }; }
+ };
-// ======================> devcb_write8
+ class builder_base
+ {
+ protected:
+ builder_base(devcb_write &target, bool append) : m_target(target), m_append(append) { }
+ builder_base(builder_base const &) = delete;
+ builder_base(builder_base &&) = default;
+ ~builder_base() { assert(m_consumed); }
+ builder_base &operator=(builder_base const &) = delete;
+ builder_base &operator=(builder_base &&) = default;
+
+ void consume() { m_consumed = true; }
+ void built() { assert(!m_built); m_built = true; }
+
+ template <typename T>
+ void register_creator()
+ {
+ if (!m_consumed)
+ {
+ if (!m_append)
+ m_target.m_creators.clear();
+ consume();
+ m_target.m_creators.emplace_back(std::make_unique<creator_impl<T> >(std::move(static_cast<T &>(*this))));
+ }
+ }
+
+ devcb_write &m_target;
+ bool const m_append;
+ bool m_consumed = false;
+ bool m_built = false;
+ };
-class devcb_write8 : public devcb_write_base
-{
-public:
- devcb_write8(device_t &device) : devcb_write_base(device, 0xff) { }
- void operator()(u8 data, u8 mask = 0xff) { write(*m_space, 0, data, mask); }
- void operator()(offs_t offset, u8 data, u8 mask = 0xff) { write(*m_space, offset, data, mask); }
- void operator()(address_space &space, offs_t offset, u8 data, u8 mask = 0xff) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); }
-};
+ template <typename Source, typename Func>
+ class transform_builder : public builder_base, public transform_base<mask_t<transform_result_t<typename Source::output_t, typename Source::output_t, Func>, typename Source::output_t>, transform_builder<Source, Func> >
+ {
+ public:
+ template <typename T, typename U> friend class transform_builder;
+
+ using input_t = typename Source::output_t;
+ using output_t = mask_t<transform_result_t<typename Source::output_t, typename Source::output_t, Func>, typename Source::output_t>;
+
+ template <typename T>
+ transform_builder(devcb_write &target, bool append, Source &&src, T &&cb, output_t mask)
+ : builder_base(target, append)
+ , transform_base<output_t, transform_builder>(mask)
+ , m_src(std::move(src))
+ , m_cb(std::forward<T>(cb))
+ { m_src.consume(); }
+ transform_builder(transform_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<output_t, transform_builder>(std::move(that))
+ , m_src(std::move(that.m_src))
+ , m_cb(std::move(that.m_cb))
+ {
+ m_src.consume();
+ that.consume();
+ that.built();
+ }
+ ~transform_builder() { this->template register_creator<transform_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<output_t, output_t, T>::value, transform_builder<transform_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return transform_builder<transform_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, std::move(*this), std::forward<T>(cb), this->mask());
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return m_src.build(
+ [cb = std::move(m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t &offset, input_t data, std::make_unsigned_t<input_t> &mem_mask)
+ {
+ auto const trans(devcb_write::invoke_transform<input_t, output_t>(cb, space, offset, data, mem_mask));
+ mem_mask &= mask;
+ return (trans ^ exor) & mask;
+ });
+ }
+
+ void validity_check(validity_checker &valid) const { m_src.validity_check(valid); }
+
+ private:
+ transform_builder(transform_builder const &) = delete;
+ transform_builder &operator=(transform_builder const &) = delete;
+ transform_builder &operator=(transform_builder &&that) = delete;
+
+ template <typename T>
+ auto build(T &&chain)
+ {
+ assert(this->m_consumed);
+ this->built();
+ return m_src.build(
+ [f = std::move(chain), cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t &offset, input_t data, std::make_unsigned_t<input_t> &mem_mask)
+ {
+ auto const trans(devcb_write::invoke_transform<input_t, output_t>(cb, space, offset, data, mem_mask));
+ output_t out_mask(mem_mask & mask);
+ return f(space, offset, (trans ^ exor) & mask, out_mask);
+ });
+ }
+
+ Source m_src;
+ Func m_cb;
+ };
+ template <typename Sink, typename Func>
+ class first_transform_builder : public builder_base, public transform_base<mask_t<transform_result_t<typename Sink::input_t, typename Sink::input_t, Func>, typename Sink::input_t>, first_transform_builder<Sink, Func> >
+ {
+ public:
+ template <typename T, typename U> friend class transform_builder;
+
+ using input_t = typename Sink::input_t;
+ using output_t = mask_t<transform_result_t<typename Sink::input_t, typename Sink::input_t, Func>, typename Sink::input_t>;
+
+ template <typename T>
+ first_transform_builder(devcb_write &target, bool append, Sink &&sink, T &&cb, std::make_unsigned_t<Input> in_exor, std::make_unsigned_t<Input> in_mask)
+ : builder_base(target, append)
+ , transform_base<output_t, first_transform_builder>(DefaultMask)
+ , m_sink(std::move(sink))
+ , m_cb(std::forward<T>(cb))
+ , m_in_exor(in_exor & in_mask)
+ , m_in_mask(in_mask)
+ { m_sink.consume(); }
+ first_transform_builder(first_transform_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<output_t, first_transform_builder>(std::move(that))
+ , m_sink(std::move(that.m_sink))
+ , m_cb(std::move(that.m_cb))
+ , m_in_exor(that.m_in_exor)
+ , m_in_mask(that.m_in_mask)
+ {
+ m_sink.consume();
+ that.consume();
+ that.built();
+ }
+ ~first_transform_builder() { this->template register_creator<first_transform_builder>(); }
+
+ void validity_check(validity_checker &valid) const { m_sink.validity_check(valid); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<output_t, output_t, T>::value, transform_builder<first_transform_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return transform_builder<first_transform_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, std::move(*this), std::forward<T>(cb), this->mask());
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [sink = m_sink.build(), cb = std::move(this->m_cb), in_exor = m_in_exor, in_mask = m_in_mask, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ {
+ data = (data ^ in_exor) & in_mask;
+ mem_mask &= in_mask;
+ auto const trans(devcb_write::invoke_transform<input_t, output_t>(cb, space, offset, data, mem_mask));
+ mem_mask &= mask;
+ sink(space, offset, (trans ^ exor) & mask, mem_mask);
+ };
+ }
+
+ private:
+ first_transform_builder(first_transform_builder const &) = delete;
+ first_transform_builder operator=(first_transform_builder const &) = delete;
+ first_transform_builder operator=(first_transform_builder &&that) = delete;
+
+ template <typename T>
+ auto build(T &&chain)
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [f = std::move(chain), sink = m_sink.build(), cb = std::move(this->m_cb), in_exor = m_in_exor, in_mask = m_in_mask, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ {
+ data = (data ^ in_exor) & in_mask;
+ mem_mask &= in_mask;
+ auto const trans_1(devcb_write::invoke_transform<input_t, output_t>(cb, space, offset, data, mem_mask));
+ output_t out_mask(mem_mask & mask);
+ auto const trans_n(f(space, offset, (trans_1 ^ exor) & mask, out_mask));
+ sink(space, offset, trans_n, out_mask);
+ };
+ }
+
+ Sink m_sink;
+ Func m_cb;
+ std::make_unsigned_t<Input> const m_in_exor, m_in_mask;
+ };
-// ======================> devcb_write16
+ template <typename Func>
+ class functoid_builder : public builder_base, public transform_base<std::make_unsigned_t<Input>, functoid_builder<Func> >
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = Input;
+
+ wrapped_builder(functoid_builder &&that) : builder_base(std::move(that)), m_cb(std::move(that.m_cb)) { that.consume(); that.built(); }
+ wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)), m_cb(std::move(that.m_cb)) { that.consume(); that.built(); }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [cb = std::move(this->m_cb)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { devcb_write::invoke_write<Input>(cb, space, offset, data, mem_mask); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ Func m_cb;
+ };
+
+ functoid_builder(functoid_builder const &) = delete;
+ functoid_builder &operator=(functoid_builder const &) = delete;
+ functoid_builder &operator=(functoid_builder &&that) = delete;
+
+ Func m_cb;
+
+ public:
+ using input_t = Input;
+
+ template <typename T>
+ functoid_builder(devcb_write &target, bool append, T &&cb)
+ : builder_base(target, append)
+ , transform_base<std::make_unsigned_t<Input>, functoid_builder>(DefaultMask)
+ , m_cb(std::forward<T>(cb))
+ { }
+ functoid_builder(functoid_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<std::make_unsigned_t<Input>, functoid_builder>(std::move(that))
+ , m_cb(std::move(that.m_cb))
+ {
+ that.consume();
+ that.built();
+ }
+ ~functoid_builder() { this->template register_creator<functoid_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return first_transform_builder<wrapped_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->mask());
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { devcb_write::invoke_write<Input>(cb, space, offset, (data ^ exor) & mask, mem_mask & mask); };
+ }
+ };
-class devcb_write16 : public devcb_write_base
-{
-public:
- devcb_write16(device_t &device) : devcb_write_base(device, 0xffff) { }
- void operator()(u16 data, u16 mask = 0xffff) { write(*m_space, 0, data, mask); }
- void operator()(offs_t offset, u16 data, u16 mask = 0xffff) { write(*m_space, offset, data, mask); }
- void operator()(address_space &space, offs_t offset, u16 data, u16 mask = 0xffff) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); }
-};
+ template <typename Delegate>
+ class delegate_builder : public builder_base, public transform_base<mask_t<Input, typename delegate_traits<Delegate>::input_t>, delegate_builder<Delegate> >
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = intermediate_t<Input, typename delegate_traits<Delegate>::input_t>;
+
+ wrapped_builder(delegate_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_delegate(std::move(that.m_delegate))
+ {
+ that.consume();
+ that.built();
+ }
+ wrapped_builder(wrapped_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_delegate(std::move(that.m_delegate))
+ {
+ that.consume();
+ that.built();
+ }
+
+ void validity_check(validity_checker &valid) const
+ {
+ if (!m_devbase.subdevice(m_delegate.device_name()))
+ osd_printf_error("Write callback bound to non-existent object tag %s (%s)\n", m_delegate.device_name(), m_delegate.name());
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ m_delegate.bind_relative_to(m_devbase);
+ return
+ [cb = std::move(this->m_delegate)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { devcb_write::invoke_write<Input>(cb, space, offset, data, mem_mask); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ device_t &m_devbase;
+ Delegate m_delegate;
+ };
+
+ delegate_builder(delegate_builder const &) = delete;
+ delegate_builder &operator=(delegate_builder const &) = delete;
+ delegate_builder &operator=(delegate_builder &&that) = delete;
+
+ device_t &m_devbase;
+ Delegate m_delegate;
+
+ public:
+ using input_t = intermediate_t<Input, typename delegate_traits<Delegate>::input_t>;
+
+ template <typename T>
+ delegate_builder(devcb_write &target, bool append, device_t &devbase, char const *tag, T &&func, char const *name)
+ : builder_base(target, append)
+ , transform_base<mask_t<Input, typename delegate_traits<Delegate>::input_t>, delegate_builder>(DefaultMask & delegate_traits<Delegate>::default_mask)
+ , m_devbase(devbase)
+ , m_delegate(std::forward<T>(func), name, tag, std::add_pointer_t<devcb_write::delegate_device_class_t<T> >(nullptr))
+ { }
+ template <typename T>
+ delegate_builder(devcb_write &target, bool append, device_t &devbase, devcb_write::delegate_device_class_t<T> &obj, T &&func, char const *name)
+ : builder_base(target, append)
+ , transform_base<mask_t<Input, typename delegate_traits<Delegate>::input_t>, delegate_builder>(DefaultMask & delegate_traits<Delegate>::default_mask)
+ , m_devbase(devbase)
+ , m_delegate(std::forward<T>(func), name, devcb_write::get_tag(obj), &obj)
+ { }
+ delegate_builder(delegate_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<mask_t<Input, typename delegate_traits<Delegate>::input_t>, delegate_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_delegate(std::move(that.m_delegate))
+ {
+ that.consume();
+ that.built();
+ }
+ ~delegate_builder() { this->template register_creator<delegate_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return first_transform_builder<wrapped_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->mask());
+ }
+
+ void validity_check(validity_checker &valid) const
+ {
+ if (!m_devbase.subdevice(m_delegate.device_name()))
+ osd_printf_error("Write callback bound to non-existent object tag %s (%s)\n", m_delegate.device_name(), m_delegate.name());
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ m_delegate.bind_relative_to(m_devbase);
+ return
+ [cb = std::move(this->m_delegate), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { devcb_write::invoke_write<Input>(cb, space, offset, (data ^ exor) & mask, mem_mask & mask); };
+ }
+ };
+ class inputline_builder : public builder_base, public transform_base<mask_t<Input, int>, inputline_builder>
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = intermediate_t<Input, int>;
+
+ wrapped_builder(inputline_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(that.m_tag)
+ , m_exec(that.m_exec)
+ , m_linenum(that.m_linenum)
+ {
+ that.consume();
+ that.built();
+ }
+ wrapped_builder(wrapped_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(that.m_tag)
+ , m_exec(that.m_exec)
+ , m_linenum(that.m_linenum)
+ {
+ that.consume();
+ that.built();
+ }
+
+ void validity_check(validity_checker &valid) const
+ {
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag);
+ else if (!dynamic_cast<device_execute_interface *>(device))
+ osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag);
+ m_exec = dynamic_cast<device_execute_interface *>(device);
+ if (!m_exec)
+ throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ return
+ [&exec = *m_exec, linenum = m_linenum] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { exec.set_input_line(linenum, data); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ device_t &m_devbase;
+ char const *const m_tag;
+ device_execute_interface *m_exec;
+ int const m_linenum;
+ };
+
+ inputline_builder(inputline_builder const &) = delete;
+ inputline_builder &operator=(inputline_builder const &) = delete;
+ inputline_builder &operator=(inputline_builder &&that) = delete;
+
+ device_t &m_devbase;
+ char const *const m_tag;
+ device_execute_interface *m_exec;
+ int const m_linenum;
+
+ public:
+ using input_t = intermediate_t<Input, int>;
+
+ inputline_builder(devcb_write &target, bool append, device_t &devbase, char const *tag, int linenum)
+ : builder_base(target, append)
+ , transform_base<mask_t<Input, int>, inputline_builder>(1U)
+ , m_devbase(devbase)
+ , m_tag(tag)
+ , m_exec(nullptr)
+ , m_linenum(linenum)
+ { }
+ inputline_builder(devcb_write &target, bool append, device_execute_interface &exec, int linenum)
+ : builder_base(target, append)
+ , transform_base<mask_t<Input, int>, inputline_builder>(1U)
+ , m_devbase(exec.device())
+ , m_tag(exec.device().tag())
+ , m_exec(&exec)
+ , m_linenum(linenum)
+ { }
+ inputline_builder(inputline_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<mask_t<Input, int>, inputline_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(that.m_tag)
+ , m_exec(that.m_exec)
+ , m_linenum(that.m_linenum)
+ {
+ that.consume();
+ that.built();
+ }
+ ~inputline_builder() { this->template register_creator<inputline_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ first_transform_builder<wrapped_builder, std::remove_reference_t<T> > result(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->inherited_mask() ? DefaultMask : this->mask());
+ result.mask(1);
+ return std::move(result);
+ }
+
+ void validity_check(validity_checker &valid) const
+ {
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag);
+ else if (!dynamic_cast<device_execute_interface *>(device))
+ osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag);
+ m_exec = dynamic_cast<device_execute_interface *>(device);
+ if (!m_exec)
+ throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ return
+ [&exec = *m_exec, linenum = m_linenum, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { exec.set_input_line(linenum, (data ^ exor) & mask); };
+ }
+ };
-// ======================> devcb_write32
+ class latched_inputline_builder : public builder_base, public transform_base<std::make_unsigned_t<Input>, latched_inputline_builder>
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = Input;
+
+ wrapped_builder(latched_inputline_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(that.m_tag)
+ , m_exec(that.m_exec)
+ , m_linenum(that.m_linenum)
+ , m_value(that.m_value)
+ {
+ that.consume();
+ that.built();
+ }
+ wrapped_builder(wrapped_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(that.m_tag)
+ , m_exec(that.m_exec)
+ , m_linenum(that.m_linenum)
+ , m_value(that.m_value)
+ {
+ that.consume();
+ that.built();
+ }
+
+ void validity_check(validity_checker &valid) const
+ {
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag);
+ else if (!dynamic_cast<device_execute_interface *>(device))
+ osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag);
+ m_exec = dynamic_cast<device_execute_interface *>(device);
+ if (!m_exec)
+ throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ return
+ [&exec = *m_exec, linenum = m_linenum, value = m_value] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { if (data) exec.set_input_line(linenum, value); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ device_t &m_devbase;
+ char const *const m_tag;
+ device_execute_interface *m_exec;
+ int const m_linenum;
+ int const m_value;
+ };
+
+ latched_inputline_builder(latched_inputline_builder const &) = delete;
+ latched_inputline_builder &operator=(latched_inputline_builder const &) = delete;
+ latched_inputline_builder &operator=(latched_inputline_builder &&that) = delete;
+
+ device_t &m_devbase;
+ char const *const m_tag;
+ device_execute_interface *m_exec;
+ int const m_linenum;
+ int const m_value;
+
+ public:
+ using input_t = Input;
+
+ latched_inputline_builder(devcb_write &target, bool append, device_t &devbase, char const *tag, int linenum, int value)
+ : builder_base(target, append)
+ , transform_base<std::make_unsigned_t<Input>, latched_inputline_builder>(DefaultMask)
+ , m_devbase(devbase)
+ , m_tag(tag)
+ , m_exec(nullptr)
+ , m_linenum(linenum)
+ , m_value(value)
+ { }
+ latched_inputline_builder(devcb_write &target, bool append, device_execute_interface &exec, int linenum, int value)
+ : builder_base(target, append)
+ , transform_base<std::make_unsigned_t<Input>, latched_inputline_builder>(DefaultMask)
+ , m_devbase(exec.device())
+ , m_tag(exec.device().tag())
+ , m_exec(&exec)
+ , m_linenum(linenum)
+ , m_value(value)
+ { }
+ latched_inputline_builder(latched_inputline_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<std::make_unsigned_t<Input>, latched_inputline_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(that.m_tag)
+ , m_exec(that.m_exec)
+ , m_linenum(that.m_linenum)
+ , m_value(that.m_value)
+ {
+ that.consume();
+ that.built();
+ }
+ ~latched_inputline_builder() { this->template register_creator<latched_inputline_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return first_transform_builder<wrapped_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->inherited_mask() ? DefaultMask : this->mask());
+ }
+
+ void validity_check(validity_checker &valid) const
+ {
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag);
+ else if (!dynamic_cast<device_execute_interface *>(device))
+ osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ if (!m_exec)
+ {
+ device_t *const device(m_devbase.subdevice(m_tag));
+ if (!device)
+ throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag);
+ m_exec = dynamic_cast<device_execute_interface *>(device);
+ if (!m_exec)
+ throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name());
+ }
+ return
+ [&exec = *m_exec, linenum = m_linenum, value = m_value, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { if ((data ^ exor) & mask) exec.set_input_line(linenum, value); };
+ }
+ };
-class devcb_write32 : public devcb_write_base
-{
-public:
- devcb_write32(device_t &device) : devcb_write_base(device, 0xffffffff) { }
- void operator()(u32 data, u32 mask = 0xffffffff) { write(*m_space, 0, data, mask); }
- void operator()(offs_t offset, u32 data, u32 mask = 0xffffffff) { write(*m_space, offset, data, mask); }
- void operator()(address_space &space, offs_t offset, u32 data, u32 mask = 0xffffffff) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); }
-};
+ class ioport_builder : public builder_base, public transform_base<mask_t<Input, ioport_value>, ioport_builder>
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = intermediate_t<Input, ioport_value>;
+
+ wrapped_builder(ioport_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+ wrapped_builder(wrapped_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ ioport_port *const ioport(m_devbase.ioport(m_tag.c_str()));
+ if (!ioport)
+ throw emu_fatalerror("Write callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name());
+ return
+ [&port = *ioport] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { port.write(data); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_tag;
+ };
+
+ ioport_builder(ioport_builder const &) = delete;
+ ioport_builder &operator=(ioport_builder const &) = delete;
+ ioport_builder &operator=(ioport_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_tag;
+
+ public:
+ using input_t = intermediate_t<Input, ioport_value>;
+
+ ioport_builder(devcb_write &target, bool append, device_t &devbase, std::string &&tag)
+ : builder_base(target, append)
+ , transform_base<mask_t<Input, ioport_value>, ioport_builder>(DefaultMask)
+ , m_devbase(devbase)
+ , m_tag(std::move(tag))
+ { }
+ ioport_builder(ioport_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<mask_t<Input, ioport_value>, ioport_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+ ~ioport_builder() { this->template register_creator<ioport_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return first_transform_builder<wrapped_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->mask());
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ ioport_port *const ioport(m_devbase.ioport(m_tag.c_str()));
+ if (!ioport)
+ throw emu_fatalerror("Write callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name());
+ return
+ [&port = *ioport, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { port.write((data ^ exor) & mask); };
+ }
+ };
+ class membank_builder : public builder_base, public transform_base<mask_t<Input, int>, membank_builder>
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = intermediate_t<Input, int>;
+
+ wrapped_builder(membank_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+ wrapped_builder(wrapped_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ memory_bank *const bank(m_devbase.membank(m_tag.c_str()));
+ if (!bank)
+ throw emu_fatalerror("Write callback bound to non-existent memory bank %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name());
+ return
+ [&membank = *bank] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { membank.set_entry(data); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_tag;
+ };
+
+ membank_builder(membank_builder const &) = delete;
+ membank_builder &operator=(membank_builder const &) = delete;
+ membank_builder &operator=(membank_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_tag;
+
+ public:
+ using input_t = intermediate_t<Input, int>;
+
+ membank_builder(devcb_write &target, bool append, device_t &devbase, std::string &&tag)
+ : builder_base(target, append)
+ , transform_base<mask_t<Input, int>, membank_builder>(DefaultMask)
+ , m_devbase(devbase)
+ , m_tag(std::move(tag))
+ { }
+ membank_builder(membank_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<mask_t<Input, int>, membank_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+ ~membank_builder() { this->template register_creator<membank_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return first_transform_builder<wrapped_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->mask());
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ memory_bank *const bank(m_devbase.membank(m_tag.c_str()));
+ if (!bank)
+ throw emu_fatalerror("Write callback bound to non-existent memory bank %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name());
+ return
+ [&membank = *bank, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { membank.set_entry((data ^ exor) & mask); };
+ }
+ };
-// ======================> devcb_write64
+ class output_builder : public builder_base, public transform_base<mask_t<Input, s32>, output_builder>
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = intermediate_t<Input, s32>;
+
+ wrapped_builder(output_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+ wrapped_builder(wrapped_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [&item = m_devbase.machine().output().find_or_create_item(m_tag.c_str(), 0)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { item.set(data); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_tag;
+ };
+
+ output_builder(output_builder const &) = delete;
+ output_builder &operator=(output_builder const &) = delete;
+ output_builder &operator=(output_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_tag;
+
+ public:
+ using input_t = intermediate_t<Input, s32>;
+
+ output_builder(devcb_write &target, bool append, device_t &devbase, std::string &&tag)
+ : builder_base(target, append)
+ , transform_base<mask_t<Input, s32>, output_builder>(DefaultMask)
+ , m_devbase(devbase)
+ , m_tag(std::move(tag))
+ { }
+ output_builder(output_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<mask_t<Input, s32>, output_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_tag(std::move(that.m_tag))
+ {
+ that.consume();
+ that.built();
+ }
+ ~output_builder() { this->template register_creator<output_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return first_transform_builder<wrapped_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->mask());
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [&item = m_devbase.machine().output().find_or_create_item(m_tag.c_str(), 0), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { item.set((data ^ exor) & mask); };
+ }
+ };
+
+ class log_builder : public builder_base, public transform_base<std::make_unsigned_t<Input>, log_builder>
+ {
+ private:
+ class wrapped_builder : public builder_base
+ {
+ public:
+ template <typename T, typename U> friend class first_transform_builder;
+
+ using input_t = Input;
+
+ wrapped_builder(log_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_message(std::move(that.m_message))
+ {
+ that.consume();
+ that.built();
+ }
+ wrapped_builder(wrapped_builder &&that)
+ : builder_base(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_message(std::move(that.m_message))
+ {
+ that.consume();
+ that.built();
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [&devbase = m_devbase, message = std::move(m_message)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { if (data) devbase.logerror("%s: %s\n", devbase.machine().describe_context(), message); };
+ }
+
+ private:
+ wrapped_builder(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder const &) = delete;
+ wrapped_builder operator=(wrapped_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_message;
+ };
+
+ log_builder(log_builder const &) = delete;
+ log_builder &operator=(log_builder const &) = delete;
+ log_builder &operator=(log_builder &&that) = delete;
+
+ device_t &m_devbase;
+ std::string m_message;
+
+ public:
+ using input_t = Input;
+
+ log_builder(devcb_write &target, bool append, device_t &devbase, std::string &&message)
+ : builder_base(target, append)
+ , transform_base<std::make_unsigned_t<Input>, log_builder>(DefaultMask)
+ , m_devbase(devbase)
+ , m_message(std::move(message))
+ { }
+ log_builder(log_builder &&that)
+ : builder_base(std::move(that))
+ , transform_base<std::make_unsigned_t<Input>, log_builder>(std::move(that))
+ , m_devbase(that.m_devbase)
+ , m_message(std::move(that.m_message))
+ {
+ that.consume();
+ that.built();
+ }
+ ~log_builder() { this->template register_creator<log_builder>(); }
+
+ template <typename T>
+ std::enable_if_t<is_transform<input_t, input_t, T>::value, first_transform_builder<wrapped_builder, std::remove_reference_t<T> > > transform(T &&cb)
+ {
+ return first_transform_builder<wrapped_builder, std::remove_reference_t<T> >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward<T>(cb), this->exor(), this->inherited_mask() ? DefaultMask : this->mask());
+ }
+
+ void validity_check(validity_checker &valid) const { }
+
+ auto build()
+ {
+ assert(this->m_consumed);
+ this->built();
+ return
+ [&devbase = m_devbase, message = std::move(m_message), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask)
+ { if ((data ^ exor) & mask) devbase.logerror("%s: %s\n", devbase.machine().describe_context(), message); };
+ }
+ };
+ class binder
+ {
+ public:
+ binder(devcb_write &target) : m_target(target) { }
+ binder(binder const &) = delete;
+ binder(binder &&that) : m_target(that.m_target), m_append(that.m_append), m_used(that.m_used) { that.m_used = true; }
+ binder &operator=(binder const &) = delete;
+ binder &operator=(binder &&) = delete;
+
+ template <typename T>
+ std::enable_if_t<is_write<Input, T>::value, functoid_builder<std::remove_reference_t<T> > > set(T &&cb)
+ {
+ set_used();
+ return functoid_builder<std::remove_reference_t<T> >(m_target, m_append, std::forward<T>(cb));
+ }
+
+ template <typename T>
+ std::enable_if_t<is_write_method<T>::value, delegate_builder<delegate_type_t<T> > > set(T &&func, char const *name)
+ {
+ set_used();
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner().mconfig().current_device(), DEVICE_SELF, std::forward<T>(func), name);
+ }
+
+ template <typename T>
+ std::enable_if_t<is_write_method<T>::value, delegate_builder<delegate_type_t<T> > > set(char const *tag, T &&func, char const *name)
+ {
+ set_used();
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner().mconfig().current_device(), tag, std::forward<T>(func), name);
+ }
+
+ template <typename T, typename U>
+ std::enable_if_t<is_write_method<T>::value, delegate_builder<delegate_type_t<T> > > set(U &obj, T &&func, char const *name)
+ {
+ set_used();
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner().mconfig().current_device(), devcb_write::cast_reference<delegate_device_class_t<T> >(obj), std::forward<T>(func), name);
+ }
+
+ template <typename T, typename U, bool R>
+ std::enable_if_t<is_write_method<T>::value, delegate_builder<delegate_type_t<T> > > set(device_finder<U, R> &finder, T &&func, char const *name)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, target.first, target.second, std::forward<T>(func), name);
+ }
+
+ template <typename T, typename U, bool R>
+ std::enable_if_t<is_write_method<T>::value, delegate_builder<delegate_type_t<T> > > set(device_finder<U, R> const &finder, T &&func, char const *name)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return delegate_builder<delegate_type_t<T> >(m_target, m_append, target.first, target.second, std::forward<T>(func), name);
+ }
+
+ template <typename... Params>
+ auto append(Params &&... args)
+ {
+ m_append = true;
+ return set(std::forward<Params>(args)...);
+ }
+
+ inputline_builder set_inputline(char const *tag, int linenum)
+ {
+ set_used();
+ return inputline_builder(m_target, m_append, m_target.owner().mconfig().current_device(), tag, linenum);
+ }
+
+ latched_inputline_builder set_inputline(char const *tag, int linenum, int value)
+ {
+ set_used();
+ return latched_inputline_builder(m_target, m_append, m_target.owner().mconfig().current_device(), tag, linenum, value);
+ }
+
+ inputline_builder set_inputline(device_execute_interface &obj, int linenum)
+ {
+ set_used();
+ return inputline_builder(m_target, m_append, obj, linenum);
+ }
+
+ latched_inputline_builder set_inputline(device_execute_interface &obj, int linenum, int value)
+ {
+ set_used();
+ return latched_inputline_builder(m_target, m_append, obj, linenum, value);
+ }
+
+ template <typename T, bool R>
+ inputline_builder set_inputline(device_finder<T, R> const &finder, int linenum)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return inputline_builder(m_target, m_append, target.first, target.second, linenum);
+ }
+
+ template <typename T, bool R>
+ latched_inputline_builder set_inputline(device_finder<T, R> const &finder, int linenum, int value)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return latched_inputline_builder(m_target, m_append, target.first, target.second, linenum, value);
+ }
+
+ template <typename... Params>
+ auto append_inputline(Params &&... args)
+ {
+ m_append = true;
+ return set_inputline(std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ ioport_builder set_ioport(Params &&... args)
+ {
+ set_used();
+ return ioport_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward<Params>(args)...));
+ }
+
+ template <bool R>
+ ioport_builder set_ioport(ioport_finder<R> &finder)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return ioport_builder(m_target, m_append, target.first, std::string(target.second));
+ }
+
+ template <bool R>
+ ioport_builder set_ioport(ioport_finder<R> const &finder)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return ioport_builder(m_target, m_append, target.first, std::string(target.second));
+ }
+
+ template <typename... Params>
+ ioport_builder append_ioport(Params &&... args)
+ {
+ m_append = true;
+ return set_ioport(std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ membank_builder set_membank(Params &&... args)
+ {
+ set_used();
+ return membank_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward<Params>(args)...));
+ }
+
+ template <bool R>
+ membank_builder set_membank(memory_bank_finder<R> &finder)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return membank_builder(m_target, m_append, target.first, std::string(target.second));
+ }
+
+ template <bool R>
+ membank_builder set_membank(memory_bank_finder<R> const &finder)
+ {
+ set_used();
+ std::pair<device_t &, char const *> const target(finder.finder_target());
+ return membank_builder(m_target, m_append, target.first, std::string(target.second));
+ }
+
+ template <typename... Params>
+ membank_builder append_membank(Params &&... args)
+ {
+ m_append = true;
+ return set_membank(std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ output_builder set_output(Params &&... args)
+ {
+ set_used();
+ return output_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward<Params>(args)...));
+ }
+
+ template <typename... Params>
+ output_builder append_output(Params &&... args)
+ {
+ m_append = true;
+ return set_output(std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ log_builder set_log(device_t &devbase, Params &&... args)
+ {
+ set_used();
+ return log_builder(m_target, m_append, devbase, std::string(std::forward<Params>(args)...));
+ }
+
+ template <typename T, typename... Params>
+ std::enable_if_t<emu::detail::is_device_implementation<std::remove_reference_t<T> >::value, log_builder> set_log(T &devbase, Params &&... args)
+ {
+ return set_log(static_cast<device_t &>(devbase), std::forward<Params>(args)...);
+ }
+
+ template <typename T, typename... Params>
+ std::enable_if_t<emu::detail::is_device_interface<std::remove_reference_t<T> >::value, log_builder> set_log(T &devbase, Params &&... args)
+ {
+ return set_log(devbase.device(), std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ log_builder set_log(Params &&... args)
+ {
+ return set_log(m_target.owner().mconfig().current_device(), std::forward<Params>(args)...);
+ }
+
+ template <typename... Params>
+ log_builder append_log(Params &&... args)
+ {
+ m_append = true;
+ return set_log(std::forward<Params>(args)...);
+ }
+
+ void set_nop()
+ {
+ set_used();
+ m_target.m_creators.clear();
+ m_target.m_creators.emplace_back(std::make_unique<nop_creator>());
+ }
+
+ private:
+ void set_used() { assert(!m_used); m_used = true; }
+
+ devcb_write &m_target;
+ bool m_append = false;
+ bool m_used = false;
+ };
+
+ std::vector<func_t> m_functions;
+ std::vector<typename creator::ptr> m_creators;
-class devcb_write64 : public devcb_write_base
-{
public:
- devcb_write64(device_t &device) : devcb_write_base(device, 0xffffffffffffffffU) { }
- void operator()(u64 data, u64 mask = 0xffffffffffffffffU) { write(*m_space, 0, data, mask); }
- void operator()(offs_t offset, u64 data, u64 mask = 0xffffffffffffffffU) { write(*m_space, offset, data, mask); }
- void operator()(address_space &space, offs_t offset, u64 data, u64 mask = 0xffffffffffffffffU) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); }
+ devcb_write(device_t &owner);
+
+ binder bind();
+ void reset();
+
+ // legacy syntax support - will be removed
+ template <typename T, typename U, typename V>
+ devcb_base &set_callback(std::tuple<T, U, V> const &desc)
+ {
+ bind().set(std::forward<T>(std::get<0>(desc)), std::forward<U>(std::get<1>(desc)), std::forward<V>(std::get<2>(desc)));
+ return *this;
+ }
+ devcb_base &set_callback(DEVCB_INPUTLINE &&desc) { bind().set_inputline(desc.m_tag, desc.m_linenum); return *this; }
+ devcb_base &set_callback(DEVCB_ASSERTLINE &&desc) { bind().set_inputline(desc.m_tag, desc.m_linenum, ASSERT_LINE); return *this; }
+ devcb_base &set_callback(DEVCB_CLEARLINE &&desc) { bind().set_inputline(desc.m_tag, desc.m_linenum, CLEAR_LINE); return *this; }
+ devcb_base &set_callback(DEVCB_HOLDLINE &&desc) { bind().set_inputline(desc.m_tag, desc.m_linenum, HOLD_LINE); return *this; }
+ devcb_base &set_callback(DEVCB_MEMBANK &&desc) { bind().set_membank(desc.m_tag); return *this; }
+ devcb_base &set_callback(DEVCB_OUTPUT &&desc) { bind().set_output(desc.m_tag); return *this; }
+ devcb_base &set_callback(devcb_noop_t desc) { bind().set_nop(); return *this; }
+
+ virtual void validity_check(validity_checker &valid) const override;
+
+ void resolve();
+ void resolve_safe();
+
+ void operator()(address_space &space, offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask = DefaultMask);
+ void operator()(address_space &space, Input data);
+ void operator()(offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask = DefaultMask);
+ void operator()(Input data);
+
+ bool isnull() const { return m_functions.empty() && m_creators.empty(); }
+ explicit operator bool() const { return !m_functions.empty(); }
};
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+devcb_write<Input, DefaultMask>::devcb_write(device_t &owner)
+ : devcb_write_base(owner)
+{
+}
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+typename devcb_write<Input, DefaultMask>::binder devcb_write<Input, DefaultMask>::bind()
+{
+ return binder(*this);
+}
-//**************************************************************************
-// INLINE FUNCTIONS
-//**************************************************************************
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::reset()
+{
+ assert(m_functions.empty());
+ m_creators.clear();
+}
-//-------------------------------------------------
-// read - generic read callback dispatch
-//-------------------------------------------------
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::validity_check(validity_checker &valid) const
+{
+ assert(m_functions.empty());
+ devcb_write_base::validity_check(valid);
+ for (typename creator::ptr const &c : m_creators)
+ c->validity_check(valid);
+}
-inline u64 devcb_read_base::read(address_space &space, offs_t offset, u64 mask)
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::resolve()
{
- u64 result = (this->*m_adapter)(space, offset, mask);
- if (m_chain != nullptr)
- result |= m_chain->read(space, offset, mask);
- return result;
+ assert(m_functions.empty());
+ devcb_write_base::resolve();
+ m_functions.reserve(m_creators.size());
+ for (typename creator::ptr const &c : m_creators)
+ m_functions.emplace_back(c->create());
+ m_creators.clear();
}
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::resolve_safe()
+{
+ resolve();
+ if (m_functions.empty())
+ m_functions.emplace_back([] (address_space &space, offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask) { });
+}
+
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::operator()(address_space &space, offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask)
+{
+ assert(m_creators.empty() && !m_functions.empty());
+ typename std::vector<func_t>::const_iterator it(m_functions.begin());
+ (*it)(space, offset, data, mem_mask);
+ while (m_functions.end() != ++it)
+ (*it)(space, offset, data, mem_mask);
+}
+
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::operator()(address_space &space, Input data)
+{
+ this->operator()(space, 0U, data, DefaultMask);
+}
-//-------------------------------------------------
-// write - generic write callback dispatch
-//-------------------------------------------------
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::operator()(offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask)
+{
+ this->operator()(this->default_space(), offset, data, mem_mask);
+}
-inline void devcb_write_base::write(address_space &space, offs_t offset, u64 data, u64 mask)
+template <typename Input, std::make_unsigned_t<Input> DefaultMask>
+void devcb_write<Input, DefaultMask>::operator()(Input data)
{
- (this->*m_adapter)(space, offset, data, mask);
- if (m_chain != nullptr)
- m_chain->write(space, offset, data, mask);
+ this->operator()(this->default_space(), 0U, data, DefaultMask);
}
-//-------------------------------------------------
-// wrappers for ioports, constants, and loggers
-//-------------------------------------------------
-
-#define DEVCB_NOOP devcb_base::null_desc{ }
-template <typename... Params> inline devcb_base::ioport_desc DEVCB_IOPORT(Params &&... args) { return emu::detail::devcb_tag_desc_creator<devcb_base::ioport_desc>::create(std::forward<Params>(args)...); }
-template <typename... Params> inline devcb_base::membank_desc DEVCB_MEMBANK(Params &&... args) { return emu::detail::devcb_tag_desc_creator<devcb_base::membank_desc>::create(std::forward<Params>(args)...); }
-template <typename... Params> inline devcb_base::output_desc DEVCB_OUTPUT(Params &&... args) { return emu::detail::devcb_tag_desc_creator<devcb_base::output_desc>::create(std::forward<Params>(args)...); }
-inline devcb_base::constant_desc DEVCB_CONSTANT(u64 value) { return devcb_base::constant_desc{ value }; }
-inline devcb_base::logger_desc DEVCB_LOGGER(char const *string) { return devcb_base::logger_desc{ string }; }
-template <typename... Params> inline devcb_base::inputline_desc DEVCB_INPUTLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator<devcb_base::inputline_desc>::create(std::forward<Params>(args)...); }
-template <typename... Params> inline devcb_base::assertline_desc DEVCB_ASSERTLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator<devcb_base::assertline_desc>::create(std::forward<Params>(args)...); }
-template <typename... Params> inline devcb_base::clearline_desc DEVCB_CLEARLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator<devcb_base::clearline_desc>::create(std::forward<Params>(args)...); }
-template <typename... Params> inline devcb_base::holdline_desc DEVCB_HOLDLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator<devcb_base::holdline_desc>::create(std::forward<Params>(args)...); }
-#define DEVCB_VCC DEVCB_CONSTANT(1)
-#define DEVCB_GND DEVCB_CONSTANT(0)
+using devcb_read8 = devcb_read<u8>;
+using devcb_read16 = devcb_read<u16>;
+using devcb_read32 = devcb_read<u32>;
+using devcb_read64 = devcb_read<u64>;
+using devcb_read_line = devcb_read<int, 1U>;
+
+using devcb_write8 = devcb_write<u8>;
+using devcb_write16 = devcb_write<u16>;
+using devcb_write32 = devcb_write<u32>;
+using devcb_write64 = devcb_write<u64>;
+using devcb_write_line = devcb_write<int, 1U>;
+
+
+//**************************************************************************
+// TEMPLATE INSTANTIATIONS
+//**************************************************************************
+
+extern template class devcb_read<u8>;
+extern template class devcb_read<u16>;
+extern template class devcb_read<u32>;
+extern template class devcb_read<u64>;
+extern template class devcb_read<int, 1U>;
+
+extern template class devcb_read8::delegate_builder<read8_delegate>;
+extern template class devcb_read8::delegate_builder<read16_delegate>;
+extern template class devcb_read8::delegate_builder<read32_delegate>;
+extern template class devcb_read8::delegate_builder<read64_delegate>;
+extern template class devcb_read8::delegate_builder<read_line_delegate>;
+
+extern template class devcb_read16::delegate_builder<read8_delegate>;
+extern template class devcb_read16::delegate_builder<read16_delegate>;
+extern template class devcb_read16::delegate_builder<read32_delegate>;
+extern template class devcb_read16::delegate_builder<read64_delegate>;
+extern template class devcb_read16::delegate_builder<read_line_delegate>;
+
+extern template class devcb_read32::delegate_builder<read8_delegate>;
+extern template class devcb_read32::delegate_builder<read16_delegate>;
+extern template class devcb_read32::delegate_builder<read32_delegate>;
+extern template class devcb_read32::delegate_builder<read64_delegate>;
+extern template class devcb_read32::delegate_builder<read_line_delegate>;
+
+extern template class devcb_read64::delegate_builder<read8_delegate>;
+extern template class devcb_read64::delegate_builder<read16_delegate>;
+extern template class devcb_read64::delegate_builder<read32_delegate>;
+extern template class devcb_read64::delegate_builder<read64_delegate>;
+extern template class devcb_read64::delegate_builder<read_line_delegate>;
+
+extern template class devcb_read_line::delegate_builder<read8_delegate>;
+extern template class devcb_read_line::delegate_builder<read16_delegate>;
+extern template class devcb_read_line::delegate_builder<read32_delegate>;
+extern template class devcb_read_line::delegate_builder<read64_delegate>;
+extern template class devcb_read_line::delegate_builder<read_line_delegate>;
+
+extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read8_delegate> >;
+extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read16_delegate> >;
+extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read32_delegate> >;
+extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read64_delegate> >;
+extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read_line_delegate> >;
+extern template class devcb_read8::creator_impl<devcb_read8::ioport_builder>;
+
+extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read8_delegate> >;
+extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read16_delegate> >;
+extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read32_delegate> >;
+extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read64_delegate> >;
+extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read_line_delegate> >;
+extern template class devcb_read16::creator_impl<devcb_read16::ioport_builder>;
+
+extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read8_delegate> >;
+extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read16_delegate> >;
+extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read32_delegate> >;
+extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read64_delegate> >;
+extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read_line_delegate> >;
+extern template class devcb_read32::creator_impl<devcb_read32::ioport_builder>;
+
+extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read8_delegate> >;
+extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read16_delegate> >;
+extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read32_delegate> >;
+extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read64_delegate> >;
+extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read_line_delegate> >;
+extern template class devcb_read64::creator_impl<devcb_read64::ioport_builder>;
+
+extern template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read8_delegate> >;
+extern template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read16_delegate> >;
+extern template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read32_delegate> >;
+extern template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read64_delegate> >;
+extern template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read_line_delegate> >;
+extern template class devcb_read_line::creator_impl<devcb_read_line::ioport_builder>;
+
+extern template class devcb_write<u8>;
+extern template class devcb_write<u16>;
+extern template class devcb_write<u32>;
+extern template class devcb_write<u64>;
+extern template class devcb_write<int, 1U>;
+
+extern template class devcb_write8::delegate_builder<write8_delegate>;
+extern template class devcb_write8::delegate_builder<write16_delegate>;
+extern template class devcb_write8::delegate_builder<write32_delegate>;
+extern template class devcb_write8::delegate_builder<write64_delegate>;
+extern template class devcb_write8::delegate_builder<write_line_delegate>;
+
+extern template class devcb_write16::delegate_builder<write8_delegate>;
+extern template class devcb_write16::delegate_builder<write16_delegate>;
+extern template class devcb_write16::delegate_builder<write32_delegate>;
+extern template class devcb_write16::delegate_builder<write64_delegate>;
+extern template class devcb_write16::delegate_builder<write_line_delegate>;
+
+extern template class devcb_write32::delegate_builder<write8_delegate>;
+extern template class devcb_write32::delegate_builder<write16_delegate>;
+extern template class devcb_write32::delegate_builder<write32_delegate>;
+extern template class devcb_write32::delegate_builder<write64_delegate>;
+extern template class devcb_write32::delegate_builder<write_line_delegate>;
+
+extern template class devcb_write64::delegate_builder<write8_delegate>;
+extern template class devcb_write64::delegate_builder<write16_delegate>;
+extern template class devcb_write64::delegate_builder<write32_delegate>;
+extern template class devcb_write64::delegate_builder<write64_delegate>;
+extern template class devcb_write64::delegate_builder<write_line_delegate>;
+
+extern template class devcb_write_line::delegate_builder<write8_delegate>;
+extern template class devcb_write_line::delegate_builder<write16_delegate>;
+extern template class devcb_write_line::delegate_builder<write32_delegate>;
+extern template class devcb_write_line::delegate_builder<write64_delegate>;
+extern template class devcb_write_line::delegate_builder<write_line_delegate>;
+
+extern template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write8_delegate> >;
+extern template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write16_delegate> >;
+extern template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write32_delegate> >;
+extern template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write64_delegate> >;
+extern template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write_line_delegate> >;
+extern template class devcb_write8::creator_impl<devcb_write8::inputline_builder>;
+extern template class devcb_write8::creator_impl<devcb_write8::latched_inputline_builder>;
+extern template class devcb_write8::creator_impl<devcb_write8::ioport_builder>;
+extern template class devcb_write8::creator_impl<devcb_write8::membank_builder>;
+extern template class devcb_write8::creator_impl<devcb_write8::output_builder>;
+extern template class devcb_write8::creator_impl<devcb_write8::log_builder>;
+
+extern template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write8_delegate> >;
+extern template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write16_delegate> >;
+extern template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write32_delegate> >;
+extern template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write64_delegate> >;
+extern template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write_line_delegate> >;
+extern template class devcb_write16::creator_impl<devcb_write16::inputline_builder>;
+extern template class devcb_write16::creator_impl<devcb_write16::latched_inputline_builder>;
+extern template class devcb_write16::creator_impl<devcb_write16::ioport_builder>;
+extern template class devcb_write16::creator_impl<devcb_write16::membank_builder>;
+extern template class devcb_write16::creator_impl<devcb_write16::output_builder>;
+extern template class devcb_write16::creator_impl<devcb_write16::log_builder>;
+
+extern template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write8_delegate> >;
+extern template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write16_delegate> >;
+extern template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write32_delegate> >;
+extern template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write64_delegate> >;
+extern template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write_line_delegate> >;
+extern template class devcb_write32::creator_impl<devcb_write32::inputline_builder>;
+extern template class devcb_write32::creator_impl<devcb_write32::latched_inputline_builder>;
+extern template class devcb_write32::creator_impl<devcb_write32::ioport_builder>;
+extern template class devcb_write32::creator_impl<devcb_write32::membank_builder>;
+extern template class devcb_write32::creator_impl<devcb_write32::output_builder>;
+extern template class devcb_write32::creator_impl<devcb_write32::log_builder>;
+
+extern template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write8_delegate> >;
+extern template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write16_delegate> >;
+extern template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write32_delegate> >;
+extern template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write64_delegate> >;
+extern template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write_line_delegate> >;
+extern template class devcb_write64::creator_impl<devcb_write64::inputline_builder>;
+extern template class devcb_write64::creator_impl<devcb_write64::latched_inputline_builder>;
+extern template class devcb_write64::creator_impl<devcb_write64::ioport_builder>;
+extern template class devcb_write64::creator_impl<devcb_write64::membank_builder>;
+extern template class devcb_write64::creator_impl<devcb_write64::output_builder>;
+extern template class devcb_write64::creator_impl<devcb_write64::log_builder>;
+
+extern template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write8_delegate> >;
+extern template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write16_delegate> >;
+extern template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write32_delegate> >;
+extern template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write64_delegate> >;
+extern template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write_line_delegate> >;
+extern template class devcb_write_line::creator_impl<devcb_write_line::inputline_builder>;
+extern template class devcb_write_line::creator_impl<devcb_write_line::latched_inputline_builder>;
+extern template class devcb_write_line::creator_impl<devcb_write_line::ioport_builder>;
+extern template class devcb_write_line::creator_impl<devcb_write_line::membank_builder>;
+extern template class devcb_write_line::creator_impl<devcb_write_line::output_builder>;
+extern template class devcb_write_line::creator_impl<devcb_write_line::log_builder>;
#endif // MAME_EMU_DEVCB_H
diff --git a/src/emu/device.cpp b/src/emu/device.cpp
index 6bd458b0e0b..058ac84d7c4 100644
--- a/src/emu/device.cpp
+++ b/src/emu/device.cpp
@@ -315,6 +315,10 @@ void device_t::config_complete()
void device_t::validity_check(validity_checker &valid) const
{
+ // validate callbacks
+ for (devcb_base const *callback : m_callbacks)
+ callback->validity_check(valid);
+
// validate via the interfaces
for (device_interface &intf : interfaces())
intf.interface_validity_check(valid);
@@ -950,6 +954,13 @@ finder_base *device_t::register_auto_finder(finder_base &autodev)
return old;
}
+
+void device_t::register_callback(devcb_base &callback)
+{
+ m_callbacks.emplace_back(&callback);
+}
+
+
//**************************************************************************
// LIVE DEVICE INTERFACES
//**************************************************************************
diff --git a/src/emu/device.h b/src/emu/device.h
index 8b3b2c94574..e9d91bf4cf4 100644
--- a/src/emu/device.h
+++ b/src/emu/device.h
@@ -374,8 +374,7 @@ class device_t : public delegate_late_bind
friend class simple_list<device_t>;
friend class running_machine;
friend class finder_base;
- friend class devcb_read_base;
- friend class devcb_write_base;
+ friend class devcb_base;
class subdevice_list
{
@@ -523,8 +522,6 @@ public:
// owned object helpers
subdevice_list &subdevices() { return m_subdevices; }
const subdevice_list &subdevices() const { return m_subdevices; }
- const std::list<devcb_read_base *> input_callbacks() const { return m_input_callbacks; }
- const std::list<devcb_write_base *> output_callbacks() const { return m_output_callbacks; }
// device-relative tag lookups
std::string subtag(const char *tag) const;
@@ -597,6 +594,7 @@ protected:
void post_load();
void notify_clock_changed();
finder_base *register_auto_finder(finder_base &autodev);
+ void register_callback(devcb_base &callback);
//------------------- begin derived class overrides
@@ -658,8 +656,7 @@ private:
bool m_started; // true if the start function has succeeded
finder_base * m_auto_finder_list; // list of objects to auto-find
mutable std::vector<rom_entry> m_rom_entries;
- std::list<devcb_read_base *> m_input_callbacks;
- std::list<devcb_write_base *> m_output_callbacks;
+ std::list<devcb_base *> m_callbacks;
// string formatting buffer for logerror
mutable util::ovectorstream m_string_buffer;
diff --git a/src/emu/emu.h b/src/emu/emu.h
index 2bce001824c..9ed7d10240d 100644
--- a/src/emu/emu.h
+++ b/src/emu/emu.h
@@ -49,7 +49,6 @@
// memory and address spaces
#include "emumem.h"
-class address_map; // Forward declaration
#include "memarray.h"
// machine-wide utilities
diff --git a/src/emu/emufwd.h b/src/emu/emufwd.h
index 8f6ff6d3207..cb0af700c64 100644
--- a/src/emu/emufwd.h
+++ b/src/emu/emufwd.h
@@ -14,6 +14,8 @@
#pragma once
+#include <type_traits>
+
//----------------------------------
// 3rdparty
@@ -98,8 +100,8 @@ class symbol_table;
class debugger_manager;
// declared in devcb.h
-class devcb_read_base;
-class devcb_write_base;
+class devcb_base;
+template <typename Input, std::make_unsigned_t<Input> DefaultMask> class devcb_write;
// declared in devfind.h
class finder_base;
diff --git a/src/emu/mconfig.h b/src/emu/mconfig.h
index 949155fca41..6bcaec2b02e 100644
--- a/src/emu/mconfig.h
+++ b/src/emu/mconfig.h
@@ -223,9 +223,7 @@ inline std::enable_if_t<emu::detail::is_device_interface<typename std::remove_re
ATTR_COLD void _name(machine_config &config) \
{ \
device_t *device = nullptr; \
- devcb_base *devcb = nullptr; \
(void)device; \
- (void)devcb;
/**
@def MACHINE_CONFIG_END
diff --git a/src/emu/output.h b/src/emu/output.h
index ea7c599678b..79cf1c0af8b 100644
--- a/src/emu/output.h
+++ b/src/emu/output.h
@@ -27,10 +27,9 @@ typedef void (*output_notifier_func)(const char *outname, s32 value, void *param
class output_manager
{
- friend class devcb_base;
- friend class devcb_write_base;
-
private:
+ template <typename Input, std::make_unsigned_t<Input> DefaultMask> friend class devcb_write;
+
class output_notify
{
public:
diff --git a/src/emu/screen.h b/src/emu/screen.h
index f1126b4cb69..0f20848d45f 100644
--- a/src/emu/screen.h
+++ b/src/emu/screen.h
@@ -252,6 +252,7 @@ public:
}
template<class Object> devcb_base &set_screen_vblank(Object &&object) { return m_screen_vblank.set_callback(std::forward<Object>(object)); }
+ auto screen_vblank() { return m_screen_vblank.bind(); }
template<typename T> void set_palette(T &&tag) { m_palette.set_tag(std::forward<T>(tag)); }
void set_video_attributes(u32 flags) { m_video_attributes = flags; }
void set_color(rgb_t color) { m_color = color; }
@@ -272,9 +273,8 @@ public:
// beam positioning and state
int vpos() const;
int hpos() const;
- bool vblank() const { return (machine().time() < m_vblank_end_time); }
- DECLARE_READ_LINE_MEMBER(vblank) { return (machine().time() < m_vblank_end_time) ? ASSERT_LINE : CLEAR_LINE; }
- DECLARE_READ_LINE_MEMBER(hblank) { int curpos = hpos(); return (curpos < m_visarea.min_x || curpos > m_visarea.max_x) ? ASSERT_LINE : CLEAR_LINE; }
+ DECLARE_READ_LINE_MEMBER(vblank) const { return (machine().time() < m_vblank_end_time) ? 1 : 0; }
+ DECLARE_READ_LINE_MEMBER(hblank) const { int const curpos = hpos(); return (curpos < m_visarea.min_x || curpos > m_visarea.max_x) ? 1 : 0; }
// timing
attotime time_until_pos(int vpos, int hpos = 0) const;
@@ -542,7 +542,7 @@ typedef device_type_iterator<screen_device> screen_device_iterator;
#define MCFG_SCREEN_UPDATE_DEVICE(_device, _class, _method) \
downcast<screen_device &>(*device).set_screen_update(_device, &_class::_method, #_class "::" #_method);
#define MCFG_SCREEN_VBLANK_CALLBACK(_devcb) \
- devcb = &downcast<screen_device &>(*device).set_screen_vblank(DEVCB_##_devcb);
+ downcast<screen_device &>(*device).set_screen_vblank(DEVCB_##_devcb);
#define MCFG_SCREEN_PALETTE(_palette_tag) \
downcast<screen_device &>(*device).set_palette(_palette_tag);
#define MCFG_SCREEN_NO_PALETTE \
diff --git a/src/emu/validity.cpp b/src/emu/validity.cpp
index f8745e66bef..03530727721 100644
--- a/src/emu/validity.cpp
+++ b/src/emu/validity.cpp
@@ -1946,12 +1946,6 @@ void validity_checker::validate_devices()
// validate auto-finders
device.findit(true);
- // validate callbacks
- for (auto &cb : device.input_callbacks())
- cb->validity_check(*this);
- for (auto &cb : device.output_callbacks())
- cb->validity_check(*this);
-
// validate the device tag
validate_tag(device.basetag());