diff options
author | Vas Crabb <vas@vastheman.com> | 2018-07-07 02:40:29 +1000 |
---|---|---|
committer | Vas Crabb <vas@vastheman.com> | 2018-07-07 02:40:29 +1000 |
commit | c3fb11c2c98a5c28ece6a27093a0f9def350ac64 (patch) | |
tree | c68b38f05ed1d32358add721fda7f45e8803479f /src/emu | |
parent | 5d9e33b786d7ef452317439359f3cbd8cc920513 (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.cpp | 1031 | ||||
-rw-r--r-- | src/emu/devcb.h | 2980 | ||||
-rw-r--r-- | src/emu/device.cpp | 11 | ||||
-rw-r--r-- | src/emu/device.h | 9 | ||||
-rw-r--r-- | src/emu/emu.h | 1 | ||||
-rw-r--r-- | src/emu/emufwd.h | 6 | ||||
-rw-r--r-- | src/emu/mconfig.h | 2 | ||||
-rw-r--r-- | src/emu/output.h | 5 | ||||
-rw-r--r-- | src/emu/screen.h | 8 | ||||
-rw-r--r-- | src/emu/validity.cpp | 6 |
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()); |